From 7fea6f0e2e265bdfce91256105be0ad0d5029136 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Mon, 20 Feb 2017 10:48:03 +0100 Subject: [PATCH 01/29] Fix conversion issue for old IOU projects Fix https://github.com/GNS3/gns3-gui/issues/1868 --- gns3server/controller/topology.py | 8 ++++++++ tests/topologies/1_5_iou/after/1_5_iou.gns3 | 4 ++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/gns3server/controller/topology.py b/gns3server/controller/topology.py index c188e9a9..726ef519 100644 --- a/gns3server/controller/topology.py +++ b/gns3server/controller/topology.py @@ -331,6 +331,14 @@ def _convert_1_3_later(topo, topo_path): node["symbol"] = ":/symbols/vbox_guest.svg" elif old_node["type"] == "IOUDevice": node["node_type"] = "iou" + node["port_name_format"] = old_node.get("port_name_format", "Ethernet{segment0}/{port0}") + node["port_segment_size"] = int(old_node.get("port_segment_size", "4")) + if node["symbol"] is None: + if "l2" in node["properties"].get("path", ""): + node["symbol"] = ":/symbols/multilayer_switch.svg" + else: + node["symbol"] = ":/symbols/router.svg" + elif old_node["type"] == "Cloud": old_node["ports"] = _create_cloud(node, old_node, ":/symbols/cloud.svg") elif old_node["type"] == "Host": diff --git a/tests/topologies/1_5_iou/after/1_5_iou.gns3 b/tests/topologies/1_5_iou/after/1_5_iou.gns3 index f45baa1c..9e7f6b0a 100644 --- a/tests/topologies/1_5_iou/after/1_5_iou.gns3 +++ b/tests/topologies/1_5_iou/after/1_5_iou.gns3 @@ -30,8 +30,8 @@ "name": "IOU1", "node_id": "aaeb2288-a7d8-42a9-b9d8-c42ab464a390", "node_type": "iou", - "port_name_format": "Ethernet{0}", - "port_segment_size": 0, + "port_name_format": "Ethernet{segment0}/{port0}", + "port_segment_size": 4, "first_port_name": null, "properties": { "ethernet_adapters": 2, From 7407ab88b38a49a21e3b0a671e1f54ded5d76534 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Mon, 20 Feb 2017 10:56:48 +0100 Subject: [PATCH 02/29] Fix a failing test on Python 3.6 --- .travis.yml | 2 ++ gns3server/compute/vpcs/vpcs_vm.py | 15 +++++++-------- tests/compute/vpcs/test_vpcs_vm.py | 6 +++--- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/.travis.yml b/.travis.yml index a8a4eb75..c6870778 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,8 @@ language: python python: + - '3.4' - '3.5' + - '3.6' sudo: false cache: pip install: diff --git a/gns3server/compute/vpcs/vpcs_vm.py b/gns3server/compute/vpcs/vpcs_vm.py index 85952be3..e63d8436 100644 --- a/gns3server/compute/vpcs/vpcs_vm.py +++ b/gns3server/compute/vpcs/vpcs_vm.py @@ -104,7 +104,7 @@ class VPCSVM(BaseNode): Check if VPCS is available with the correct version. """ - path = self.vpcs_path + path = self._vpcs_path() if not path: raise VPCSError("No path to a VPCS executable has been set") @@ -146,8 +146,7 @@ class VPCSVM(BaseNode): else: return None - @property - def vpcs_path(self): + def _vpcs_path(self): """ Returns the VPCS executable path. @@ -217,7 +216,7 @@ class VPCSVM(BaseNode): Checks if the VPCS executable version is >= 0.8b or == 0.6.1. """ try: - output = yield from subprocess_check_output(self.vpcs_path, "-v", cwd=self.working_dir) + output = yield from subprocess_check_output(self._vpcs_path(), "-v", cwd=self.working_dir) match = re.search("Welcome to Virtual PC Simulator, version ([0-9a-z\.]+)", output) if match: version = match.group(1) @@ -225,7 +224,7 @@ class VPCSVM(BaseNode): if self._vpcs_version < parse_version("0.6.1"): raise VPCSError("VPCS executable version must be >= 0.6.1 but not a 0.8") else: - raise VPCSError("Could not determine the VPCS version for {}".format(self.vpcs_path)) + raise VPCSError("Could not determine the VPCS version for {}".format(self._vpcs_path())) except (OSError, subprocess.SubprocessError) as e: raise VPCSError("Error while looking for the VPCS version: {}".format(e)) @@ -270,8 +269,8 @@ class VPCSVM(BaseNode): self.status = "started" except (OSError, subprocess.SubprocessError) as e: vpcs_stdout = self.read_vpcs_stdout() - log.error("Could not start VPCS {}: {}\n{}".format(self.vpcs_path, e, vpcs_stdout)) - raise VPCSError("Could not start VPCS {}: {}\n{}".format(self.vpcs_path, e, vpcs_stdout)) + log.error("Could not start VPCS {}: {}\n{}".format(self._vpcs_path(), e, vpcs_stdout)) + raise VPCSError("Could not start VPCS {}: {}\n{}".format(self._vpcs_path(), e, vpcs_stdout)) def _termination_callback(self, returncode): """ @@ -514,7 +513,7 @@ class VPCSVM(BaseNode): """ - command = [self.vpcs_path] + command = [self._vpcs_path()] command.extend(["-p", str(self._internal_console_port)]) # listen to console port command.extend(["-m", str(self._manager.get_mac_id(self.id))]) # the unique ID is used to set the MAC address offset command.extend(["-i", "1"]) # option to start only one VPC instance diff --git a/tests/compute/vpcs/test_vpcs_vm.py b/tests/compute/vpcs/test_vpcs_vm.py index 6eedbd93..ce0d9257 100644 --- a/tests/compute/vpcs/test_vpcs_vm.py +++ b/tests/compute/vpcs/test_vpcs_vm.py @@ -75,7 +75,7 @@ def test_vm_invalid_vpcs_version(loop, manager, vm): def test_vm_invalid_vpcs_path(vm, manager, loop): - with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.vpcs_path", return_value="/tmp/fake/path/vpcs"): + with patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM._vpcs_path", return_value="/tmp/fake/path/vpcs"): with pytest.raises(VPCSError): nio = manager.create_nio({"type": "nio_udp", "lport": 4242, "rport": 4243, "rhost": "127.0.0.1"}) vm.port_add_nio_binding(0, nio) @@ -97,7 +97,7 @@ def test_start(loop, vm, async_run): nio = VPCS.instance().create_nio({"type": "nio_udp", "lport": 4242, "rport": 4243, "rhost": "127.0.0.1"}) async_run(vm.port_add_nio_binding(0, nio)) loop.run_until_complete(asyncio.async(vm.start())) - assert mock_exec.call_args[0] == (vm.vpcs_path, + assert mock_exec.call_args[0] == (vm._vpcs_path(), '-p', str(vm._internal_console_port), '-m', '1', @@ -133,7 +133,7 @@ def test_start_0_6_1(loop, vm, async_run): nio = VPCS.instance().create_nio({"type": "nio_udp", "lport": 4242, "rport": 4243, "rhost": "127.0.0.1"}) async_run(vm.port_add_nio_binding(0, nio)) async_run(vm.start()) - assert mock_exec.call_args[0] == (vm.vpcs_path, + assert mock_exec.call_args[0] == (vm._vpcs_path(), '-p', str(vm._internal_console_port), '-m', '1', From 132a7bfeb7c11e11173127e9bb34fede674521b6 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Mon, 20 Feb 2017 12:19:38 +0100 Subject: [PATCH 03/29] Catch an error in etherswitch when ubridge die Fix #907 --- gns3server/compute/dynamips/nodes/ethernet_switch.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gns3server/compute/dynamips/nodes/ethernet_switch.py b/gns3server/compute/dynamips/nodes/ethernet_switch.py index 037a837e..52d7c4ab 100644 --- a/gns3server/compute/dynamips/nodes/ethernet_switch.py +++ b/gns3server/compute/dynamips/nodes/ethernet_switch.py @@ -211,7 +211,8 @@ class EthernetSwitch(Device): nio = self._nios[port_number] if isinstance(nio, NIOUDP): self.manager.port_manager.release_udp_port(nio.lport, self._project) - yield from self._hypervisor.send('ethsw remove_nio "{name}" {nio}'.format(name=self._name, nio=nio)) + if self._hypervisor: + yield from self._hypervisor.send('ethsw remove_nio "{name}" {nio}'.format(name=self._name, nio=nio)) log.info('Ethernet switch "{name}" [{id}]: NIO {nio} removed from port {port}'.format(name=self._name, id=self._id, From 961c209ab1c0a6366ff7b0dcd26b458338c6efac Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Mon, 20 Feb 2017 17:25:26 +0100 Subject: [PATCH 04/29] Fix an issue when getting size from some SVG file Fix https://github.com/GNS3/gns3-gui/issues/1866 --- gns3server/utils/picture.py | 3 +- tests/resources/firefox.svg | 420 ++++++++++++++++++++++++++++++++++++ tests/utils/test_picture.py | 4 + 3 files changed, 426 insertions(+), 1 deletion(-) create mode 100644 tests/resources/firefox.svg diff --git a/gns3server/utils/picture.py b/gns3server/utils/picture.py index 4424ad9a..0fd740d8 100644 --- a/gns3server/utils/picture.py +++ b/gns3server/utils/picture.py @@ -120,7 +120,8 @@ def _svg_convert_size(size): "pc": 15, "mm": 3.543307, "cm": 35.43307, - "in": 90 + "in": 90, + "px": 1 } if len(size) > 3: if size[-2:] in conversion_table: diff --git a/tests/resources/firefox.svg b/tests/resources/firefox.svg new file mode 100644 index 00000000..2d3178f9 --- /dev/null +++ b/tests/resources/firefox.svg @@ -0,0 +1,420 @@ + + + + diff --git a/tests/utils/test_picture.py b/tests/utils/test_picture.py index d2764c40..a592d500 100644 --- a/tests/utils/test_picture.py +++ b/tests/utils/test_picture.py @@ -39,3 +39,7 @@ def test_get_size(): with open("gns3server/symbols/cloud.svg", "rb") as f: res = get_size(f.read()) assert res == (159, 71, "svg") + # Size with px + with open("tests/resources/firefox.svg", "rb") as f: + res = get_size(f.read()) + assert res == (66, 70, "svg") From b132d95a04ad34c98544215522371fb216a01b94 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Mon, 20 Feb 2017 18:28:49 +0100 Subject: [PATCH 05/29] Fix error when you delete the builtin symbols directory Fix #908 --- gns3server/controller/symbols.py | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/gns3server/controller/symbols.py b/gns3server/controller/symbols.py index 389f3ac0..5c473d8b 100644 --- a/gns3server/controller/symbols.py +++ b/gns3server/controller/symbols.py @@ -39,16 +39,17 @@ class Symbols: def list(self): self._symbols_path = {} symbols = [] - for file in os.listdir(get_resource("symbols")): - if file.startswith('.'): - continue - symbol_id = ':/symbols/' + file - symbols.append({ - 'symbol_id': symbol_id, - 'filename': file, - 'builtin': True, - }) - self._symbols_path[symbol_id] = os.path.join(get_resource("symbols"), file) + if get_resource("symbols"): + for file in os.listdir(get_resource("symbols")): + if file.startswith('.'): + continue + symbol_id = ':/symbols/' + file + symbols.append({ + 'symbol_id': symbol_id, + 'filename': file, + 'builtin': True, + }) + self._symbols_path[symbol_id] = os.path.join(get_resource("symbols"), file) directory = self.symbols_path() if directory: for file in os.listdir(directory): From 2884a40769039c451be233d95b48c3005b9b5056 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Wed, 22 Feb 2017 09:28:34 +0100 Subject: [PATCH 06/29] Fix error when the startup config file is missing Fix https://github.com/GNS3/gns3-gui/issues/1877 --- gns3server/compute/dynamips/nodes/router.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/gns3server/compute/dynamips/nodes/router.py b/gns3server/compute/dynamips/nodes/router.py index e43b827f..e0f2b673 100644 --- a/gns3server/compute/dynamips/nodes/router.py +++ b/gns3server/compute/dynamips/nodes/router.py @@ -1551,8 +1551,11 @@ class Router(BaseNode): try: startup_config_path = os.path.join(self._working_directory, startup_config) - with open(startup_config_path) as f: - self._startup_config_content = f.read() + if os.path.exists(startup_config_path): + with open(startup_config_path) as f: + self._startup_config_content = f.read() + else: + self._startup_config_content = '' except OSError as e: raise DynamipsError("Cannot access the startup-config {}: {}".format(startup_config_path, e)) From ebe8c1e53666a379c04fd6078cb564d4ed8d978f Mon Sep 17 00:00:00 2001 From: "pyup.io bot" Date: Wed, 22 Feb 2017 10:28:43 +0100 Subject: [PATCH 07/29] Update aiohttp from 1.2.0 to 1.3.3 (#905) --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 222e4b5a..2a11681b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ jsonschema>=2.4.0 -aiohttp==1.2.0 +aiohttp==1.3.3 aiohttp_cors>=0.4.0 yarl>=0.9.6 typing>=3.5.3.0 # Otherwise yarl fail with python 3.4 From 6a9180411611a5c53dceaaebdfd85b81dd6da632 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Wed, 22 Feb 2017 18:01:59 +0100 Subject: [PATCH 08/29] Catch error when you provide an invalid port name formating Fix #909 --- gns3server/controller/ports/port_factory.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/gns3server/controller/ports/port_factory.py b/gns3server/controller/ports/port_factory.py index e0174564..bc4d509b 100644 --- a/gns3server/controller/ports/port_factory.py +++ b/gns3server/controller/ports/port_factory.py @@ -15,6 +15,8 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +import aiohttp + from .atm_port import ATMPort from .frame_relay_port import FrameRelayPort from .gigabitethernet_port import GigabitEthernetPort @@ -64,11 +66,14 @@ class StandardPortFactory: port_name = first_port_name port = PortFactory(port_name, segment_number, adapter_number, port_number, "ethernet") else: - port_name = port_name_format.format( - interface_number, - segment_number, - adapter=adapter_number, - **cls._generate_replacement(interface_number, segment_number)) + try: + port_name = port_name_format.format( + interface_number, + segment_number, + adapter=adapter_number, + **cls._generate_replacement(interface_number, segment_number)) + except (ValueError, KeyError) as e: + raise aiohttp.web.HTTPConflict(text="Invalid port name format {}: {}".format(port_name_format, str(e))) port = PortFactory(port_name, segment_number, adapter_number, port_number, "ethernet") interface_number += 1 if port_segment_size: From 40be22bc58547763b15ed271db03a00f7744fb38 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Thu, 23 Feb 2017 09:13:27 +0100 Subject: [PATCH 09/29] Fix run missing function Fix https://github.com/GNS3/gns3-gui/issues/1878 --- gns3server/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gns3server/version.py b/gns3server/version.py index f30973c3..aa32f3b8 100644 --- a/gns3server/version.py +++ b/gns3server/version.py @@ -31,7 +31,7 @@ if "dev" in __version__: import os import subprocess if os.path.exists(os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", ".git")): - r = subprocess.run(["git", "rev-parse", "--short", "HEAD"], stdout=subprocess.PIPE).stdout.decode().strip("\n") + r = subprocess.check_output(["git", "rev-parse", "--short", "HEAD"]).decode().strip("\n") __version__ += "-" + r except Exception as e: print(e) From 726480f67660f651845f7b562f1d7eed1dd3ccf2 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Thu, 23 Feb 2017 09:21:49 +0100 Subject: [PATCH 10/29] Disable Keep Alive because it's bug with old Qt versions --- gns3server/web/response.py | 1 + 1 file changed, 1 insertion(+) diff --git a/gns3server/web/response.py b/gns3server/web/response.py index c057c6a9..65d85d53 100644 --- a/gns3server/web/response.py +++ b/gns3server/web/response.py @@ -39,6 +39,7 @@ class Response(aiohttp.web.Response): self._route = route self._output_schema = output_schema self._request = request + headers['Connection'] = "close" # Disable keep alive because create trouble with old Qt (5.2, 5.3 and 5.4) headers['X-Route'] = self._route headers['Server'] = "Python/{0[0]}.{0[1]} GNS3/{1}".format(sys.version_info, __version__) super().__init__(headers=headers, **kwargs) From afa3f12e30120cd1c4dea092ba7a7b46cab602ac Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Thu, 23 Feb 2017 11:48:34 +0100 Subject: [PATCH 11/29] Allow any 1.3 aiohttp release but not 1.4 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 2a11681b..da2ab028 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ jsonschema>=2.4.0 -aiohttp==1.3.3 +aiohttp>=1.3.0,<=1.4.0 aiohttp_cors>=0.4.0 yarl>=0.9.6 typing>=3.5.3.0 # Otherwise yarl fail with python 3.4 From 30db4d8b5cd68220c635c47e5a2c4aaf0bf97148 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Thu, 23 Feb 2017 11:49:46 +0100 Subject: [PATCH 12/29] Yarl 0.9.8 is require by aiohttp 1.3 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index da2ab028..c5400455 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ jsonschema>=2.4.0 aiohttp>=1.3.0,<=1.4.0 aiohttp_cors>=0.4.0 -yarl>=0.9.6 +yarl>=0.9.8 typing>=3.5.3.0 # Otherwise yarl fail with python 3.4 Jinja2>=2.7.3 raven>=5.23.0 From 8aca3c7b99ea51aa9e76de5a45885ac0591391bf Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Thu, 23 Feb 2017 14:34:21 +0100 Subject: [PATCH 13/29] Do not crash at startup if local server as the same name as remote server Fix #910 --- gns3server/controller/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gns3server/controller/__init__.py b/gns3server/controller/__init__.py index d0726713..e159b1ac 100644 --- a/gns3server/controller/__init__.py +++ b/gns3server/controller/__init__.py @@ -254,7 +254,7 @@ class Controller: return None for compute in self._computes.values(): - if name and compute.name == name: + if name and compute.name == name and not force: raise aiohttp.web.HTTPConflict(text='Compute name "{}" already exists'.format(name)) compute = Compute(compute_id=compute_id, controller=self, name=name, **kwargs) From 0d96471f2939c786ee85e8b9eaba1bbb6f6f8b64 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Thu, 23 Feb 2017 15:35:30 +0100 Subject: [PATCH 14/29] Fix headless startup of the GNS3 VM --- gns3server/controller/gns3vm/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/gns3server/controller/gns3vm/__init__.py b/gns3server/controller/gns3vm/__init__.py index dec8a8c3..f72c7aec 100644 --- a/gns3server/controller/gns3vm/__init__.py +++ b/gns3server/controller/gns3vm/__init__.py @@ -267,6 +267,7 @@ class GNS3VM: engine.vmname = self._settings["vmname"] engine.ram = self._settings["ram"] engine.vpcus = self._settings["vcpus"] + engine.headless = self._settings["headless"] compute = yield from self._controller.add_compute(compute_id="vm", name="GNS3 VM is starting ({})".format(engine.vmname), host=None, From 5bfa864f0c9ae2f3392a21980458a26280fe68e6 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Thu, 23 Feb 2017 16:19:20 +0100 Subject: [PATCH 15/29] Increase timeout for detecting VirtualBox GNS3 VM --- gns3server/controller/gns3vm/virtualbox_gns3_vm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gns3server/controller/gns3vm/virtualbox_gns3_vm.py b/gns3server/controller/gns3vm/virtualbox_gns3_vm.py index c6788034..07dcb72d 100644 --- a/gns3server/controller/gns3vm/virtualbox_gns3_vm.py +++ b/gns3server/controller/gns3vm/virtualbox_gns3_vm.py @@ -221,7 +221,7 @@ class VirtualBoxGNS3VM(BaseGNS3VM): second to a GNS3 endpoint in order to get the list of the interfaces and their IP and after that match it with VirtualBox host only. """ - remaining_try = 240 + remaining_try = 300 while remaining_try > 0: json_data = None session = aiohttp.ClientSession() From 3fb24dd89551db1ca4c79a704776f9a8e79215f5 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Thu, 23 Feb 2017 17:54:01 +0100 Subject: [PATCH 16/29] Avoid a crash when the connection with the server close --- gns3server/controller/compute.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gns3server/controller/compute.py b/gns3server/controller/compute.py index 413abba6..85f10575 100644 --- a/gns3server/controller/compute.py +++ b/gns3server/controller/compute.py @@ -426,7 +426,7 @@ class Compute: except aiohttp.errors.WSServerHandshakeError: self._ws = None break - if response.tp == aiohttp.MsgType.closed or response.tp == aiohttp.MsgType.error: + if response.tp == aiohttp.MsgType.closed or response.tp == aiohttp.MsgType.error or response.data is None: self._connected = False break msg = json.loads(response.data) From 366c567864e87255cf6c3cf490b4c1b26e7b80f0 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Thu, 23 Feb 2017 18:21:00 +0100 Subject: [PATCH 17/29] Fix restoration of private config when using dynamips Fix #906 --- gns3server/compute/dynamips/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/gns3server/compute/dynamips/__init__.py b/gns3server/compute/dynamips/__init__.py index 4ad0472f..d46f321d 100644 --- a/gns3server/compute/dynamips/__init__.py +++ b/gns3server/compute/dynamips/__init__.py @@ -534,6 +534,8 @@ class Dynamips(BaseManager): elif private_config_content: private_config_path = self._create_config(vm, default_private_config_path, private_config_content) yield from vm.set_configs(vm.startup_config, private_config_path) + elif os.path.isfile(default_private_config_path) and os.path.getsize(default_private_config_path) > 0: + yield from vm.set_configs(vm.startup_config, default_private_config_path) def _create_config(self, vm, path, content=None): """ From 65b75a92123db6198d59a98d83ae4bbbbb929387 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Fri, 24 Feb 2017 11:55:41 +0100 Subject: [PATCH 18/29] Fix an issue with serial capture for IOU Fix https://github.com/GNS3/gns3-gui/issues/1886 --- gns3server/compute/iou/iou_vm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gns3server/compute/iou/iou_vm.py b/gns3server/compute/iou/iou_vm.py index fc5ce499..5bd8590e 100644 --- a/gns3server/compute/iou/iou_vm.py +++ b/gns3server/compute/iou/iou_vm.py @@ -1167,7 +1167,7 @@ class IOUVM(BaseNode): bay=adapter_number, unit=port_number, output_file=output_file, - data_link_type=data_link_type)) + data_link_type=re.sub("^DLT_", "", data_link_type))) @asyncio.coroutine def stop_capture(self, adapter_number, port_number): From 2e0f012952259f3b6995f1af7cfef497ba4cf4d7 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Fri, 24 Feb 2017 13:58:03 +0100 Subject: [PATCH 19/29] Improve ACPI shutdown for virtualbox --- gns3server/compute/virtualbox/virtualbox_vm.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/gns3server/compute/virtualbox/virtualbox_vm.py b/gns3server/compute/virtualbox/virtualbox_vm.py index e1d520dc..77466ec9 100644 --- a/gns3server/compute/virtualbox/virtualbox_vm.py +++ b/gns3server/compute/virtualbox/virtualbox_vm.py @@ -303,6 +303,16 @@ class VirtualBoxVM(BaseNode): if self.acpi_shutdown: # use ACPI to shutdown the VM result = yield from self._control_vm("acpipowerbutton") + trial = 0 + while True: + vm_state = yield from self._get_vm_state() + if vm_state == "poweroff": + break + yield from asyncio.sleep(1) + trial += 1 + if trial >= 120: + yield from self._control_vm("poweroff") + break self.status = "stopped" log.debug("ACPI shutdown result: {}".format(result)) else: From 34d07369468ea04147476d380565dbe2a5324ca6 Mon Sep 17 00:00:00 2001 From: "pyup.io bot" Date: Mon, 27 Feb 2017 08:45:45 +0100 Subject: [PATCH 20/29] Update sphinx from 1.5.2 to 1.5.3 (#916) --- dev-requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev-requirements.txt b/dev-requirements.txt index 4cfcf60e..54339617 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -1,6 +1,6 @@ -rrequirements.txt -sphinx==1.5.2 +sphinx==1.5.3 pytest==3.0.6 pep8==1.7.0 pytest-catchlog==1.2.2 From 53dd1bd6e19ccdbe3e6f3318cd32773abf3cc21a Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Mon, 27 Feb 2017 11:08:58 +0100 Subject: [PATCH 21/29] Ensure we dump a .gns3 before exporting it Fix #915 --- gns3server/controller/export_project.py | 3 +++ tests/controller/test_export_project.py | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/gns3server/controller/export_project.py b/gns3server/controller/export_project.py index 25c167e2..1cc93a12 100644 --- a/gns3server/controller/export_project.py +++ b/gns3server/controller/export_project.py @@ -47,6 +47,9 @@ def export_project(project, temporary_dir, include_images=False, keep_compute_id if project.is_running(): raise aiohttp.web.HTTPConflict(text="Running topology could not be exported") + # Make sure we save the project + project.dump() + z = zipstream.ZipFile(allowZip64=True) if not os.path.exists(project._path): diff --git a/tests/controller/test_export_project.py b/tests/controller/test_export_project.py index 2b6b6610..f48df7c8 100644 --- a/tests/controller/test_export_project.py +++ b/tests/controller/test_export_project.py @@ -33,7 +33,9 @@ from gns3server.controller.export_project import export_project, _filter_files @pytest.fixture def project(controller): - return Project(controller=controller, name="Test") + p = Project(controller=controller, name="Test") + p.dump = MagicMock() + return p @pytest.fixture From 8e9c480d8dfe142a3a02aacdb87a8b624cbc35ab Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Mon, 27 Feb 2017 11:24:06 +0100 Subject: [PATCH 22/29] Catch some invalid node name formatting Fix #917 --- gns3server/controller/project.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/gns3server/controller/project.py b/gns3server/controller/project.py index 5bf23744..da884f31 100644 --- a/gns3server/controller/project.py +++ b/gns3server/controller/project.py @@ -293,6 +293,8 @@ class Project: 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") + except ValueError as e: + raise aiohttp.web.HTTPConflict(text="{} is not a valid replacement string in the node name".format(base_name)) if name not in self._allocated_node_names: self._allocated_node_names.add(name) return name From 9c71e96fd4bb55bf24b95c623b478e09488c628f Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Mon, 27 Feb 2017 11:31:51 +0100 Subject: [PATCH 23/29] Report aiohttp version in crash report --- gns3server/crash_report.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/gns3server/crash_report.py b/gns3server/crash_report.py index 7edc2865..7302207b 100644 --- a/gns3server/crash_report.py +++ b/gns3server/crash_report.py @@ -18,6 +18,7 @@ import os import sys import struct +import aiohttp import platform @@ -94,6 +95,7 @@ class CrashReport: "os:win_32": " ".join(platform.win32_ver()), "os:mac": "{} {}".format(platform.mac_ver()[0], platform.mac_ver()[2]), "os:linux": " ".join(platform.linux_distribution()), + "aiohttp:version": aiohttp.__version__, "python:version": "{}.{}.{}".format(sys.version_info[0], sys.version_info[1], sys.version_info[2]), From 8fd59c7967fc3d764e052e0713e88c90dd90b911 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Mon, 27 Feb 2017 12:03:26 +0100 Subject: [PATCH 24/29] If the GNS3 VM as failed to start reset his status --- gns3server/controller/gns3vm/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/gns3server/controller/gns3vm/__init__.py b/gns3server/controller/gns3vm/__init__.py index f72c7aec..e069e2dc 100644 --- a/gns3server/controller/gns3vm/__init__.py +++ b/gns3server/controller/gns3vm/__init__.py @@ -278,6 +278,7 @@ class GNS3VM: except Exception as e: yield from self._controller.delete_compute("vm") log.error("Can't start the GNS3 VM: {}", str(e)) + yield from compute.update(name="GNS3 VM ({})".format(engine.vmname)) raise e yield from compute.update(name="GNS3 VM ({})".format(engine.vmname), protocol=self.protocol, From 70e2b87ff08182beaa51d7f3453b51585e0b4a35 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Mon, 27 Feb 2017 12:48:05 +0100 Subject: [PATCH 25/29] Patch hostname in configuration file even if name is unsync Ref https://github.com/GNS3/gns3-gui/issues/1889 --- gns3server/compute/dynamips/nodes/router.py | 3 ++- gns3server/compute/iou/iou_vm.py | 2 +- gns3server/compute/vpcs/vpcs_vm.py | 1 + tests/compute/iou/test_iou_vm.py | 8 ++++++++ tests/compute/vpcs/test_vpcs_vm.py | 15 +++++++++++---- 5 files changed, 23 insertions(+), 6 deletions(-) diff --git a/gns3server/compute/dynamips/nodes/router.py b/gns3server/compute/dynamips/nodes/router.py index e0f2b673..b264ca64 100644 --- a/gns3server/compute/dynamips/nodes/router.py +++ b/gns3server/compute/dynamips/nodes/router.py @@ -24,6 +24,7 @@ import asyncio import time import sys import os +import re import glob import shlex import base64 @@ -1493,7 +1494,7 @@ class Router(BaseNode): try: with open(startup_config_path, "r+", encoding="utf-8", errors="replace") as f: old_config = f.read() - new_config = old_config.replace(self.name, new_name) + new_config = re.sub(r"^hostname .+$", "hostname " + new_name, old_config, flags=re.MULTILINE) f.seek(0) self._startup_config_content = new_config f.write(new_config) diff --git a/gns3server/compute/iou/iou_vm.py b/gns3server/compute/iou/iou_vm.py index 5bd8590e..8a12aa9b 100644 --- a/gns3server/compute/iou/iou_vm.py +++ b/gns3server/compute/iou/iou_vm.py @@ -307,7 +307,7 @@ class IOUVM(BaseNode): if self.startup_config_file: content = self.startup_config_content - content = content.replace(self._name, new_name) + content = re.sub(r"^hostname .+$", "hostname " + new_name, content, flags=re.MULTILINE) self.startup_config_content = content super(IOUVM, IOUVM).name.__set__(self, new_name) diff --git a/gns3server/compute/vpcs/vpcs_vm.py b/gns3server/compute/vpcs/vpcs_vm.py index e63d8436..0c8c9ebc 100644 --- a/gns3server/compute/vpcs/vpcs_vm.py +++ b/gns3server/compute/vpcs/vpcs_vm.py @@ -171,6 +171,7 @@ class VPCSVM(BaseNode): if self.script_file: content = self.startup_script content = content.replace(self._name, new_name) + content = re.sub(r"^set pcname .+$", "set pcname " + new_name, content, flags=re.MULTILINE) self.startup_script = content super(VPCSVM, VPCSVM).name.__set__(self, new_name) diff --git a/tests/compute/iou/test_iou_vm.py b/tests/compute/iou/test_iou_vm.py index 3f00875e..83e65041 100644 --- a/tests/compute/iou/test_iou_vm.py +++ b/tests/compute/iou/test_iou_vm.py @@ -298,6 +298,14 @@ def test_change_name(vm, tmpdir): assert vm.name == "hello" with open(path) as f: assert f.read() == "hostname hello" + # support hostname not sync + vm.name = "alpha" + with open(path, 'w+') as f: + f.write("no service password-encryption\nhostname beta\nno ip icmp rate-limit unreachable") + vm.name = "charlie" + assert vm.name == "charlie" + with open(path) as f: + assert f.read() == "no service password-encryption\nhostname charlie\nno ip icmp rate-limit unreachable" def test_library_check(loop, vm): diff --git a/tests/compute/vpcs/test_vpcs_vm.py b/tests/compute/vpcs/test_vpcs_vm.py index ce0d9257..b36ac1c9 100644 --- a/tests/compute/vpcs/test_vpcs_vm.py +++ b/tests/compute/vpcs/test_vpcs_vm.py @@ -243,12 +243,12 @@ def test_update_startup_script(vm): def test_update_startup_script_h(vm): - content = "setname %h\n" + content = "set pcname %h\n" vm.name = "pc1" vm.startup_script = content assert os.path.exists(vm.script_file) with open(vm.script_file) as f: - assert f.read() == "setname pc1\n" + assert f.read() == "set pcname pc1\n" def test_get_startup_script(vm): @@ -275,11 +275,18 @@ def test_change_name(vm, tmpdir): path = os.path.join(vm.working_dir, 'startup.vpc') vm.name = "world" with open(path, 'w+') as f: - f.write("name world") + f.write("set pcname world") vm.name = "hello" assert vm.name == "hello" with open(path) as f: - assert f.read() == "name hello" + assert f.read() == "set pcname hello" + # Support when the name is not sync with config + with open(path, 'w+') as f: + f.write("set pcname alpha") + vm.name = "beta" + assert vm.name == "beta" + with open(path) as f: + assert f.read() == "set pcname beta" def test_close(vm, port_manager, loop): From ec6411f7300b1d9fdf96f14880bdacd9289732bd Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Tue, 28 Feb 2017 11:22:53 +0100 Subject: [PATCH 26/29] Fix an error with some SVG Fix #919 --- gns3server/controller/drawing.py | 1 + 1 file changed, 1 insertion(+) diff --git a/gns3server/controller/drawing.py b/gns3server/controller/drawing.py index 078d67b0..39a4d158 100644 --- a/gns3server/controller/drawing.py +++ b/gns3server/controller/drawing.py @@ -43,6 +43,7 @@ class Drawing: self._id = str(uuid.uuid4()) else: self._id = drawing_id + self._svg = "" self.svg = svg self._x = x self._y = y From da8811515ddd7a7d96b9be76ea3e8bc9bbafc535 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Tue, 28 Feb 2017 11:42:07 +0100 Subject: [PATCH 27/29] Remove noise from log when VMware is not installed --- gns3server/controller/gns3vm/__init__.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/gns3server/controller/gns3vm/__init__.py b/gns3server/controller/gns3vm/__init__.py index e069e2dc..73a759fb 100644 --- a/gns3server/controller/gns3vm/__init__.py +++ b/gns3server/controller/gns3vm/__init__.py @@ -222,8 +222,14 @@ class GNS3VM: """ engine = self._get_engine(engine) vms = [] - for vm in (yield from engine.list()): - vms.append({"vmname": vm["vmname"]}) + try: + for vm in (yield from engine.list()): + vms.append({"vmname": vm["vmname"]}) + except GNS3VMError as e: + # We raise error only if user activated the GNS3 VM + # otherwise you have noise when VMware is not installed + if self.enable: + raise e return vms @asyncio.coroutine From 41d7570b24082c145270cfcdda7579c3704edc1e Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Tue, 28 Feb 2017 12:08:47 +0100 Subject: [PATCH 28/29] Load local server before anything else --- gns3server/controller/__init__.py | 6 ++++-- tests/controller/test_controller.py | 10 +++++----- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/gns3server/controller/__init__.py b/gns3server/controller/__init__.py index e159b1ac..efc62bca 100644 --- a/gns3server/controller/__init__.py +++ b/gns3server/controller/__init__.py @@ -53,9 +53,10 @@ class Controller: @asyncio.coroutine def start(self): log.info("Start controller") - yield from self.load() + server_config = Config.instance().get_section_config("Server") host = server_config.get("host", "localhost") + # If console_host is 0.0.0.0 client will use the ip they use # to connect to the controller console_host = host @@ -70,6 +71,7 @@ class Controller: user=server_config.get("user", ""), password=server_config.get("password", ""), force=True) + yield from self._load_controller_settings() yield from self.load_projects() yield from self.gns3vm.auto_start_vm() yield from self._project_auto_open() @@ -116,7 +118,7 @@ class Controller: json.dump(data, f, indent=4) @asyncio.coroutine - def load(self): + def _load_controller_settings(self): """ Reload the controller configuration from disk """ diff --git a/tests/controller/test_controller.py b/tests/controller/test_controller.py index 3227c54f..3ae347a2 100644 --- a/tests/controller/test_controller.py +++ b/tests/controller/test_controller.py @@ -42,7 +42,7 @@ def test_save(controller, controller_config_path): assert data["gns3vm"] == controller.gns3vm.__json__() -def test_load(controller, controller_config_path, async_run): +def test_load_controller_settings(controller, controller_config_path, async_run): controller.save() with open(controller_config_path) as f: data = json.load(f) @@ -60,7 +60,7 @@ def test_load(controller, controller_config_path, async_run): data["gns3vm"] = {"vmname": "Test VM"} with open(controller_config_path, "w+") as f: json.dump(data, f) - async_run(controller.load()) + async_run(controller._load_controller_settings()) assert controller.settings["IOU"] assert controller.computes["test1"].__json__() == { "compute_id": "test1", @@ -104,7 +104,7 @@ def test_import_computes_1_x(controller, controller_config_path, async_run): with open(os.path.join(config_dir, "gns3_gui.conf"), "w+") as f: json.dump(gns3_gui_conf, f) - async_run(controller.load()) + async_run(controller._load_controller_settings()) for compute in controller.computes.values(): if compute.id != "local": assert len(compute.id) == 36 @@ -146,7 +146,7 @@ def test_import_gns3vm_1_x(controller, controller_config_path, async_run): json.dump(gns3_gui_conf, f) controller.gns3vm.settings["engine"] = None - async_run(controller.load()) + async_run(controller._load_controller_settings()) assert controller.gns3vm.settings["engine"] == "vmware" assert controller.gns3vm.settings["enable"] assert controller.gns3vm.settings["headless"] @@ -202,7 +202,7 @@ def test_import_remote_gns3vm_1_x(controller, controller_config_path, async_run) json.dump(gns3_gui_conf, f) with asyncio_patch("gns3server.controller.compute.Compute.connect"): - async_run(controller.load()) + async_run(controller._load_controller_settings()) assert controller.gns3vm.settings["engine"] == "remote" assert controller.gns3vm.settings["vmname"] == "http://127.0.0.1:3081" From 39106ac36bf7d1f7f07bf6d4177b03b6757e19ff Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Tue, 28 Feb 2017 13:11:03 +0100 Subject: [PATCH 29/29] Do not prevent the creation of a local server on a machine named gns3vm Fix #920 --- gns3server/controller/__init__.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/gns3server/controller/__init__.py b/gns3server/controller/__init__.py index efc62bca..ee917327 100644 --- a/gns3server/controller/__init__.py +++ b/gns3server/controller/__init__.py @@ -62,8 +62,13 @@ class Controller: console_host = host if host == "0.0.0.0": host = "127.0.0.1" + + name = socket.gethostname() + if name == "gns3vm": + name = "Main server" + yield from self.add_compute(compute_id="local", - name=socket.gethostname(), + name=name, protocol=server_config.get("protocol", "http"), host=host, console_host=console_host,