From 96194cef673546723c1b8c63ad6abb7eef05ca1d Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Fri, 3 Feb 2017 14:44:32 +0100 Subject: [PATCH 01/12] Fix import/export of dynamips configuration --- gns3server/handlers/api/controller/node_handler.py | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/gns3server/handlers/api/controller/node_handler.py b/gns3server/handlers/api/controller/node_handler.py index 67857df7..d9a715f0 100644 --- a/gns3server/handlers/api/controller/node_handler.py +++ b/gns3server/handlers/api/controller/node_handler.py @@ -344,10 +344,7 @@ class NodeHandler: raise aiohttp.web.HTTPForbidden node_type = node.node_type - if node_type == "dynamips": - path = "/project-files/{}/{}".format(node_type, path) - else: - path = "/project-files/{}/{}/{}".format(node_type, node.id, path) + path = "/project-files/{}/{}/{}".format(node_type, node.id, path) res = yield from node.compute.http_query("GET", "/projects/{project_id}/files{path}".format(project_id=project.id, path=path), timeout=None, raw=True) response.set_status(200) @@ -384,12 +381,9 @@ class NodeHandler: raise aiohttp.web.HTTPForbidden node_type = node.node_type - if node_type == "dynamips": - path = "/project-files/{}/{}".format(node_type, path) - else: - path = "/project-files/{}/{}/{}".format(node_type, node.id, path) + path = "/project-files/{}/{}/{}".format(node_type, node.id, path) data = yield from request.content.read() - res = yield from node.compute.http_query("POST", "/projects/{project_id}/files{path}".format(project_id=project.id, path=path), data=data, timeout=None, raw=True) + yield from node.compute.http_query("POST", "/projects/{project_id}/files{path}".format(project_id=project.id, path=path), data=data, timeout=None, raw=True) response.set_status(201) From 959c08449ec44cc8ac1d2fac0596dccac046e4a7 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Fri, 3 Feb 2017 14:56:55 +0100 Subject: [PATCH 02/12] Do not crash if you pass {name} in name --- gns3server/controller/project.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gns3server/controller/project.py b/gns3server/controller/project.py index 4debcb73..2e360b15 100644 --- a/gns3server/controller/project.py +++ b/gns3server/controller/project.py @@ -289,7 +289,7 @@ class Project: if '{0}' in base_name or '{id}' in base_name: # base name is a template, replace {0} or {id} by an unique identifier for number in range(1, 1000000): - name = base_name.format(number, id=number) + name = base_name.format(number, id=number, name="Node") if name not in self._allocated_node_names: self._allocated_node_names.add(name) return name From d06af526b29b85db1fbd3d721cf53a7ee5bc5973 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Mon, 6 Feb 2017 10:49:09 +0100 Subject: [PATCH 03/12] Fix the server don't start if a remote is unavailable --- gns3server/controller/__init__.py | 2 -- gns3server/controller/compute.py | 8 ++++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/gns3server/controller/__init__.py b/gns3server/controller/__init__.py index 295e6762..825e9455 100644 --- a/gns3server/controller/__init__.py +++ b/gns3server/controller/__init__.py @@ -16,7 +16,6 @@ # along with this program. If not, see . import os -import sys import json import socket import asyncio @@ -318,7 +317,6 @@ class Controller: try: return self._computes[compute_id] except KeyError: - server_config = Config.instance().get_section_config("Server") if compute_id == "vm": raise aiohttp.web.HTTPNotFound(text="You try to use a node on the GNS3 VM server but the GNS3 VM is not configured") raise aiohttp.web.HTTPNotFound(text="Compute ID {} doesn't exist".format(compute_id)) diff --git a/gns3server/controller/compute.py b/gns3server/controller/compute.py index 47b780e8..f3373c46 100644 --- a/gns3server/controller/compute.py +++ b/gns3server/controller/compute.py @@ -22,14 +22,12 @@ import socket import json import uuid import sys -import os import io from ..utils import parse_version -from ..utils.images import list_images, md5sum +from ..utils.images import list_images from ..utils.asyncio import locked_coroutine from ..controller.controller_error import ControllerError -from ..config import Config from ..version import __version__ @@ -381,7 +379,9 @@ class Compute: except aiohttp.web.HTTPNotFound: raise aiohttp.web.HTTPConflict(text="The server {} is not a GNS3 server or it's a 1.X server".format(self._id)) except aiohttp.web.HTTPUnauthorized: - raise aiohttp.web.HTTPConflict(text="Invalid auth for server {} ".format(self._id)) + raise aiohttp.web.HTTPConflict(text="Invalid auth for server {}".format(self._id)) + except aiohttp.web.HTTPServiceUnavailable: + raise aiohttp.web.HTTPConflict(text="The server {} is unavailable".format(self._id)) if "version" not in response.json: self._http_session.close() From fbe26d11cff6e6ea125949b65ec43820f7aa9149 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Mon, 6 Feb 2017 11:07:35 +0100 Subject: [PATCH 04/12] Fix a potential crash --- gns3server/controller/__init__.py | 2 +- gns3server/controller/project.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gns3server/controller/__init__.py b/gns3server/controller/__init__.py index 825e9455..581a9897 100644 --- a/gns3server/controller/__init__.py +++ b/gns3server/controller/__init__.py @@ -378,7 +378,7 @@ class Controller: :param load: Load the topology """ topo_data = load_topology(path) - topology = topo_data.pop("topology") + topo_data.pop("topology") topo_data.pop("version") topo_data.pop("revision") topo_data.pop("type") diff --git a/gns3server/controller/project.py b/gns3server/controller/project.py index 2e360b15..35793034 100644 --- a/gns3server/controller/project.py +++ b/gns3server/controller/project.py @@ -440,7 +440,7 @@ class Project: Create a link. By default the link is empty """ if link_id and link_id in self._links: - return self._links[link.id] + return self._links[link_id] link = UDPLink(self, link_id=link_id) self._links[link.id] = link self.dump() From 0d7157c295c462c49924bbd825f9396d9c005448 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Mon, 6 Feb 2017 11:40:00 +0100 Subject: [PATCH 05/12] Improve a lot project loading speed Fix #893 --- gns3server/controller/link.py | 7 +++++-- gns3server/controller/project.py | 26 +++++++++++++++++--------- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/gns3server/controller/link.py b/gns3server/controller/link.py index 5f8bf61e..03c5f12c 100644 --- a/gns3server/controller/link.py +++ b/gns3server/controller/link.py @@ -52,9 +52,11 @@ class Link: return self._created @asyncio.coroutine - def add_node(self, node, adapter_number, port_number, label=None): + def add_node(self, node, adapter_number, port_number, label=None, dump=True): """ Add a node to the link + + :param dump: Dump project on disk """ port = node.get_port(adapter_number, port_number) @@ -101,7 +103,8 @@ class Link: self._created = True self._project.controller.notification.emit("link.created", self.__json__()) - self._project.dump() + if dump: + self._project.dump() @asyncio.coroutine def update_nodes(self, nodes): diff --git a/gns3server/controller/project.py b/gns3server/controller/project.py index 35793034..38d3c4ed 100644 --- a/gns3server/controller/project.py +++ b/gns3server/controller/project.py @@ -314,10 +314,11 @@ class Project: @open_required @asyncio.coroutine - def add_node(self, compute, name, node_id, node_type=None, **kwargs): + def add_node(self, compute, name, node_id, dump=True, node_type=None, **kwargs): """ Create a node or return an existing node + :param dump: Dump topology to disk :param kwargs: See the documentation of node """ if node_id in self._nodes: @@ -349,7 +350,8 @@ class Project: yield from node.create() self._nodes[node.id] = node self.controller.notification.emit("node.created", node.__json__()) - self.dump() + if dump: + self.dump() return node @locked_coroutine @@ -401,17 +403,19 @@ class Project: @open_required @asyncio.coroutine - def add_drawing(self, drawing_id=None, **kwargs): + def add_drawing(self, drawing_id=None, dump=True, **kwargs): """ Create an drawing or return an existing drawing + :param dump: Dump the topology to disk :param kwargs: See the documentation of drawing """ if drawing_id not in self._drawings: drawing = Drawing(self, drawing_id=drawing_id, **kwargs) self._drawings[drawing.id] = drawing self.controller.notification.emit("drawing.created", drawing.__json__()) - self.dump() + if dump: + self.dump() return drawing return self._drawings[drawing_id] @@ -435,15 +439,18 @@ class Project: @open_required @asyncio.coroutine - def add_link(self, link_id=None): + def add_link(self, link_id=None, dump=True): """ Create a link. By default the link is empty + + :param dump: Dump topology to disk """ if link_id and link_id in self._links: return self._links[link_id] link = UDPLink(self, link_id=link_id) self._links[link.id] = link - self.dump() + if dump: + self.dump() return link @open_required @@ -626,15 +633,16 @@ class Project: compute = self.controller.get_compute(node.pop("compute_id")) name = node.pop("name") node_id = node.pop("node_id") - yield from self.add_node(compute, name, node_id, **node) + yield from self.add_node(compute, name, node_id, **node, dump=False) for link_data in topology.get("links", []): link = yield from self.add_link(link_id=link_data["link_id"]) for node_link in link_data["nodes"]: node = self.get_node(node_link["node_id"]) - yield from link.add_node(node, node_link["adapter_number"], node_link["port_number"], label=node_link.get("label")) + yield from link.add_node(node, node_link["adapter_number"], node_link["port_number"], label=node_link.get("label"), dump=False) for drawing_data in topology.get("drawings", []): - drawing = yield from self.add_drawing(**drawing_data) + yield from self.add_drawing(**drawing_data, dump=False) + self.dump() # We catch all error to be able to rollback the .gns3 to the previous state except Exception as e: for compute in self._project_created_on_compute: From 9c7d2e9915d34e556a203fdcdf25b09a44f864ba Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Mon, 6 Feb 2017 15:05:29 +0100 Subject: [PATCH 06/12] Raise an error if you put an invalid key in node name Fix https://github.com/GNS3/gns3-gui/issues/1833 --- gns3server/controller/project.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/gns3server/controller/project.py b/gns3server/controller/project.py index 38d3c4ed..c0dab9cb 100644 --- a/gns3server/controller/project.py +++ b/gns3server/controller/project.py @@ -289,7 +289,10 @@ class Project: if '{0}' in base_name or '{id}' in base_name: # base name is a template, replace {0} or {id} by an unique identifier for number in range(1, 1000000): - name = base_name.format(number, id=number, name="Node") + try: + name = base_name.format(number, id=number, name="Node") + except KeyError as e: + raise aiohttp.web.HTTPConflict(text="{" + e.args[0] + "} is not a valid replacement string in the node name") if name not in self._allocated_node_names: self._allocated_node_names.add(name) return name From 08c2892295dfae0ad4c7c4ac035501102b7da360 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Mon, 6 Feb 2017 16:47:40 +0100 Subject: [PATCH 07/12] If we can't resolve compute name return 0.0.0.0 It's not perfect, but it's rare in most cases it's handle before. Fix #892 --- gns3server/controller/compute.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/gns3server/controller/compute.py b/gns3server/controller/compute.py index f3373c46..7437d796 100644 --- a/gns3server/controller/compute.py +++ b/gns3server/controller/compute.py @@ -214,7 +214,10 @@ class Compute: """ Return the IP associated to the host """ - return socket.gethostbyname(self._host) + try: + return socket.gethostbyname(self._host) + except socket.gaierror: + return '0.0.0.0' @host.setter def host(self, host): From bcc71b54557647428cd9a96c8de15e69dd7e6d34 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Mon, 6 Feb 2017 16:52:13 +0100 Subject: [PATCH 08/12] Fix a crash with Python 3.4 Fix https://github.com/GNS3/gns3-server/issues/876 --- gns3server/controller/project.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gns3server/controller/project.py b/gns3server/controller/project.py index c0dab9cb..d8b73a99 100644 --- a/gns3server/controller/project.py +++ b/gns3server/controller/project.py @@ -636,7 +636,7 @@ class Project: compute = self.controller.get_compute(node.pop("compute_id")) name = node.pop("name") node_id = node.pop("node_id") - yield from self.add_node(compute, name, node_id, **node, dump=False) + yield from self.add_node(compute, name, node_id, dump=False, **node) for link_data in topology.get("links", []): link = yield from self.add_link(link_id=link_data["link_id"]) for node_link in link_data["nodes"]: From 6ded234681cabfa0f4eb2955e233f3aa8573e7d5 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Mon, 6 Feb 2017 17:19:02 +0100 Subject: [PATCH 09/12] Fix an error with Python 3.4 --- gns3server/controller/project.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gns3server/controller/project.py b/gns3server/controller/project.py index d8b73a99..8796e028 100644 --- a/gns3server/controller/project.py +++ b/gns3server/controller/project.py @@ -643,7 +643,7 @@ class Project: node = self.get_node(node_link["node_id"]) yield from link.add_node(node, node_link["adapter_number"], node_link["port_number"], label=node_link.get("label"), dump=False) for drawing_data in topology.get("drawings", []): - yield from self.add_drawing(**drawing_data, dump=False) + yield from self.add_drawing(dump=False, **drawing_data) self.dump() # We catch all error to be able to rollback the .gns3 to the previous state From 94fd4bcbe9acc3f3ae82a0bd172f89fab6e088bd Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Mon, 6 Feb 2017 17:56:08 +0100 Subject: [PATCH 10/12] Fix stacktrace display when connecting to remote server Fix #891 --- gns3server/controller/compute.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/gns3server/controller/compute.py b/gns3server/controller/compute.py index 7437d796..413abba6 100644 --- a/gns3server/controller/compute.py +++ b/gns3server/controller/compute.py @@ -361,6 +361,16 @@ class Compute: response = yield from self._run_http_query(method, path, data=data, **kwargs) return response + @asyncio.coroutine + def _try_reconnect(self): + """ + We catch error during reconnect + """ + try: + yield from self.connect() + except aiohttp.web.HTTPConflict: + pass + @locked_coroutine def connect(self): """ @@ -375,8 +385,10 @@ class Compute: self._connection_failure += 1 # After 5 failure we close the project using the compute to avoid sync issues if self._connection_failure == 5: + log.warning("Can't connect to compute %s", self._id) yield from self._controller.close_compute_projects(self) - asyncio.get_event_loop().call_later(2, lambda: asyncio.async(self.connect())) + + asyncio.get_event_loop().call_later(2, lambda: asyncio.async(self._try_reconnect())) return except aiohttp.web.HTTPNotFound: From 19b70accd553244ab9403fe53a18f5b954a7688d Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Mon, 6 Feb 2017 17:59:00 +0100 Subject: [PATCH 11/12] Catch error when we can't access to a unix socket --- gns3server/utils/asyncio/serial.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/gns3server/utils/asyncio/serial.py b/gns3server/utils/asyncio/serial.py index 6dc961dd..48d12bc0 100644 --- a/gns3server/utils/asyncio/serial.py +++ b/gns3server/utils/asyncio/serial.py @@ -122,7 +122,10 @@ def _asyncio_open_serial_unix(path): raise NodeError('Pipe file "{}" is missing'.format(path)) output = SerialReaderWriterProtocol() - con = yield from asyncio.get_event_loop().create_unix_connection(lambda: output, path) + try: + yield from asyncio.get_event_loop().create_unix_connection(lambda: output, path) + except ConnectionRefusedError: + raise NodeError('Can\'t open pipe file "{}"'.format(path)) return output From f33e470601c3400d7a9c2761e0b195b46dcf36f9 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Tue, 7 Feb 2017 10:36:36 +0100 Subject: [PATCH 12/12] Fix error when you have error on your filesystem during project convertion Fix #894 --- gns3server/controller/topology.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/gns3server/controller/topology.py b/gns3server/controller/topology.py index b9400364..3f7a1540 100644 --- a/gns3server/controller/topology.py +++ b/gns3server/controller/topology.py @@ -163,11 +163,14 @@ def _convert_2_0_0_beta_2(topo, topo_path): dynamips_dir = os.path.join(topo_dir, "project-files", "dynamips") node_dir = os.path.join(dynamips_dir, node_id) - os.makedirs(os.path.join(node_dir, "configs"), exist_ok=True) - for path in glob.glob(os.path.join(glob.escape(dynamips_dir), "*_i{}_*".format(dynamips_id))): - shutil.move(path, os.path.join(node_dir, os.path.basename(path))) - for path in glob.glob(os.path.join(glob.escape(dynamips_dir), "configs", "i{}_*".format(dynamips_id))): - shutil.move(path, os.path.join(node_dir, "configs", os.path.basename(path))) + try: + os.makedirs(os.path.join(node_dir, "configs"), exist_ok=True) + for path in glob.glob(os.path.join(glob.escape(dynamips_dir), "*_i{}_*".format(dynamips_id))): + shutil.move(path, os.path.join(node_dir, os.path.basename(path))) + for path in glob.glob(os.path.join(glob.escape(dynamips_dir), "configs", "i{}_*".format(dynamips_id))): + shutil.move(path, os.path.join(node_dir, "configs", os.path.basename(path))) + except OSError as e: + raise aiohttp.web.HTTPConflict(text="Can't convert project {}: {}".format(topo_path, str(e))) return topo