From ef5714bd3ef24a1c97baaad5155ec146dbbd5d71 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Fri, 31 Mar 2017 10:07:25 +0200 Subject: [PATCH 01/53] 2.0.0rc3 --- CHANGELOG | 46 ++++++++++++++++++++++++++++++++++++++ gns3server/crash_report.py | 2 +- gns3server/version.py | 2 +- 3 files changed, 48 insertions(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 3b6baac5..c47aacf7 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,51 @@ # Change Log +## 2.0.0rc3 31/03/2017 + +* Support IOU image without .bin at the end +* Allow to change some properties of an already connected ethernet switch +* Ensure we start only one ubridge +* Catch some broken hostname for compute node +* Fix limit of 20 docker containers +* Fix race conditions in creation of Frame Relay Switch +* Fix conversion of project from 1.X with custom symbol for cloud +* Dissallow parallel pull of docker images +* Add a scripts for running current dev version on GNS3 VM +* Fix a crash with missing size in the svg files +* Fix an utf8 error in auth code +* Improve vmrun timeout message +* Support utf-8 characters in user and password for auth +* Handle password configuration change on remote servers +* Fix Bug when delete fake-running VMBox +* Fix Can't connect to compute local on some computers +* Add a modification uuid to settings returned by the server +* Check python version in setup.py only for install +* Fix Session is closed when listing docker images +* Cleanup docker source code +* Use aiohttp session for docker queries +* Escape special characters from SVG text +* Fix some port short name display issues +* Catch server disconnected errors from computes +* Generate a node uuid if the uuid is missing in the .gns3 +* Ensure to dump project before exporting it +* Fix return code check for SIGSEGV of IOU images +* Prevent vmname change for VirtualBox linked clone +* Upgrade to aiohttp 1.3.5 to solve issue with big file +* Handle some invalid svg +* Try to fix some 1.3 topology with corrupted data +* Fix ComputeError: Can't connect to Main server +* Catch error when the server as trouble to access to itself +* Catch a timeout error in docker +* Lock yarl version because 0.10 is not compatible with aiohttp 1.3 +* Raise error if image are not avaible on main server during export +* Fix a race condition when killing ubridge +* If your settings from 1.X are broken with skip them at import +* Catch a permission error on symbols +* Catch unicode error when you try to duplicate a project with invalid characters +* Catch error when you try to put an invalid server url +* Fix an error when handling ubridge errors +* Fix crash when handling an error in project creation + ## 2.0.0rc2 10/03/2017 * Drop color logging for remote install, seem to fail in some conditions diff --git a/gns3server/crash_report.py b/gns3server/crash_report.py index defbe8e1..4d13eadf 100644 --- a/gns3server/crash_report.py +++ b/gns3server/crash_report.py @@ -54,7 +54,7 @@ class CrashReport: Report crash to a third party service """ - DSN = "sync+https://7c028290d17b4035916285b304d42311:ddf752e704c7423cacab93f8e34f713c@sentry.io/38482" + DSN = "sync+https://71c8e5474e304e2384abc4df1b245acb:9f6d1b1b885a4ad1895f55338d4522d7@sentry.io/38482" if hasattr(sys, "frozen"): cacert = get_resource("cacert.pem") if cacert is not None and os.path.isfile(cacert): diff --git a/gns3server/version.py b/gns3server/version.py index 900b10cc..6d82d6e1 100644 --- a/gns3server/version.py +++ b/gns3server/version.py @@ -23,7 +23,7 @@ # or negative for a release candidate or beta (after the base version # number has been incremented) -__version__ = "2.0.0dev11" +__version__ = "2.0.0rc3" # If it's a git checkout try to add the commit if "dev" in __version__: From b92f881921377cbffbdc5cc61c8a0bb5d43e01f7 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Fri, 31 Mar 2017 10:08:29 +0200 Subject: [PATCH 02/53] 2.0.0dev12 --- gns3server/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gns3server/version.py b/gns3server/version.py index 6d82d6e1..26e862d6 100644 --- a/gns3server/version.py +++ b/gns3server/version.py @@ -23,7 +23,7 @@ # or negative for a release candidate or beta (after the base version # number has been incremented) -__version__ = "2.0.0rc3" +__version__ = "2.0.0dev12" # If it's a git checkout try to add the commit if "dev" in __version__: From 11c7f35ea2f50f359d6e71df752cb46865a56e3d Mon Sep 17 00:00:00 2001 From: "pyup.io bot" Date: Mon, 3 Apr 2017 09:06:26 +0200 Subject: [PATCH 03/53] Update sphinx from 1.5.3 to 1.5.4 (#966) --- dev-requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev-requirements.txt b/dev-requirements.txt index 4cfd2926..aba94b27 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -1,6 +1,6 @@ -rrequirements.txt -sphinx==1.5.3 +sphinx==1.5.4 pytest==3.0.7 pep8==1.7.0 pytest-catchlog==1.2.2 From b7e1a56056e6d074da5a688c67b52d9a6fdfec84 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Mon, 3 Apr 2017 16:24:32 +0200 Subject: [PATCH 04/53] Catch a startup error due to the usage of some antivirus --- gns3server/run.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gns3server/run.py b/gns3server/run.py index ccfb1a9d..edcc6cb6 100644 --- a/gns3server/run.py +++ b/gns3server/run.py @@ -163,7 +163,7 @@ def pid_lock(path): pid = int(f.read()) try: os.kill(pid, 0) # If the proces is not running kill return an error - except OSError: + except (OSError, SystemError): pid = None except OSError as e: log.critical("Can't open pid file %s: %s", pid, str(e)) From 8f33d9ab79578813eaddce51f1d2121580731289 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Mon, 3 Apr 2017 16:27:08 +0200 Subject: [PATCH 05/53] Update documentation links Fix #1977 --- gns3server/templates/index.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gns3server/templates/index.html b/gns3server/templates/index.html index 66c49e47..e013d37d 100644 --- a/gns3server/templates/index.html +++ b/gns3server/templates/index.html @@ -5,7 +5,7 @@ -

If you are looking for uploading the IOU. You can since 1.4 upload them directly from the client see: this documentation.

+

If you are looking for uploading the IOU. You can since 1.4 upload them directly from the client see: this documentation.

{% endblock %} From 7f621ed5dd2e069bfee6c994293ff5019ed1f636 Mon Sep 17 00:00:00 2001 From: "pyup.io bot" Date: Mon, 3 Apr 2017 23:44:26 +0200 Subject: [PATCH 06/53] Update sphinx from 1.5.4 to 1.5.5 (#967) --- dev-requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev-requirements.txt b/dev-requirements.txt index aba94b27..7e3a31c6 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -1,6 +1,6 @@ -rrequirements.txt -sphinx==1.5.4 +sphinx==1.5.5 pytest==3.0.7 pep8==1.7.0 pytest-catchlog==1.2.2 From ec6fdb7324fdfe05382bd57c9da491bf5ddc110f Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Tue, 4 Apr 2017 14:23:43 +0200 Subject: [PATCH 07/53] Catch GNS3 VM loading error at startup --- gns3server/controller/__init__.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/gns3server/controller/__init__.py b/gns3server/controller/__init__.py index a48881fc..584ed887 100644 --- a/gns3server/controller/__init__.py +++ b/gns3server/controller/__init__.py @@ -31,7 +31,7 @@ from .symbols import Symbols from ..version import __version__ from .topology import load_topology from .gns3vm import GNS3VM - +from .gns3vm.gns3_vm_error import GNS3VMError import logging log = logging.getLogger(__name__) @@ -87,10 +87,13 @@ class Controller: for c in computes: try: yield from self.add_compute(**c) - except aiohttp.web_exceptions.HTTPConflict: + except (aiohttp.web_exceptions.HTTPConflict): pass # Skip not available servers at loading yield from self.load_projects() - yield from self.gns3vm.auto_start_vm() + try: + yield from self.gns3vm.auto_start_vm() + except GNS3VMError as e: + log.warn(str(e)) yield from self._project_auto_open() def _update_config(self): From 5edb631b004b5c1c589fdf2b43bafd4c7f940339 Mon Sep 17 00:00:00 2001 From: grossmj Date: Fri, 7 Apr 2017 17:25:14 +0800 Subject: [PATCH 08/53] Fixes Qemu sata option. Ref #875. --- gns3server/compute/qemu/qemu_vm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gns3server/compute/qemu/qemu_vm.py b/gns3server/compute/qemu/qemu_vm.py index 7682c5e9..ae901516 100644 --- a/gns3server/compute/qemu/qemu_vm.py +++ b/gns3server/compute/qemu/qemu_vm.py @@ -1378,7 +1378,7 @@ class QemuVM(BaseNode): # special case, sata controller doesn't exist in Qemu options.extend(["-device", 'ahci,id=ahci{},bus=pci.{}'.format(disk_index, disk_index)]) options.extend(["-drive", 'file={},if=none,id=drive-sata-disk{},index={},media=disk'.format(disk, disk_index, disk_index)]) - options.extend(["-device", 'ide-drive,drive=drive-sata-disk{},bus=ahci{}.0'.format(disk_index, disk_index)]) + options.extend(["-device", 'ide-drive,drive=drive-sata-disk{},bus=ahci{}.0,id=drive-sata-disk{}'.format(disk_index, disk_index, disk_index)]) else: options.extend(["-drive", 'file={},if={},index={},media=disk'.format(disk, interface, disk_index)]) From 03a134af75059de7d409e26e55b3348fc5e2473e Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Fri, 7 Apr 2017 15:33:22 +0200 Subject: [PATCH 09/53] Catch an error at startup when the remote GNS3 VM is not a real GNS3 VM --- gns3server/controller/gns3vm/__init__.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/gns3server/controller/gns3vm/__init__.py b/gns3server/controller/gns3vm/__init__.py index 73a759fb..bace3022 100644 --- a/gns3server/controller/gns3vm/__init__.py +++ b/gns3server/controller/gns3vm/__init__.py @@ -18,6 +18,7 @@ import sys import copy import asyncio +import aiohttp from ...utils.asyncio import locked_coroutine from .vmware_gns3_vm import VMwareGNS3VM @@ -242,10 +243,13 @@ class GNS3VM: yield from self.start() except GNS3VMError as e: # User will receive the error later when they will try to use the node - yield from self._controller.add_compute(compute_id="vm", - name="GNS3 VM ({})".format(self.current_engine().vmname), - host=None, - force=True) + try: + yield from self._controller.add_compute(compute_id="vm", + name="GNS3 VM ({})".format(self.current_engine().vmname), + host=None, + force=True) + except aiohttp.web.HTTPConflict: + pass log.error("Can't start the GNS3 VM: {}", str(e)) @asyncio.coroutine From fdd1084714f34b8d43031126040c9f82727d1891 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Mon, 10 Apr 2017 16:58:00 +0200 Subject: [PATCH 10/53] Fix bridge 'bridge0' already exist when we have trouble with a container Fix #973 --- gns3server/compute/docker/docker_vm.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/gns3server/compute/docker/docker_vm.py b/gns3server/compute/docker/docker_vm.py index 0ac110ac..c8ebd66f 100644 --- a/gns3server/compute/docker/docker_vm.py +++ b/gns3server/compute/docker/docker_vm.py @@ -361,6 +361,7 @@ class DockerVM(BaseNode): try: yield from self._add_ubridge_connection(nio, adapter_number) except UbridgeNamespaceError: + log.error("Container {} failed to start", self.name) yield from self.stop() # The container can crash soon after the start, this means we can not move the interface to the container namespace @@ -517,6 +518,8 @@ class DockerVM(BaseNode): state = yield from self._get_container_state() if state == "running": return True + if self.status == "started": # The container crashed we need to clean + yield from self.stop() return False @asyncio.coroutine From ab2af5ceabd3f3cd29daffae91a1ec6b517da245 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Mon, 10 Apr 2017 17:44:09 +0200 Subject: [PATCH 11/53] Catch error and log them when we can't write the config Fix #974 --- gns3server/controller/__init__.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/gns3server/controller/__init__.py b/gns3server/controller/__init__.py index 584ed887..e00cd338 100644 --- a/gns3server/controller/__init__.py +++ b/gns3server/controller/__init__.py @@ -146,9 +146,12 @@ class Controller: "password": c.password, "compute_id": c.id }) - os.makedirs(os.path.dirname(self._config_file), exist_ok=True) - with open(self._config_file, 'w+') as f: - json.dump(data, f, indent=4) + try: + os.makedirs(os.path.dirname(self._config_file), exist_ok=True) + with open(self._config_file, 'w+') as f: + json.dump(data, f, indent=4) + except OSError as e: + log.error("Can't write the configuration {}: {}".format(self._config_file, str(e))) @asyncio.coroutine def _load_controller_settings(self): From 18835974fb5691e1c51bad32b7f46c96da5965fa Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Tue, 11 Apr 2017 10:57:01 +0200 Subject: [PATCH 12/53] Fix required field in schema not use Fix #972 --- gns3server/schemas/atm_switch.py | 3 ++- gns3server/schemas/cloud.py | 3 ++- gns3server/schemas/compute.py | 5 +++-- gns3server/schemas/ethernet_hub.py | 3 ++- gns3server/schemas/ethernet_switch.py | 3 ++- gns3server/schemas/frame_relay_switch.py | 3 ++- gns3server/schemas/node.py | 3 ++- tests/controller/test_import_project.py | 7 +++++++ 8 files changed, 22 insertions(+), 8 deletions(-) diff --git a/gns3server/schemas/atm_switch.py b/gns3server/schemas/atm_switch.py index e6a12f97..17cb109b 100644 --- a/gns3server/schemas/atm_switch.py +++ b/gns3server/schemas/atm_switch.py @@ -15,6 +15,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +import copy ATM_SWITCH_CREATE_SCHEMA = { "$schema": "http://json-schema.org/draft-04/schema#", @@ -81,5 +82,5 @@ ATM_SWITCH_OBJECT_SCHEMA = { "required": ["name", "node_id", "project_id"] } -ATM_SWITCH_UPDATE_SCHEMA = ATM_SWITCH_OBJECT_SCHEMA +ATM_SWITCH_UPDATE_SCHEMA = copy.deepcopy(ATM_SWITCH_OBJECT_SCHEMA) del ATM_SWITCH_UPDATE_SCHEMA["required"] diff --git a/gns3server/schemas/cloud.py b/gns3server/schemas/cloud.py index a67d8d35..aaf58467 100644 --- a/gns3server/schemas/cloud.py +++ b/gns3server/schemas/cloud.py @@ -15,6 +15,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +import copy from .port import PORT_OBJECT_SCHEMA HOST_INTERFACE_SCHEMA = { @@ -136,5 +137,5 @@ CLOUD_OBJECT_SCHEMA = { "required": ["name", "node_id", "project_id", "ports_mapping"] } -CLOUD_UPDATE_SCHEMA = CLOUD_OBJECT_SCHEMA +CLOUD_UPDATE_SCHEMA = copy.deepcopy(CLOUD_OBJECT_SCHEMA) del CLOUD_UPDATE_SCHEMA["required"] diff --git a/gns3server/schemas/compute.py b/gns3server/schemas/compute.py index 7d0d65ab..8e44de50 100644 --- a/gns3server/schemas/compute.py +++ b/gns3server/schemas/compute.py @@ -15,6 +15,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +import copy from .capabilities import CAPABILITIES_SCHEMA COMPUTE_CREATE_SCHEMA = { @@ -52,10 +53,10 @@ COMPUTE_CREATE_SCHEMA = { } }, "additionalProperties": False, - "required": ["compute_id", "protocol", "host", "port"] + "required": ["protocol", "host", "port"] } -COMPUTE_UPDATE_SCHEMA = COMPUTE_CREATE_SCHEMA +COMPUTE_UPDATE_SCHEMA = copy.deepcopy(COMPUTE_CREATE_SCHEMA) del COMPUTE_UPDATE_SCHEMA["required"] COMPUTE_OBJECT_SCHEMA = { diff --git a/gns3server/schemas/ethernet_hub.py b/gns3server/schemas/ethernet_hub.py index e24b33e0..363ad470 100644 --- a/gns3server/schemas/ethernet_hub.py +++ b/gns3server/schemas/ethernet_hub.py @@ -15,6 +15,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +import copy ETHERNET_HUB_CREATE_SCHEMA = { "$schema": "http://json-schema.org/draft-04/schema#", @@ -129,5 +130,5 @@ ETHERNET_HUB_OBJECT_SCHEMA = { "required": ["name", "node_id", "project_id", "ports_mapping"] } -ETHERNET_HUB_UPDATE_SCHEMA = ETHERNET_HUB_OBJECT_SCHEMA +ETHERNET_HUB_UPDATE_SCHEMA = copy.deepcopy(ETHERNET_HUB_OBJECT_SCHEMA) del ETHERNET_HUB_UPDATE_SCHEMA["required"] diff --git a/gns3server/schemas/ethernet_switch.py b/gns3server/schemas/ethernet_switch.py index 1a92a111..40dd2baa 100644 --- a/gns3server/schemas/ethernet_switch.py +++ b/gns3server/schemas/ethernet_switch.py @@ -15,6 +15,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +import copy ETHERNET_SWITCH_CREATE_SCHEMA = { "$schema": "http://json-schema.org/draft-04/schema#", @@ -153,5 +154,5 @@ ETHERNET_SWITCH_OBJECT_SCHEMA = { "required": ["name", "node_id", "project_id"] } -ETHERNET_SWITCH_UPDATE_SCHEMA = ETHERNET_SWITCH_OBJECT_SCHEMA +ETHERNET_SWITCH_UPDATE_SCHEMA = copy.deepcopy(ETHERNET_SWITCH_OBJECT_SCHEMA) del ETHERNET_SWITCH_UPDATE_SCHEMA["required"] diff --git a/gns3server/schemas/frame_relay_switch.py b/gns3server/schemas/frame_relay_switch.py index 5d4967af..0f99508c 100644 --- a/gns3server/schemas/frame_relay_switch.py +++ b/gns3server/schemas/frame_relay_switch.py @@ -15,6 +15,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +import copy FRAME_RELAY_SWITCH_CREATE_SCHEMA = { "$schema": "http://json-schema.org/draft-04/schema#", @@ -81,5 +82,5 @@ FRAME_RELAY_SWITCH_OBJECT_SCHEMA = { "required": ["name", "node_id", "project_id"] } -FRAME_RELAY_SWITCH_UPDATE_SCHEMA = FRAME_RELAY_SWITCH_OBJECT_SCHEMA +FRAME_RELAY_SWITCH_UPDATE_SCHEMA = copy.deepcopy(FRAME_RELAY_SWITCH_OBJECT_SCHEMA) del FRAME_RELAY_SWITCH_UPDATE_SCHEMA["required"] diff --git a/gns3server/schemas/node.py b/gns3server/schemas/node.py index a832c8cf..6e8f4e2a 100644 --- a/gns3server/schemas/node.py +++ b/gns3server/schemas/node.py @@ -15,6 +15,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +import copy from .label import LABEL_OBJECT_SCHEMA NODE_TYPE_SCHEMA = { @@ -234,5 +235,5 @@ NODE_OBJECT_SCHEMA = { } NODE_CREATE_SCHEMA = NODE_OBJECT_SCHEMA -NODE_UPDATE_SCHEMA = NODE_OBJECT_SCHEMA +NODE_UPDATE_SCHEMA = copy.deepcopy(NODE_OBJECT_SCHEMA) del NODE_UPDATE_SCHEMA["required"] diff --git a/tests/controller/test_import_project.py b/tests/controller/test_import_project.py index 7f8740c3..23d09457 100644 --- a/tests/controller/test_import_project.py +++ b/tests/controller/test_import_project.py @@ -181,6 +181,7 @@ def test_import_iou_linux_no_vm(linux_platform, async_run, tmpdir, controller): { "compute_id": "local", "node_type": "iou", + "name": "test", "properties": {} } ], @@ -224,6 +225,7 @@ def test_import_iou_linux_with_vm(linux_platform, async_run, tmpdir, controller) "compute_id": "local", "node_id": "0fd3dd4d-dc93-4a04-a9b9-7396a9e22e8b", "node_type": "iou", + "name": "test", "properties": {} } ], @@ -267,11 +269,13 @@ def test_import_iou_non_linux(windows_platform, async_run, tmpdir, controller): "compute_id": "local", "node_id": "0fd3dd4d-dc93-4a04-a9b9-7396a9e22e8b", "node_type": "iou", + "name": "test", "properties": {} }, { "compute_id": "local", "node_type": "vpcs", + "name": "test2", "properties": {} } ], @@ -319,12 +323,14 @@ def test_import_node_id(linux_platform, async_run, tmpdir, controller): "compute_id": "local", "node_id": "0fd3dd4d-dc93-4a04-a9b9-7396a9e22e8b", "node_type": "iou", + "name": "test", "properties": {} }, { "compute_id": "local", "node_id": "c3ae286c-c81f-40d9-a2d0-5874b2f2478d", "node_type": "iou", + "name": "test2", "properties": {} } ], @@ -409,6 +415,7 @@ def test_import_keep_compute_id(windows_platform, async_run, tmpdir, controller) "compute_id": "local", "node_id": "0fd3dd4d-dc93-4a04-a9b9-7396a9e22e8b", "node_type": "iou", + "name": "test", "properties": {} } ], From 62cd5e4aa39d5bae32cfa24caf981d9a32d2b29b Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Tue, 11 Apr 2017 15:05:31 +0200 Subject: [PATCH 13/53] Fix ghost vmware vms Fix #975 --- gns3server/compute/vmware/__init__.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/gns3server/compute/vmware/__init__.py b/gns3server/compute/vmware/__init__.py index db0591d5..4a4082c5 100644 --- a/gns3server/compute/vmware/__init__.py +++ b/gns3server/compute/vmware/__init__.py @@ -588,8 +588,9 @@ class VMware(BaseManager): for vm_settings in vm_entries.values(): if "displayname" in vm_settings and "config" in vm_settings: - log.debug('Found VM named "{}" with VMX file "{}"'.format(vm_settings["displayname"], vm_settings["config"])) - vmware_vms.append({"vmname": vm_settings["displayname"], "vmx_path": vm_settings["config"]}) + if os.path.exists(vm_settings["config"]): + log.debug('Found VM named "{}" with VMX file "{}"'.format(vm_settings["displayname"], vm_settings["config"])) + vmware_vms.append({"vmname": vm_settings["displayname"], "vmx_path": vm_settings["config"]}) return vmware_vms def _get_vms_from_directory(self, directory): From 878aee6e909a753efd99eeb527e038a2c13f299d Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Thu, 13 Apr 2017 09:55:22 +0200 Subject: [PATCH 14/53] Correct version number --- gns3server/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gns3server/version.py b/gns3server/version.py index 6709413a..72202e2f 100644 --- a/gns3server/version.py +++ b/gns3server/version.py @@ -24,4 +24,4 @@ # number has been incremented) __version__ = "1.5.4dev1" -__version_info__ = (1, 5, 3, -99) +__version_info__ = (1, 5, 4, -99) From a8294c3de859820837e2fd7611b44b4cec52b2cb Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Thu, 13 Apr 2017 10:22:20 +0200 Subject: [PATCH 15/53] Backport requirements of 2.0 --- requirements.txt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/requirements.txt b/requirements.txt index c6f78f1c..21a43549 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,9 +1,9 @@ jsonschema>=2.4.0 -aiohttp>=1.2.0 -aiohttp_cors>=0.4.0 -yarl>=0.7.0 +aiohttp>=1.3.5,<=1.4.0 # pyup: ignore +aiohttp-cors==0.5.1 # pyup: ignore +yarl>=0.9.8,<0.10 # pyup: ignore typing>=3.5.3.0 # Otherwise yarl fail with python 3.4 Jinja2>=2.7.3 -raven>=5.2.0 +raven>=5.23.0 psutil>=3.0.0 -zipstream>=1.1.3 +zipstream>=1.1.4 From b755840a272a87c5557e6c5317092b30b11271ca Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Thu, 13 Apr 2017 12:38:16 +0200 Subject: [PATCH 16/53] Add security issues section --- README.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.rst b/README.rst index 81ffc9d5..2f804027 100644 --- a/README.rst +++ b/README.rst @@ -206,3 +206,8 @@ If you want test coverage: .. code:: bash py.test --cov-report term-missing --cov=gns3server + +Security issues +---------------- +Please contact us using contact informations available here: +http://docs.gns3.com/1ON9JBXSeR7Nt2-Qum2o3ZX0GU86BZwlmNSUgvmqNWGY/index.html From 57ba7ab8018b62d56f34b4b31614620db5dee11e Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Thu, 13 Apr 2017 14:24:07 +0200 Subject: [PATCH 17/53] Fix crash in some tests --- tests/modules/test_project.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/modules/test_project.py b/tests/modules/test_project.py index c170879a..d6946770 100644 --- a/tests/modules/test_project.py +++ b/tests/modules/test_project.py @@ -216,15 +216,15 @@ def test_get_default_project_directory(monkeypatch): def test_clean_project_directory(tmpdir): # A non anonymous project with uuid. - project1 = tmpdir / uuid4() + project1 = tmpdir / str(uuid4()) project1.mkdir() # A non anonymous project. - oldproject = tmpdir / uuid4() + oldproject = tmpdir / str(uuid4()) oldproject.mkdir() # an anonymous project - project2 = tmpdir / uuid4() + project2 = tmpdir / str(uuid4()) project2.mkdir() tmp = (project2 / ".gns3_temporary") with open(str(tmp), 'w+') as f: From 3afe85ba3c9e142d5b1e0aef3bfe879d216c593c Mon Sep 17 00:00:00 2001 From: Bernhard Ehlers Date: Thu, 13 Apr 2017 14:25:40 +0200 Subject: [PATCH 18/53] Fix 1.5: Error message, when stopping IOU router #769 Signed-off-by: Julien Duponchelle --- gns3server/modules/iou/iou_vm.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/gns3server/modules/iou/iou_vm.py b/gns3server/modules/iou/iou_vm.py index f58612a6..8e427c6d 100644 --- a/gns3server/modules/iou/iou_vm.py +++ b/gns3server/modules/iou/iou_vm.py @@ -541,8 +541,6 @@ class IOUVM(BaseVM): self._ioucon_thread_stop_event.set() if returncode != 0: - log.info("{} process has stopped, return code: {}".format(process_name, returncode)) - else: if returncode == 11: message = "{} process has stopped, return code: {}. This could be an issue with the image using a different image can fix the issue.\n{}".format(process_name, returncode, self.read_iou_stdout()) else: From 6d150bb62c5b76d8eb60fe69463043ab2e7bd1ee Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Thu, 13 Apr 2017 14:45:27 +0200 Subject: [PATCH 19/53] Freeze server dependencies to the same version used for 1.5.3 --- .travis.yml | 2 ++ requirements.txt | 6 +++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index d1c7bda6..b6d711a7 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/requirements.txt b/requirements.txt index 21a43549..3bf3f042 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ jsonschema>=2.4.0 -aiohttp>=1.3.5,<=1.4.0 # pyup: ignore -aiohttp-cors==0.5.1 # pyup: ignore -yarl>=0.9.8,<0.10 # pyup: ignore +aiohttp==1.2.0 # pyup: ignore +aiohttp-cors==0.4.0 # pyup: ignore +yarl==0.8.1 # pyup: ignore typing>=3.5.3.0 # Otherwise yarl fail with python 3.4 Jinja2>=2.7.3 raven>=5.23.0 From 85136bc2e4091639549cddc8023baf59cc4b7af7 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Thu, 13 Apr 2017 15:56:12 +0200 Subject: [PATCH 20/53] Fix VPCS tests for recent version --- gns3server/handlers/api/vpcs_handler.py | 2 +- gns3server/modules/vpcs/vpcs_vm.py | 13 +++++----- tests/modules/vpcs/test_vpcs_vm.py | 32 ++++++++++--------------- 3 files changed, 19 insertions(+), 28 deletions(-) diff --git a/gns3server/handlers/api/vpcs_handler.py b/gns3server/handlers/api/vpcs_handler.py index 59dd1dcd..6af82ad7 100644 --- a/gns3server/handlers/api/vpcs_handler.py +++ b/gns3server/handlers/api/vpcs_handler.py @@ -202,7 +202,7 @@ class VPCSHandler: nio_type = request.json["type"] if nio_type not in ("nio_udp", "nio_tap"): raise HTTPConflict(text="NIO of type {} is not supported".format(nio_type)) - nio = vpcs_manager.create_nio(vm.vpcs_path, request.json) + nio = vpcs_manager.create_nio(vm.vpcs_path(), request.json) vm.port_add_nio_binding(int(request.match_info["port_number"]), nio) response.set_status(201) response.json(nio) diff --git a/gns3server/modules/vpcs/vpcs_vm.py b/gns3server/modules/vpcs/vpcs_vm.py index 08efcf10..70474576 100644 --- a/gns3server/modules/vpcs/vpcs_vm.py +++ b/gns3server/modules/vpcs/vpcs_vm.py @@ -94,7 +94,7 @@ class VPCSVM(BaseVM): 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") @@ -132,7 +132,6 @@ class VPCSVM(BaseVM): else: return None - @property def vpcs_path(self): """ Returns the VPCS executable path. @@ -203,7 +202,7 @@ class VPCSVM(BaseVM): 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) @@ -211,7 +210,7 @@ class VPCSVM(BaseVM): if self._vpcs_version < parse_version("0.8b") and self._vpcs_version != parse_version("0.6.1"): raise VPCSError("VPCS executable version must be >= 0.8b or 0.6.1") 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)) @@ -247,8 +246,8 @@ class VPCSVM(BaseVM): 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): """ @@ -417,7 +416,7 @@ class VPCSVM(BaseVM): """ - command = [self.vpcs_path] + command = [self.vpcs_path()] command.extend(["-p", str(self._console)]) # 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/modules/vpcs/test_vpcs_vm.py b/tests/modules/vpcs/test_vpcs_vm.py index ce7cf577..949bdf74 100644 --- a/tests/modules/vpcs/test_vpcs_vm.py +++ b/tests/modules/vpcs/test_vpcs_vm.py @@ -65,7 +65,7 @@ def test_vm_check_vpcs_version_0_6_1(loop, vm, manager): def test_vm_invalid_vpcs_version(loop, manager, vm): with asyncio_patch("gns3server.modules.vpcs.vpcs_vm.subprocess_check_output", return_value="Welcome to Virtual PC Simulator, version 0.1"): with pytest.raises(VPCSError): - nio = manager.create_nio(vm.vpcs_path, {"type": "nio_udp", "lport": 4242, "rport": 4243, "rhost": "127.0.0.1"}) + nio = manager.create_nio(vm.vpcs_path(), {"type": "nio_udp", "lport": 4242, "rport": 4243, "rhost": "127.0.0.1"}) vm.port_add_nio_binding(0, nio) loop.run_until_complete(asyncio.async(vm._check_vpcs_version())) assert vm.name == "test" @@ -73,9 +73,9 @@ def test_vm_invalid_vpcs_version(loop, manager, vm): def test_vm_invalid_vpcs_path(vm, manager, loop): - with asyncio_patch("gns3server.modules.vpcs.vpcs_vm.VPCSVM.vpcs_path", return_value="/tmp/fake/path/vpcs"): + with patch("gns3server.modules.vpcs.vpcs_vm.VPCSVM.vpcs_path", return_value="/tmp/fake/path/vpcs"): with pytest.raises(VPCSError): - nio = manager.create_nio(vm.vpcs_path, {"type": "nio_udp", "lport": 4242, "rport": 4243, "rhost": "127.0.0.1"}) + nio = manager.create_nio(vm.vpcs_path(), {"type": "nio_udp", "lport": 4242, "rport": 4243, "rhost": "127.0.0.1"}) vm.port_add_nio_binding(0, nio) loop.run_until_complete(asyncio.async(vm.start())) assert vm.name == "test" @@ -89,10 +89,10 @@ def test_start(loop, vm): with asyncio_patch("gns3server.modules.vpcs.vpcs_vm.VPCSVM._check_requirements", return_value=True): with asyncio_patch("asyncio.create_subprocess_exec", return_value=process) as mock_exec: - nio = VPCS.instance().create_nio(vm.vpcs_path, {"type": "nio_udp", "lport": 4242, "rport": 4243, "rhost": "127.0.0.1"}) + nio = VPCS.instance().create_nio(vm.vpcs_path(), {"type": "nio_udp", "lport": 4242, "rport": 4243, "rhost": "127.0.0.1"}) 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.console), '-m', '1', @@ -125,10 +125,10 @@ def test_start_0_6_1(loop, vm): with asyncio_patch("gns3server.modules.vpcs.vpcs_vm.VPCSVM._check_requirements", return_value=True): with asyncio_patch("asyncio.create_subprocess_exec", return_value=process) as mock_exec: - nio = VPCS.instance().create_nio(vm.vpcs_path, {"type": "nio_udp", "lport": 4242, "rport": 4243, "rhost": "127.0.0.1"}) + nio = VPCS.instance().create_nio(vm.vpcs_path(), {"type": "nio_udp", "lport": 4242, "rport": 4243, "rhost": "127.0.0.1"}) 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.console), '-m', '1', @@ -158,7 +158,7 @@ def test_stop(loop, vm): with asyncio_patch("gns3server.modules.vpcs.vpcs_vm.VPCSVM._check_requirements", return_value=True): with asyncio_patch("asyncio.create_subprocess_exec", return_value=process): - nio = VPCS.instance().create_nio(vm.vpcs_path, {"type": "nio_udp", "lport": 4242, "rport": 4243, "rhost": "127.0.0.1"}) + nio = VPCS.instance().create_nio(vm.vpcs_path(), {"type": "nio_udp", "lport": 4242, "rport": 4243, "rhost": "127.0.0.1"}) vm.port_add_nio_binding(0, nio) loop.run_until_complete(asyncio.async(vm.start())) @@ -191,7 +191,7 @@ def test_reload(loop, vm): with asyncio_patch("gns3server.modules.vpcs.vpcs_vm.VPCSVM._check_requirements", return_value=True): with asyncio_patch("asyncio.create_subprocess_exec", return_value=process): - nio = VPCS.instance().create_nio(vm.vpcs_path, {"type": "nio_udp", "lport": 4242, "rport": 4243, "rhost": "127.0.0.1"}) + nio = VPCS.instance().create_nio(vm.vpcs_path(), {"type": "nio_udp", "lport": 4242, "rport": 4243, "rhost": "127.0.0.1"}) vm.port_add_nio_binding(0, nio) loop.run_until_complete(asyncio.async(vm.start())) assert vm.is_running() @@ -207,7 +207,7 @@ def test_reload(loop, vm): def test_add_nio_binding_udp(vm): - nio = VPCS.instance().create_nio(vm.vpcs_path, {"type": "nio_udp", "lport": 4242, "rport": 4243, "rhost": "127.0.0.1"}) + nio = VPCS.instance().create_nio(vm.vpcs_path(), {"type": "nio_udp", "lport": 4242, "rport": 4243, "rhost": "127.0.0.1"}) vm.port_add_nio_binding(0, nio) assert nio.lport == 4242 @@ -215,21 +215,13 @@ def test_add_nio_binding_udp(vm): @pytest.mark.skipif(sys.platform.startswith("win"), reason="Not supported on Windows") def test_add_nio_binding_tap(vm, ethernet_device): with patch("gns3server.modules.base_manager.BaseManager.has_privileged_access", return_value=True): - nio = VPCS.instance().create_nio(vm.vpcs_path, {"type": "nio_tap", "tap_device": ethernet_device}) + nio = VPCS.instance().create_nio(vm.vpcs_path(), {"type": "nio_tap", "tap_device": ethernet_device}) vm.port_add_nio_binding(0, nio) assert nio.tap_device == ethernet_device -# def test_add_nio_binding_tap_no_privileged_access(vm): -# with patch("gns3server.modules.base_manager.BaseManager.has_privileged_access", return_value=False): -# with pytest.raises(aiohttp.web.HTTPForbidden): -# nio = VPCS.instance().create_nio(vm.vpcs_path, {"type": "nio_tap", "tap_device": "test"}) -# vm.port_add_nio_binding(0, nio) -# assert vm._ethernet_adapter.ports[0] is None -# - def test_port_remove_nio_binding(vm): - nio = VPCS.instance().create_nio(vm.vpcs_path, {"type": "nio_udp", "lport": 4242, "rport": 4243, "rhost": "127.0.0.1"}) + nio = VPCS.instance().create_nio(vm.vpcs_path(), {"type": "nio_udp", "lport": 4242, "rport": 4243, "rhost": "127.0.0.1"}) vm.port_add_nio_binding(0, nio) vm.port_remove_nio_binding(0) assert vm._ethernet_adapter.ports[0] is None From 838680cb40688c78bd0b5aff1ef55de57ac59d18 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Thu, 13 Apr 2017 17:30:25 +0200 Subject: [PATCH 21/53] 1.5.4 --- CHANGELOG | 9 +++++++++ gns3server/crash_report.py | 2 +- gns3server/version.py | 4 ++-- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 481805e5..dbca4af6 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,14 @@ # Change Log +## 1.5.4 13/04/2017 + +* Fix VPCS tests for recent version +* Freeze server dependencies to the same version used for 1.5.3 +* Fix 1.5: Error message, when stopping IOU router #769 +* Drop color logging for remote install, seem to fail in some conditions +* Cleanup the remote install script +* Support for Xenial in remote install + ## 1.5.3 12/01/2016 * Fix sporadically systemd is unable to start gns3-server diff --git a/gns3server/crash_report.py b/gns3server/crash_report.py index 81427ce9..bf94ad68 100644 --- a/gns3server/crash_report.py +++ b/gns3server/crash_report.py @@ -52,7 +52,7 @@ class CrashReport: Report crash to a third party service """ - DSN = "sync+https://700b0c46edb0473baacd2dc318d8de1f:824bd6d75471494ebcb87ce27cfdeade@sentry.io/38482" + DSN = "sync+https://a50622052018408097c33bb425dcb9bc:55f46ecbe2d04d6e9c3bd8d182bf2990@sentry.io/38482" if hasattr(sys, "frozen"): cacert = get_resource("cacert.pem") if cacert is not None and os.path.isfile(cacert): diff --git a/gns3server/version.py b/gns3server/version.py index 72202e2f..ea19764f 100644 --- a/gns3server/version.py +++ b/gns3server/version.py @@ -23,5 +23,5 @@ # or negative for a release candidate or beta (after the base version # number has been incremented) -__version__ = "1.5.4dev1" -__version_info__ = (1, 5, 4, -99) +__version__ = "1.5.4" +__version_info__ = (1, 5, 4, 0) From a9b76c965625a550664744e40982c16101e09c64 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Fri, 14 Apr 2017 08:46:31 +0200 Subject: [PATCH 22/53] 1.5.5dev1 --- gns3server/version.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gns3server/version.py b/gns3server/version.py index ea19764f..daa9ff7f 100644 --- a/gns3server/version.py +++ b/gns3server/version.py @@ -23,5 +23,5 @@ # or negative for a release candidate or beta (after the base version # number has been incremented) -__version__ = "1.5.4" -__version_info__ = (1, 5, 4, 0) +__version__ = "1.5.5dev1" +__version_info__ = (1, 5, 5, -99) From 5fb2a462c2736b7a6ce33bbefe5b0e26ffe68749 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Fri, 14 Apr 2017 08:53:53 +0200 Subject: [PATCH 23/53] Merge master --- CHANGELOG | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index c47aacf7..69d5f004 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,14 @@ # Change Log +## 1.5.4 13/04/2017 + +* Fix VPCS tests for recent version +* Freeze server dependencies to the same version used for 1.5.3 +* Fix 1.5: Error message, when stopping IOU router #769 +* Drop color logging for remote install, seem to fail in some conditions +* Cleanup the remote install script +* Support for Xenial in remote install + ## 2.0.0rc3 31/03/2017 * Support IOU image without .bin at the end From d2d5f003f82e10424e8c03e4cd2efc50403ac60a Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Fri, 14 Apr 2017 09:09:43 +0200 Subject: [PATCH 24/53] Fix a merge error --- tests/modules/test_project.py | 493 ----------------------------- tests/modules/vpcs/test_vpcs_vm.py | 284 ----------------- 2 files changed, 777 deletions(-) delete mode 100644 tests/modules/test_project.py delete mode 100644 tests/modules/vpcs/test_vpcs_vm.py diff --git a/tests/modules/test_project.py b/tests/modules/test_project.py deleted file mode 100644 index d6946770..00000000 --- a/tests/modules/test_project.py +++ /dev/null @@ -1,493 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (C) 2015 GNS3 Technologies Inc. -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -import os -import uuid -import json -import asyncio -import pytest -import aiohttp -import zipfile -from uuid import uuid4 -from unittest.mock import patch - -from tests.utils import asyncio_patch -from gns3server.modules.project import Project -from gns3server.modules.vpcs import VPCS, VPCSVM - - -@pytest.fixture(scope="module") -def manager(port_manager): - m = VPCS.instance() - m.port_manager = port_manager - return m - - -@pytest.fixture(scope="function") -def vm(project, manager, loop): - vm = manager.create_vm("test", project.id, "00010203-0405-0607-0809-0a0b0c0d0e0f") - return loop.run_until_complete(asyncio.async(vm)) - - -def test_affect_uuid(): - p = Project() - assert len(p.id) == 36 - - p = Project(project_id='00010203-0405-0607-0809-0a0b0c0d0e0f') - assert p.id == '00010203-0405-0607-0809-0a0b0c0d0e0f' - - -def test_path(tmpdir): - with patch("gns3server.modules.project.Project.is_local", return_value=True): - p = Project(location=str(tmpdir)) - assert p.path == os.path.join(str(tmpdir), p.id) - assert os.path.exists(os.path.join(str(tmpdir), p.id)) - assert not os.path.exists(os.path.join(p.path, ".gns3_temporary")) - - -def test_init_path(tmpdir): - - with patch("gns3server.modules.project.Project.is_local", return_value=True): - p = Project(path=str(tmpdir)) - assert p.path == str(tmpdir) - - -def test_changing_path_temporary_flag(tmpdir): - - with patch("gns3server.modules.project.Project.is_local", return_value=True): - p = Project(temporary=True) - assert os.path.exists(p.path) - original_path = p.path - assert os.path.exists(os.path.join(p.path, ".gns3_temporary")) - - p.path = str(tmpdir) - - -def test_temporary_path(): - p = Project(temporary=True) - assert os.path.exists(p.path) - assert os.path.exists(os.path.join(p.path, ".gns3_temporary")) - - -def test_remove_temporary_flag(): - p = Project(temporary=True) - assert os.path.exists(p.path) - assert os.path.exists(os.path.join(p.path, ".gns3_temporary")) - p.temporary = False - assert not os.path.exists(os.path.join(p.path, ".gns3_temporary")) - - -def test_changing_location_not_allowed(tmpdir): - with patch("gns3server.modules.project.Project.is_local", return_value=False): - with pytest.raises(aiohttp.web.HTTPForbidden): - p = Project(location=str(tmpdir)) - - -def test_changing_path_not_allowed(tmpdir): - with patch("gns3server.modules.project.Project.is_local", return_value=False): - with pytest.raises(aiohttp.web.HTTPForbidden): - p = Project() - p.path = str(tmpdir) - - -def test_changing_path_with_quote_not_allowed(tmpdir): - with patch("gns3server.modules.project.Project.is_local", return_value=True): - with pytest.raises(aiohttp.web.HTTPForbidden): - p = Project() - p.path = str(tmpdir / "project\"53") - - -def test_json(tmpdir): - p = Project() - assert p.__json__() == {"name": p.name, "location": p.location, "path": p.path, "project_id": p.id, "temporary": False} - - -def test_vm_working_directory(tmpdir, vm): - with patch("gns3server.modules.project.Project.is_local", return_value=True): - p = Project(location=str(tmpdir)) - assert p.vm_working_directory(vm) == os.path.join(str(tmpdir), p.id, 'project-files', vm.module_name, vm.id) - assert os.path.exists(p.vm_working_directory(vm)) - - -def test_mark_vm_for_destruction(vm): - project = Project() - project.add_vm(vm) - project.mark_vm_for_destruction(vm) - assert len(project._vms_to_destroy) == 1 - assert len(project.vms) == 0 - - -def test_commit(manager, loop): - project = Project() - vm = VPCSVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", project, manager) - project.add_vm(vm) - directory = project.vm_working_directory(vm) - project.mark_vm_for_destruction(vm) - assert len(project._vms_to_destroy) == 1 - assert os.path.exists(directory) - loop.run_until_complete(asyncio.async(project.commit())) - assert len(project._vms_to_destroy) == 0 - assert os.path.exists(directory) is False - assert len(project.vms) == 0 - - -def test_commit_permission_issue(manager, loop): - """ - GNS3 will fix the permission and continue to delete - """ - project = Project() - vm = VPCSVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", project, manager) - project.add_vm(vm) - directory = project.vm_working_directory(vm) - project.mark_vm_for_destruction(vm) - assert len(project._vms_to_destroy) == 1 - assert os.path.exists(directory) - os.chmod(directory, 0) - loop.run_until_complete(asyncio.async(project.commit())) - - -def test_project_delete(loop): - project = Project() - directory = project.path - assert os.path.exists(directory) - loop.run_until_complete(asyncio.async(project.delete())) - assert os.path.exists(directory) is False - - -def test_project_delete_permission_issue(loop): - project = Project() - directory = project.path - assert os.path.exists(directory) - os.chmod(directory, 0) - with pytest.raises(aiohttp.web.HTTPInternalServerError): - loop.run_until_complete(asyncio.async(project.delete())) - os.chmod(directory, 700) - - -def test_project_add_vm(manager): - project = Project() - vm = VPCSVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", project, manager) - project.add_vm(vm) - assert len(project.vms) == 1 - - -def test_project_close(loop, vm, project): - - with asyncio_patch("gns3server.modules.vpcs.vpcs_vm.VPCSVM.close") as mock: - loop.run_until_complete(asyncio.async(project.close())) - assert mock.called - assert vm.id not in vm.manager._vms - - -def test_project_close_temporary_project(loop, manager): - """A temporary project is deleted when closed""" - - project = Project(temporary=True) - directory = project.path - assert os.path.exists(directory) - loop.run_until_complete(asyncio.async(project.close())) - assert os.path.exists(directory) is False - - -def test_get_default_project_directory(monkeypatch): - - monkeypatch.undo() - project = Project() - path = os.path.normpath(os.path.expanduser("~/GNS3/projects")) - assert project._get_default_project_directory() == path - assert os.path.exists(path) - - -def test_clean_project_directory(tmpdir): - - # A non anonymous project with uuid. - project1 = tmpdir / str(uuid4()) - project1.mkdir() - - # A non anonymous project. - oldproject = tmpdir / str(uuid4()) - oldproject.mkdir() - - # an anonymous project - project2 = tmpdir / str(uuid4()) - project2.mkdir() - tmp = (project2 / ".gns3_temporary") - with open(str(tmp), 'w+') as f: - f.write("1") - - with patch("gns3server.config.Config.get_section_config", return_value={"project_directory": str(tmpdir)}): - Project.clean_project_directory() - - assert os.path.exists(str(project1)) - assert os.path.exists(str(oldproject)) - assert not os.path.exists(str(project2)) - - -def test_list_files(tmpdir, loop): - - with patch("gns3server.config.Config.get_section_config", return_value={"project_directory": str(tmpdir)}): - project = Project() - path = project.path - os.makedirs(os.path.join(path, "vm-1", "dynamips")) - with open(os.path.join(path, "vm-1", "dynamips", "test.bin"), "w+") as f: - f.write("test") - open(os.path.join(path, "vm-1", "dynamips", "test.ghost"), "w+").close() - with open(os.path.join(path, "test.txt"), "w+") as f: - f.write("test2") - - files = loop.run_until_complete(asyncio.async(project.list_files())) - - assert files == [ - { - "path": "test.txt", - "md5sum": "ad0234829205b9033196ba818f7a872b" - }, - { - "path": os.path.join("vm-1", "dynamips", "test.bin"), - "md5sum": "098f6bcd4621d373cade4e832627b4f6" - } - ] - - -def test_export(tmpdir): - project = Project() - path = project.path - os.makedirs(os.path.join(path, "vm-1", "dynamips")) - - # The .gns3 should be renamed project.gns3 in order to simplify import - with open(os.path.join(path, "test.gns3"), 'w+') as f: - f.write("{}") - - with open(os.path.join(path, "vm-1", "dynamips", "test"), 'w+') as f: - f.write("HELLO") - with open(os.path.join(path, "vm-1", "dynamips", "test_log.txt"), 'w+') as f: - f.write("LOG") - os.makedirs(os.path.join(path, "project-files", "snapshots")) - with open(os.path.join(path, "project-files", "snapshots", "test"), 'w+') as f: - f.write("WORLD") - - z = project.export() - - with open(str(tmpdir / 'zipfile.zip'), 'wb') as f: - for data in z: - f.write(data) - - with zipfile.ZipFile(str(tmpdir / 'zipfile.zip')) as myzip: - with myzip.open("vm-1/dynamips/test") as myfile: - content = myfile.read() - assert content == b"HELLO" - - assert 'test.gns3' not in myzip.namelist() - assert 'project.gns3' in myzip.namelist() - assert 'project-files/snapshots/test' not in myzip.namelist() - assert 'vm-1/dynamips/test_log.txt' not in myzip.namelist() - - -def test_export_fix_path(tmpdir): - """ - Fix absolute image path - """ - project = Project() - path = project.path - - topology = { - "topology": { - "nodes": [ - { - "properties": { - "image": "/tmp/c3725-adventerprisek9-mz.124-25d.image" - }, - "type": "C3725" - } - ] - } - } - - with open(os.path.join(path, "test.gns3"), 'w+') as f: - json.dump(topology, f) - - z = project.export() - with open(str(tmpdir / 'zipfile.zip'), 'wb') as f: - for data in z: - f.write(data) - - with zipfile.ZipFile(str(tmpdir / 'zipfile.zip')) as myzip: - with myzip.open("project.gns3") as myfile: - content = myfile.read().decode() - topology = json.loads(content) - assert topology["topology"]["nodes"][0]["properties"]["image"] == "c3725-adventerprisek9-mz.124-25d.image" - - -def test_export_with_images(tmpdir): - """ - Fix absolute image path - """ - project = Project() - path = project.path - - os.makedirs(str(tmpdir / "IOS")) - with open(str(tmpdir / "IOS" / "test.image"), "w+") as f: - f.write("AAA") - - topology = { - "topology": { - "nodes": [ - { - "properties": { - "image": "test.image" - }, - "type": "C3725" - } - ] - } - } - - with open(os.path.join(path, "test.gns3"), 'w+') as f: - json.dump(topology, f) - - with patch("gns3server.modules.Dynamips.get_images_directory", return_value=str(tmpdir / "IOS"),): - z = project.export(include_images=True) - with open(str(tmpdir / 'zipfile.zip'), 'wb') as f: - for data in z: - f.write(data) - - with zipfile.ZipFile(str(tmpdir / 'zipfile.zip')) as myzip: - myzip.getinfo("images/IOS/test.image") - - -def test_export_with_vm(tmpdir): - project = Project() - path = project.path - os.makedirs(os.path.join(path, "vm-1", "dynamips")) - - # The .gns3 should be renamed project.gns3 in order to simplify import - with open(os.path.join(path, "test.gns3"), 'w+') as f: - f.write("{}") - - with open(os.path.join(path, "vm-1", "dynamips", "test"), 'w+') as f: - f.write("HELLO") - with open(os.path.join(path, "vm-1", "dynamips", "test_log.txt"), 'w+') as f: - f.write("LOG") - os.makedirs(os.path.join(path, "project-files", "snapshots")) - with open(os.path.join(path, "project-files", "snapshots", "test"), 'w+') as f: - f.write("WORLD") - - os.makedirs(os.path.join(path, "servers", "vm", "project-files", "docker")) - with open(os.path.join(path, "servers", "vm", "project-files", "docker", "busybox"), 'w+') as f: - f.write("DOCKER") - - z = project.export() - - with open(str(tmpdir / 'zipfile.zip'), 'wb') as f: - for data in z: - f.write(data) - - with zipfile.ZipFile(str(tmpdir / 'zipfile.zip')) as myzip: - with myzip.open("vm-1/dynamips/test") as myfile: - content = myfile.read() - assert content == b"HELLO" - - assert 'test.gns3' not in myzip.namelist() - assert 'project.gns3' in myzip.namelist() - assert 'project-files/snapshots/test' not in myzip.namelist() - assert 'vm-1/dynamips/test_log.txt' not in myzip.namelist() - assert 'servers/vm/project-files/docker/busybox' not in myzip.namelist() - assert 'project-files/docker/busybox' in myzip.namelist() - - -def test_import(tmpdir): - - project_id = str(uuid.uuid4()) - project = Project(name="test", project_id=project_id) - - topology = { - "project_id": str(uuid.uuid4()), - "name": "testtest", - "topology": { - "nodes": [ - { - "server_id": 3, - "type": "VPCSDevice" - }, - { - "server_id": 3, - "type": "QemuVM" - } - ] - } - } - - with open(str(tmpdir / "project.gns3"), 'w+') as f: - json.dump(topology, f) - with open(str(tmpdir / "b.png"), 'w+') as f: - f.write("B") - - zip_path = str(tmpdir / "project.zip") - with zipfile.ZipFile(zip_path, 'w') as myzip: - myzip.write(str(tmpdir / "project.gns3"), "project.gns3") - myzip.write(str(tmpdir / "b.png"), "b.png") - myzip.write(str(tmpdir / "b.png"), "project-files/dynamips/test") - myzip.write(str(tmpdir / "b.png"), "project-files/qemu/test") - - with open(zip_path, "rb") as f: - project.import_zip(f) - - assert os.path.exists(os.path.join(project.path, "b.png")) - assert os.path.exists(os.path.join(project.path, "test.gns3")) - assert os.path.exists(os.path.join(project.path, "project-files/dynamips/test")) - assert os.path.exists(os.path.join(project.path, "servers/vm/project-files/qemu/test")) - - with open(os.path.join(project.path, "test.gns3")) as f: - content = json.load(f) - - assert content["name"] == "test" - assert content["project_id"] == project_id - assert content["topology"]["servers"] == [ - { - "id": 1, - "local": True, - "vm": False - }, - { - "id": 2, - "local": False, - "vm": True - }, - ] - assert content["topology"]["nodes"][0]["server_id"] == 1 - assert content["topology"]["nodes"][1]["server_id"] == 2 - - -def test_import_with_images(tmpdir): - - project_id = str(uuid.uuid4()) - project = Project(name="test", project_id=project_id) - - with open(str(tmpdir / "test.image"), 'w+') as f: - f.write("B") - - zip_path = str(tmpdir / "project.zip") - with zipfile.ZipFile(zip_path, 'w') as myzip: - myzip.write(str(tmpdir / "test.image"), "images/IOS/test.image") - - with open(zip_path, "rb") as f: - project.import_zip(f) - - # TEST import images - path = os.path.join(project._config().get("images_path"), "IOS", "test.image") - assert os.path.exists(path), path diff --git a/tests/modules/vpcs/test_vpcs_vm.py b/tests/modules/vpcs/test_vpcs_vm.py deleted file mode 100644 index 949bdf74..00000000 --- a/tests/modules/vpcs/test_vpcs_vm.py +++ /dev/null @@ -1,284 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (C) 2015 GNS3 Technologies Inc. -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -import pytest -import aiohttp -import asyncio -import os -import sys - -from tests.utils import asyncio_patch -from gns3server.utils import parse_version -from unittest.mock import patch, MagicMock - -from gns3server.modules.vpcs.vpcs_vm import VPCSVM -from gns3server.modules.vpcs.vpcs_error import VPCSError -from gns3server.modules.vpcs import VPCS - - -@pytest.fixture(scope="module") -def manager(port_manager): - m = VPCS.instance() - m.port_manager = port_manager - return m - - -@pytest.fixture(scope="function") -def vm(project, manager): - vm = VPCSVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", project, manager) - vm._vpcs_version = parse_version("0.9") - return vm - - -def test_vm(project, manager): - vm = VPCSVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", project, manager) - assert vm.name == "test" - assert vm.id == "00010203-0405-0607-0809-0a0b0c0d0e0f" - - -def test_vm_check_vpcs_version(loop, vm, manager): - with asyncio_patch("gns3server.modules.vpcs.vpcs_vm.subprocess_check_output", return_value="Welcome to Virtual PC Simulator, version 0.9"): - loop.run_until_complete(asyncio.async(vm._check_vpcs_version())) - assert vm._vpcs_version == parse_version("0.9") - - -def test_vm_check_vpcs_version_0_6_1(loop, vm, manager): - with asyncio_patch("gns3server.modules.vpcs.vpcs_vm.subprocess_check_output", return_value="Welcome to Virtual PC Simulator, version 0.6.1"): - loop.run_until_complete(asyncio.async(vm._check_vpcs_version())) - assert vm._vpcs_version == parse_version("0.6.1") - - -def test_vm_invalid_vpcs_version(loop, manager, vm): - with asyncio_patch("gns3server.modules.vpcs.vpcs_vm.subprocess_check_output", return_value="Welcome to Virtual PC Simulator, version 0.1"): - with pytest.raises(VPCSError): - nio = manager.create_nio(vm.vpcs_path(), {"type": "nio_udp", "lport": 4242, "rport": 4243, "rhost": "127.0.0.1"}) - vm.port_add_nio_binding(0, nio) - loop.run_until_complete(asyncio.async(vm._check_vpcs_version())) - assert vm.name == "test" - assert vm.id == "00010203-0405-0607-0809-0a0b0c0d0e0f" - - -def test_vm_invalid_vpcs_path(vm, manager, loop): - with patch("gns3server.modules.vpcs.vpcs_vm.VPCSVM.vpcs_path", return_value="/tmp/fake/path/vpcs"): - with pytest.raises(VPCSError): - nio = manager.create_nio(vm.vpcs_path(), {"type": "nio_udp", "lport": 4242, "rport": 4243, "rhost": "127.0.0.1"}) - vm.port_add_nio_binding(0, nio) - loop.run_until_complete(asyncio.async(vm.start())) - assert vm.name == "test" - assert vm.id == "00010203-0405-0607-0809-0a0b0c0d0e0e" - - -def test_start(loop, vm): - process = MagicMock() - process.returncode = None - queue = vm.project.get_listen_queue() - - with asyncio_patch("gns3server.modules.vpcs.vpcs_vm.VPCSVM._check_requirements", return_value=True): - with asyncio_patch("asyncio.create_subprocess_exec", return_value=process) as mock_exec: - nio = VPCS.instance().create_nio(vm.vpcs_path(), {"type": "nio_udp", "lport": 4242, "rport": 4243, "rhost": "127.0.0.1"}) - vm.port_add_nio_binding(0, nio) - loop.run_until_complete(asyncio.async(vm.start())) - assert mock_exec.call_args[0] == (vm.vpcs_path(), - '-p', - str(vm.console), - '-m', '1', - '-i', - '1', - '-F', - '-R', - '-s', - '4242', - '-c', - '4243', - '-t', - '127.0.0.1') - assert vm.is_running() - assert vm.command_line == ' '.join(mock_exec.call_args[0]) - (action, event) = queue.get_nowait() - assert action == "vm.started" - assert event == vm - - -def test_start_0_6_1(loop, vm): - """ - Version 0.6.1 doesn't have the -R options. It's not require - because GNS3 provide a patch for this. - """ - process = MagicMock() - process.returncode = None - queue = vm.project.get_listen_queue() - vm._vpcs_version = parse_version("0.6.1") - - with asyncio_patch("gns3server.modules.vpcs.vpcs_vm.VPCSVM._check_requirements", return_value=True): - with asyncio_patch("asyncio.create_subprocess_exec", return_value=process) as mock_exec: - nio = VPCS.instance().create_nio(vm.vpcs_path(), {"type": "nio_udp", "lport": 4242, "rport": 4243, "rhost": "127.0.0.1"}) - vm.port_add_nio_binding(0, nio) - loop.run_until_complete(asyncio.async(vm.start())) - assert mock_exec.call_args[0] == (vm.vpcs_path(), - '-p', - str(vm.console), - '-m', '1', - '-i', - '1', - '-F', - '-s', - '4242', - '-c', - '4243', - '-t', - '127.0.0.1') - assert vm.is_running() - (action, event) = queue.get_nowait() - assert action == "vm.started" - assert event == vm - - -def test_stop(loop, vm): - process = MagicMock() - - # Wait process kill success - future = asyncio.Future() - future.set_result(True) - process.wait.return_value = future - process.returncode = None - - with asyncio_patch("gns3server.modules.vpcs.vpcs_vm.VPCSVM._check_requirements", return_value=True): - with asyncio_patch("asyncio.create_subprocess_exec", return_value=process): - nio = VPCS.instance().create_nio(vm.vpcs_path(), {"type": "nio_udp", "lport": 4242, "rport": 4243, "rhost": "127.0.0.1"}) - vm.port_add_nio_binding(0, nio) - - loop.run_until_complete(asyncio.async(vm.start())) - assert vm.is_running() - - queue = vm.project.get_listen_queue() - - with asyncio_patch("gns3server.utils.asyncio.wait_for_process_termination"): - loop.run_until_complete(asyncio.async(vm.stop())) - assert vm.is_running() is False - - if sys.platform.startswith("win"): - process.send_signal.assert_called_with(1) - else: - process.terminate.assert_called_with() - - (action, event) = queue.get_nowait() - assert action == "vm.stopped" - assert event == vm - - -def test_reload(loop, vm): - process = MagicMock() - - # Wait process kill success - future = asyncio.Future() - future.set_result(True) - process.wait.return_value = future - process.returncode = None - - with asyncio_patch("gns3server.modules.vpcs.vpcs_vm.VPCSVM._check_requirements", return_value=True): - with asyncio_patch("asyncio.create_subprocess_exec", return_value=process): - nio = VPCS.instance().create_nio(vm.vpcs_path(), {"type": "nio_udp", "lport": 4242, "rport": 4243, "rhost": "127.0.0.1"}) - vm.port_add_nio_binding(0, nio) - loop.run_until_complete(asyncio.async(vm.start())) - assert vm.is_running() - - with asyncio_patch("gns3server.utils.asyncio.wait_for_process_termination"): - loop.run_until_complete(asyncio.async(vm.reload())) - assert vm.is_running() is True - - if sys.platform.startswith("win"): - process.send_signal.assert_called_with(1) - else: - process.terminate.assert_called_with() - - -def test_add_nio_binding_udp(vm): - nio = VPCS.instance().create_nio(vm.vpcs_path(), {"type": "nio_udp", "lport": 4242, "rport": 4243, "rhost": "127.0.0.1"}) - vm.port_add_nio_binding(0, nio) - assert nio.lport == 4242 - - -@pytest.mark.skipif(sys.platform.startswith("win"), reason="Not supported on Windows") -def test_add_nio_binding_tap(vm, ethernet_device): - with patch("gns3server.modules.base_manager.BaseManager.has_privileged_access", return_value=True): - nio = VPCS.instance().create_nio(vm.vpcs_path(), {"type": "nio_tap", "tap_device": ethernet_device}) - vm.port_add_nio_binding(0, nio) - assert nio.tap_device == ethernet_device - - -def test_port_remove_nio_binding(vm): - nio = VPCS.instance().create_nio(vm.vpcs_path(), {"type": "nio_udp", "lport": 4242, "rport": 4243, "rhost": "127.0.0.1"}) - vm.port_add_nio_binding(0, nio) - vm.port_remove_nio_binding(0) - assert vm._ethernet_adapter.ports[0] is None - - -def test_update_startup_script(vm): - content = "echo GNS3 VPCS\nip 192.168.1.2\n" - vm.startup_script = content - filepath = os.path.join(vm.working_dir, 'startup.vpc') - assert os.path.exists(filepath) - with open(filepath) as f: - assert f.read() == content - - -def test_update_startup_script_h(vm): - content = "setname %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" - - -def test_get_startup_script(vm): - content = "echo GNS3 VPCS\nip 192.168.1.2" - vm.startup_script = content - assert vm.startup_script == os.linesep.join(["echo GNS3 VPCS", "ip 192.168.1.2"]) - - -def test_get_startup_script_using_default_script(vm): - content = "echo GNS3 VPCS\nip 192.168.1.2\n" - - # Reset script file location - vm._script_file = None - - filepath = os.path.join(vm.working_dir, 'startup.vpc') - with open(filepath, 'wb+') as f: - assert f.write(content.encode("utf-8")) - - assert vm.startup_script == content - assert vm.script_file == filepath - - -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") - vm.name = "hello" - assert vm.name == "hello" - with open(path) as f: - assert f.read() == "name hello" - - -def test_close(vm, port_manager, loop): - with asyncio_patch("gns3server.modules.vpcs.vpcs_vm.VPCSVM._check_requirements", return_value=True): - with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()): - vm.start() - loop.run_until_complete(asyncio.async(vm.close())) - assert vm.is_running() is False From 57cdef6b020a91fdeb9c954a1fa36a5284c69f09 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Fri, 14 Apr 2017 10:33:19 +0200 Subject: [PATCH 25/53] Fix AttributeError: 'NoneType' object has no attribute 'returncode' Fix #976 --- gns3server/ubridge/hypervisor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gns3server/ubridge/hypervisor.py b/gns3server/ubridge/hypervisor.py index 1e6ae825..9deb7b83 100644 --- a/gns3server/ubridge/hypervisor.py +++ b/gns3server/ubridge/hypervisor.py @@ -199,7 +199,7 @@ class Hypervisor(UBridgeHypervisor): try: yield from wait_for_process_termination(self._process, timeout=3) except asyncio.TimeoutError: - if self._process.returncode is None: + if self._process and self._process.returncode is None: log.warn("uBridge process {} is still running... killing it".format(self._process.pid)) try: self._process.kill() From f3a87d76efeb76d7de6b49dcb1a2ad2ba673c478 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Fri, 14 Apr 2017 10:38:21 +0200 Subject: [PATCH 26/53] Fix import of some old topologies Fix #977 --- gns3server/controller/topology.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/gns3server/controller/topology.py b/gns3server/controller/topology.py index 00e87b66..ee27a652 100644 --- a/gns3server/controller/topology.py +++ b/gns3server/controller/topology.py @@ -259,8 +259,11 @@ def _convert_1_3_later(topo, topo_path): except KeyError: node["compute_id"] = "local" node["console_type"] = old_node["properties"].get("console_type", "telnet") - node["name"] = old_node["label"]["text"] - node["label"] = _convert_label(old_node["label"]) + if "label" in old_node: + node["name"] = old_node["label"]["text"] + node["label"] = _convert_label(old_node["label"]) + else: + node["name"] = old_node["properties"]["name"] node["node_id"] = old_node.get("vm_id", str(uuid.uuid4())) node["symbol"] = old_node.get("symbol", None) From 3518a781e9879253aa8e117623f35d7c7538eec9 Mon Sep 17 00:00:00 2001 From: Athmane Madjoudj Date: Tue, 18 Apr 2017 09:15:16 +0200 Subject: [PATCH 27/53] typing is already included in Py >= 3.5 (#979) --- requirements.txt | 1 - setup.py | 3 +++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 3bf3f042..763e3982 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,6 @@ jsonschema>=2.4.0 aiohttp==1.2.0 # pyup: ignore aiohttp-cors==0.4.0 # pyup: ignore yarl==0.8.1 # pyup: ignore -typing>=3.5.3.0 # Otherwise yarl fail with python 3.4 Jinja2>=2.7.3 raven>=5.23.0 psutil>=3.0.0 diff --git a/setup.py b/setup.py index 8d41046d..044d589d 100644 --- a/setup.py +++ b/setup.py @@ -40,6 +40,9 @@ class PyTest(TestCommand): dependencies = open("requirements.txt", "r").read().splitlines() +if sys.version_info <= (3, 4): + dependencies.append('typing>=3.5.3.0 # Otherwise yarl fail with python 3.4') + setup( name="gns3-server", version=__import__("gns3server").__version__, From 7302f65ffeed2fecf6d4854a86fe15ab2e9a82bb Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Tue, 18 Apr 2017 11:44:20 +0200 Subject: [PATCH 28/53] Catch timeout error on docker Fix #981 --- gns3server/compute/docker/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/gns3server/compute/docker/__init__.py b/gns3server/compute/docker/__init__.py index 835e7cd1..bd74f361 100644 --- a/gns3server/compute/docker/__init__.py +++ b/gns3server/compute/docker/__init__.py @@ -134,6 +134,8 @@ class Docker(BaseManager): ) except (aiohttp.ClientResponseError, aiohttp.ClientOSError) as e: raise DockerError("Docker has returned an error: {}".format(str(e))) + except (asyncio.TimeoutError): + raise DockerError("Docker timeout " + method + " " + path) if response.status >= 300: body = yield from response.read() try: From 5729d37992f72b8a3b6f2da0d6a4a4b0f393cbd6 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Tue, 18 Apr 2017 11:54:10 +0200 Subject: [PATCH 29/53] Setup appveyor --- appveyor.yml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 appveyor.yml diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 00000000..23774f99 --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,26 @@ +version: '{build}-{branch}' + +# Do not build feature branch with open Pull Requests +skip_branch_with_pr: true + +image: Visual Studio 2015 + +platform: x64 + +shallow_clone: true + + +environment: + matrix: + - PYTHON: "C:\\Python36-x64" + DISTUTILS_USE_SDK: "1" + - PYTHON: "C:\\Python35-x64" + DISTUTILS_USE_SDK: "1" + +install: + - "%PYTHON%\\python.exe -m pip install -r dev-requirements.txt" + +build: off + +test_script: + - "%PYTHON%\\python.exe -m py.test" From c50e80b2d3d96b445a2524357e43b87e0ad329db Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Tue, 18 Apr 2017 11:54:10 +0200 Subject: [PATCH 30/53] Setup appveyor --- appveyor.yml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 appveyor.yml diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 00000000..24f3dbe9 --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,26 @@ +version: '{build}-{branch}' + +# Do not build feature branch with open Pull Requests +skip_branch_with_pr: true + +image: Visual Studio 2015 + +platform: x64 + +shallow_clone: true + + +environment: + matrix: + - PYTHON: "C:\\Python36-x64" + DISTUTILS_USE_SDK: "1" + - PYTHON: "C:\\Python35-x64" + DISTUTILS_USE_SDK: "1" + +install: + - "%PYTHON%\\python.exe -m pip install -r dev-requirements.txt" + +build: off + +test_script: + - "%PYTHON%\\python.exe -m pytest" From 25a992a87053a3a6250cf7d45d3ac0808b58d8bb Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Tue, 18 Apr 2017 12:20:04 +0200 Subject: [PATCH 31/53] Build only for Python 3.6 on Windows --- appveyor.yml | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 24f3dbe9..26fc3a83 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,21 +1,14 @@ version: '{build}-{branch}' -# Do not build feature branch with open Pull Requests -skip_branch_with_pr: true - image: Visual Studio 2015 platform: x64 shallow_clone: true - environment: - matrix: - - PYTHON: "C:\\Python36-x64" - DISTUTILS_USE_SDK: "1" - - PYTHON: "C:\\Python35-x64" - DISTUTILS_USE_SDK: "1" + PYTHON: "C:\\Python36-x64" + DISTUTILS_USE_SDK: "1" install: - "%PYTHON%\\python.exe -m pip install -r dev-requirements.txt" From 722028b2d2b78b7a09a3ad3849f881f9127e2792 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Tue, 18 Apr 2017 12:27:54 +0200 Subject: [PATCH 32/53] Install win32 dependencies for the tests --- appveyor.yml | 1 + win-requirements.txt | 3 +++ 2 files changed, 4 insertions(+) create mode 100644 win-requirements.txt diff --git a/appveyor.yml b/appveyor.yml index 26fc3a83..c933be78 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -12,6 +12,7 @@ environment: install: - "%PYTHON%\\python.exe -m pip install -r dev-requirements.txt" + - "%PYTHON%\\python.exe -m pip install -r win-requirements.txt" build: off diff --git a/win-requirements.txt b/win-requirements.txt new file mode 100644 index 00000000..5905e970 --- /dev/null +++ b/win-requirements.txt @@ -0,0 +1,3 @@ +-rrequirements.txt + +pypiwin32 # pyup: ignore From 0bf6ed52cd8ca39d04eb14dec6be093897d575bb Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Tue, 18 Apr 2017 13:39:34 +0200 Subject: [PATCH 33/53] Verbose test on windows --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index c933be78..225725c8 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -17,4 +17,4 @@ install: build: off test_script: - - "%PYTHON%\\python.exe -m pytest" + - "%PYTHON%\\python.exe -m pytest -v" From 0d687e62ad87ce5f9a2e8f9e6ed0a41327dca662 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Tue, 18 Apr 2017 13:48:28 +0200 Subject: [PATCH 34/53] Try to enable test on appveyor --- appveyor.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 225725c8..cbcbec2b 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -4,8 +4,6 @@ image: Visual Studio 2015 platform: x64 -shallow_clone: true - environment: PYTHON: "C:\\Python36-x64" DISTUTILS_USE_SDK: "1" From 51ef5d8c905965dd885c9f2842a44a946c98b4cf Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Tue, 18 Apr 2017 14:04:50 +0200 Subject: [PATCH 35/53] Fix a broken test on windows machines --- tests/utils/test_images.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/utils/test_images.py b/tests/utils/test_images.py index 8b9b5c24..7de59eb8 100644 --- a/tests/utils/test_images.py +++ b/tests/utils/test_images.py @@ -133,7 +133,7 @@ def test_list_images(tmpdir): 'filename': 'test2.image', 'filesize': 7, 'md5sum': 'b0d5aa897d937aced5a6b1046e8f7e2e', - 'path': str(path2) + 'path': force_unix_path(str(path2)) } ] From 3de04d6e76d1b0a4f4f5c5ff5177dcfd81bdbd1a Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Tue, 18 Apr 2017 14:08:37 +0200 Subject: [PATCH 36/53] Install winpcap for the windows tests --- appveyor.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/appveyor.yml b/appveyor.yml index cbcbec2b..3867c85f 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -9,6 +9,7 @@ environment: DISTUTILS_USE_SDK: "1" install: + - cinst nmap - "%PYTHON%\\python.exe -m pip install -r dev-requirements.txt" - "%PYTHON%\\python.exe -m pip install -r win-requirements.txt" From 36de30e25e9826727673d552049643e8dcdb8dda Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Tue, 18 Apr 2017 14:23:29 +0200 Subject: [PATCH 37/53] Fix various tests for windows --- gns3server/utils/images.py | 2 +- tests/handlers/api/controller/test_symbol.py | 6 +----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/gns3server/utils/images.py b/gns3server/utils/images.py index 0f7bc8f5..794d19e7 100644 --- a/gns3server/utils/images.py +++ b/gns3server/utils/images.py @@ -79,7 +79,7 @@ def list_images(type): images.append({ "filename": filename, - "path": path, + "path": force_unix_path(path), "md5sum": md5sum(os.path.join(root, filename)), "filesize": os.stat(os.path.join(root, filename)).st_size}) except OSError as e: diff --git a/tests/handlers/api/controller/test_symbol.py b/tests/handlers/api/controller/test_symbol.py index be6a4010..0d41f441 100644 --- a/tests/handlers/api/controller/test_symbol.py +++ b/tests/handlers/api/controller/test_symbol.py @@ -35,11 +35,7 @@ def test_symbols(http_controller): def test_get(http_controller): response = http_controller.get('/symbols/' + urllib.parse.quote(':/symbols/firewall.svg') + '/raw') assert response.status == 200 - # Different carriage return - if sys.platform.startswith("win"): - assert response.headers['CONTENT-LENGTH'] == '9568' - else: - assert response.headers['CONTENT-LENGTH'] == '9381' + assert response.headers['CONTENT-LENGTH'] == '9381' assert response.headers['CONTENT-TYPE'] == 'image/svg+xml' assert '' in response.html From 691c2dfd8e67ce976892908203713c9da58be467 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Tue, 18 Apr 2017 14:27:44 +0200 Subject: [PATCH 38/53] Drop a test about OVA no longer use --- tests/compute/test_manager.py | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/tests/compute/test_manager.py b/tests/compute/test_manager.py index 72412ce9..fabb6f69 100644 --- a/tests/compute/test_manager.py +++ b/tests/compute/test_manager.py @@ -219,18 +219,6 @@ def test_get_relative_image_path(qemu, tmpdir, config): assert qemu.get_relative_image_path(path5) == path5 -def test_get_relative_image_path_ova(qemu, tmpdir, config): - os.makedirs(str(tmpdir / "QEMU" / "test.ova")) - path = str(tmpdir / "QEMU" / "test.ova" / "test.bin") - open(path, 'w+').close() - - config.set_section_config("Server", { - "images_path": str(tmpdir) - }) - assert qemu.get_relative_image_path(path) == os.path.join("test.ova", "test.bin") - assert qemu.get_relative_image_path(os.path.join("test.ova", "test.bin")) == os.path.join("test.ova", "test.bin") - - def test_list_images(loop, qemu, tmpdir): fake_images = ["a.qcow2", "b.qcow2", ".blu.qcow2", "a.qcow2.md5sum"] From b4434b5134c673bd5b7e79f135f4dfd9f582db74 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Tue, 18 Apr 2017 15:50:11 +0200 Subject: [PATCH 39/53] Add tests for checking if path are correctly cleaned --- tests/test_utils.py | 2 ++ tests/utils/test_images.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/test_utils.py b/tests/test_utils.py index af7d9815..0dbcc3b8 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -24,6 +24,8 @@ def test_force_unix_path(): assert force_unix_path("a\\b") == "a/b" assert force_unix_path("a\\b\\..\\c") == "a/c" assert force_unix_path("C:\Temp") == "C:/Temp" + assert force_unix_path(force_unix_path("C:\Temp")) == "C:/Temp" + assert force_unix_path("a//b") == "a/b" def test_macaddress_to_int(): diff --git a/tests/utils/test_images.py b/tests/utils/test_images.py index 7de59eb8..8b9b5c24 100644 --- a/tests/utils/test_images.py +++ b/tests/utils/test_images.py @@ -133,7 +133,7 @@ def test_list_images(tmpdir): 'filename': 'test2.image', 'filesize': 7, 'md5sum': 'b0d5aa897d937aced5a6b1046e8f7e2e', - 'path': force_unix_path(str(path2)) + 'path': str(path2) } ] From 54014ccd398c79bdbf24c4585742de2c6334c4e0 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Tue, 18 Apr 2017 16:01:07 +0200 Subject: [PATCH 40/53] Fix one more windows test --- tests/compute/test_manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/compute/test_manager.py b/tests/compute/test_manager.py index fabb6f69..ba7abaa1 100644 --- a/tests/compute/test_manager.py +++ b/tests/compute/test_manager.py @@ -250,7 +250,7 @@ def test_list_images_recursives(loop, qemu, tmpdir): assert loop.run_until_complete(qemu.list_images()) == [ {"filename": "a.qcow2", "path": "a.qcow2", "md5sum": "c4ca4238a0b923820dcc509a6f75849b", "filesize": 1}, {"filename": "b.qcow2", "path": "b.qcow2", "md5sum": "c4ca4238a0b923820dcc509a6f75849b", "filesize": 1}, - {"filename": "c.qcow2", "path": os.path.sep.join(["c", "c.qcow2"]), "md5sum": "c4ca4238a0b923820dcc509a6f75849b", "filesize": 1} + {"filename": "c.qcow2", "path": force_unix_path(os.path.sep.join(["c", "c.qcow2"])), "md5sum": "c4ca4238a0b923820dcc509a6f75849b", "filesize": 1} ] From c72ae1bfe701959f89b5c456c3240be68f6b963c Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Tue, 18 Apr 2017 16:21:45 +0200 Subject: [PATCH 41/53] Fix IOU test run on Windows --- tests/utils/test_images.py | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/tests/utils/test_images.py b/tests/utils/test_images.py index 8b9b5c24..cce60775 100644 --- a/tests/utils/test_images.py +++ b/tests/utils/test_images.py @@ -16,6 +16,7 @@ # along with this program. If not, see . import os +import sys from unittest.mock import patch @@ -105,9 +106,10 @@ def test_list_images(tmpdir): path = tmpdir / "images2" / "test_invalid.image" path.write(b'NOTANELF', ensure=True) - path3 = tmpdir / "images1" / "IOU" / "test3.bin" - path3.write(b'\x7fELF\x01\x02\x01', ensure=True) - path3 = force_unix_path(str(path3)) + if sys.platform.startswith("linux"): + path3 = tmpdir / "images1" / "IOU" / "test3.bin" + path3.write(b'\x7fELF\x01\x02\x01', ensure=True) + path3 = force_unix_path(str(path3)) path4 = tmpdir / "images1" / "QEMU" / "test4.qcow2" path4.write("1", ensure=True) @@ -137,14 +139,15 @@ def test_list_images(tmpdir): } ] - assert list_images("iou") == [ - { - 'filename': 'test3.bin', - 'filesize': 7, - 'md5sum': 'b0d5aa897d937aced5a6b1046e8f7e2e', - 'path': 'test3.bin' - } - ] + if sys.platform.startswith("linux"): + assert list_images("iou") == [ + { + 'filename': 'test3.bin', + 'filesize': 7, + 'md5sum': 'b0d5aa897d937aced5a6b1046e8f7e2e', + 'path': 'test3.bin' + } + ] assert list_images("qemu") == [ { From 37e21f2a3db68527dfadccd41a23a715213ac7c7 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Tue, 18 Apr 2017 16:35:36 +0200 Subject: [PATCH 42/53] Skip a test for qemu not supported on windows --- tests/compute/qemu/test_qemu_vm.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/compute/qemu/test_qemu_vm.py b/tests/compute/qemu/test_qemu_vm.py index 46c1c992..71ba1f71 100644 --- a/tests/compute/qemu/test_qemu_vm.py +++ b/tests/compute/qemu/test_qemu_vm.py @@ -154,6 +154,7 @@ def test_termination_callback(vm, async_run): assert event == vm +@pytest.mark.skipif(sys.platform.startswith("win"), reason="Not supported on Windows") def test_termination_callback_error(vm, tmpdir, async_run): with open(str(tmpdir / "qemu.log"), "w+") as f: From d9b93ccd66a6907a6adc07b344d52e47ebd47930 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Tue, 18 Apr 2017 18:04:00 +0200 Subject: [PATCH 43/53] Fix an issue with editing network on windows Fix #982 --- gns3server/handlers/api/controller/node_handler.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gns3server/handlers/api/controller/node_handler.py b/gns3server/handlers/api/controller/node_handler.py index d9a715f0..50166e88 100644 --- a/gns3server/handlers/api/controller/node_handler.py +++ b/gns3server/handlers/api/controller/node_handler.py @@ -20,6 +20,7 @@ import aiohttp from gns3server.web.route import Route from gns3server.controller import Controller +from gns3server.utils import force_unix_path from gns3server.schemas.node import ( NODE_OBJECT_SCHEMA, @@ -337,7 +338,7 @@ class NodeHandler: project = yield from Controller.instance().get_loaded_project(request.match_info["project_id"]) node = project.get_node(request.match_info["node_id"]) path = request.match_info["path"] - path = os.path.normpath(path) + path = force_unix_path(path) # Raise error if user try to escape if path[0] == ".": From 2962649dc82ef9a00b5a84655bce4e219c686822 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Wed, 19 Apr 2017 09:17:32 +0200 Subject: [PATCH 44/53] Fix a race condition when handling error at project opening Fix #983 --- 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 6622145f..c71ce360 100644 --- a/gns3server/controller/project.py +++ b/gns3server/controller/project.py @@ -650,7 +650,7 @@ class Project: 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: + for compute in list(self._project_created_on_compute): try: yield from compute.post("/projects/{}/close".format(self._id)) # We don't care if a compute is down at this step From d90a8fa4827dfd1d1fa192b567a455c753aeb27b Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Wed, 19 Apr 2017 10:09:52 +0200 Subject: [PATCH 45/53] Trigger nightly build when pushing --- appveyor.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 3867c85f..f2e2f941 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -7,6 +7,8 @@ platform: x64 environment: PYTHON: "C:\\Python36-x64" DISTUTILS_USE_SDK: "1" + API_TOKEN: + secure: VEKn4bYH3QO0ixtQW5ni4Enmn8cS1NlZV246ludBDgQ= install: - cinst nmap @@ -16,4 +18,7 @@ install: build: off test_script: - - "%PYTHON%\\python.exe -m pytest -v" + - "%PYTHON%\\python.exe -m pytest -v" + +deploy_script: + - curl "https://ci.appveyor.com/api/builds" -XPOST -H 'Authorization: Bearer %API_TOKEN%' -H 'Content-Type: application/json' -d '{"accountName": "gns3-build", "projectSlug": "gns3-build", "branch": "%APPVEYOR_REPO_BRANCH%"} From 11321383c55cb5b50b03f8ec6e5090c39e961826 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Wed, 19 Apr 2017 15:53:39 +0200 Subject: [PATCH 46/53] Remove not working trigger of nightly build --- appveyor.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index f2e2f941..d35cafe6 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -19,6 +19,3 @@ build: off test_script: - "%PYTHON%\\python.exe -m pytest -v" - -deploy_script: - - curl "https://ci.appveyor.com/api/builds" -XPOST -H 'Authorization: Bearer %API_TOKEN%' -H 'Content-Type: application/json' -d '{"accountName": "gns3-build", "projectSlug": "gns3-build", "branch": "%APPVEYOR_REPO_BRANCH%"} From 456a28304de4fd006d0fe63d84a6a71176383073 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Thu, 20 Apr 2017 10:52:55 +0200 Subject: [PATCH 47/53] 2.0.0rc4 --- CHANGELOG | 17 +++++++++++++++++ gns3server/crash_report.py | 2 +- gns3server/version.py | 2 +- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 69d5f004..120f8721 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,22 @@ # Change Log +## 2.0.0rc4 20/04/2017 + +* Fix a race condition when handling error at project opening +* Fix an issue with editing network on windows +* Fix windows tests +* Catch timeout error on docker +* typing is already included in Py >= 3.5 (#979) +* Fix import of some old topologies +* Fix AttributeError: 'NoneType' object has no attribute 'returncode' +* Fix ghost vmware vms +* Fix required field in schema not use +* Catch error and log them when we can't write the config +* Fix bridge 'bridge0' already exist when we have trouble with a container +* Catch an error at startup when the remote GNS3 VM is not a real GNS3 VM +* Fixes Qemu sata option. Ref #875. +* Catch GNS3 VM loading error at startup + ## 1.5.4 13/04/2017 * Fix VPCS tests for recent version diff --git a/gns3server/crash_report.py b/gns3server/crash_report.py index 4d13eadf..6b486de1 100644 --- a/gns3server/crash_report.py +++ b/gns3server/crash_report.py @@ -54,7 +54,7 @@ class CrashReport: Report crash to a third party service """ - DSN = "sync+https://71c8e5474e304e2384abc4df1b245acb:9f6d1b1b885a4ad1895f55338d4522d7@sentry.io/38482" + DSN = "sync+https://19cca90b55874be5862caf9b507fbd7b:1c0897efd092467a874e89b2e4803b29@sentry.io/38482" if hasattr(sys, "frozen"): cacert = get_resource("cacert.pem") if cacert is not None and os.path.isfile(cacert): diff --git a/gns3server/version.py b/gns3server/version.py index 26e862d6..b3de7df2 100644 --- a/gns3server/version.py +++ b/gns3server/version.py @@ -23,7 +23,7 @@ # or negative for a release candidate or beta (after the base version # number has been incremented) -__version__ = "2.0.0dev12" +__version__ = "2.0.0rc4" # If it's a git checkout try to add the commit if "dev" in __version__: From 78e030b7ab3c89366532829a2c8fca3f01361479 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Thu, 20 Apr 2017 10:55:36 +0200 Subject: [PATCH 48/53] 2.0.0dev13 --- gns3server/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gns3server/version.py b/gns3server/version.py index b3de7df2..e7a795dd 100644 --- a/gns3server/version.py +++ b/gns3server/version.py @@ -23,7 +23,7 @@ # or negative for a release candidate or beta (after the base version # number has been incremented) -__version__ = "2.0.0rc4" +__version__ = "2.0.0dev13" # If it's a git checkout try to add the commit if "dev" in __version__: From c9ceeee9de2da1cb1c18ae2fef34703bf1c65113 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Mon, 24 Apr 2017 17:31:20 +0200 Subject: [PATCH 49/53] Handle some invalid SVG images Fix #986 --- gns3server/utils/picture.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/gns3server/utils/picture.py b/gns3server/utils/picture.py index 50d17996..1ec072de 100644 --- a/gns3server/utils/picture.py +++ b/gns3server/utils/picture.py @@ -17,7 +17,7 @@ import io import struct -from xml.etree.ElementTree import ElementTree +from xml.etree.ElementTree import ElementTree, ParseError def get_size(data, default_width=0, default_height=0): @@ -95,7 +95,11 @@ def get_size(data, default_width=0, default_height=0): filetype = "svg" fhandle = io.BytesIO(data) tree = ElementTree() - tree.parse(fhandle) + try: + tree.parse(fhandle) + except ParseError: + raise ValueError("Invalid SVG file") + root = tree.getroot() try: From 8b9f22c30c154c5508caeb2b9ee16347d09d929d Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Mon, 24 Apr 2017 17:37:41 +0200 Subject: [PATCH 50/53] Handling server disconnect error when docker daemon die Fix #985 --- gns3server/compute/docker/__init__.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/gns3server/compute/docker/__init__.py b/gns3server/compute/docker/__init__.py index bd74f361..3a70b282 100644 --- a/gns3server/compute/docker/__init__.py +++ b/gns3server/compute/docker/__init__.py @@ -20,10 +20,10 @@ Docker server module. """ import sys +import json import asyncio import logging import aiohttp -import json from gns3server.utils import parse_version from gns3server.utils.asyncio import locked_coroutine from gns3server.compute.base_manager import BaseManager @@ -189,7 +189,10 @@ class Docker(BaseManager): # The pull api will stream status via an HTTP JSON stream content = "" while True: - chunk = yield from response.content.read(1024) + try: + chunk = yield from response.content.read(1024) + except aiohttp.errors.ServerDisconnectedError: + break if not chunk: break content += chunk.decode("utf-8") From effbe594142a0b1cfd735c5c391f2e7d272dbff2 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Mon, 24 Apr 2017 17:51:55 +0200 Subject: [PATCH 51/53] Lock docker API to 1.27 Fix https://github.com/GNS3/gns3-gui/issues/1994 --- gns3server/compute/docker/__init__.py | 4 ++-- tests/compute/docker/test_docker.py | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/gns3server/compute/docker/__init__.py b/gns3server/compute/docker/__init__.py index 3a70b282..ba092a56 100644 --- a/gns3server/compute/docker/__init__.py +++ b/gns3server/compute/docker/__init__.py @@ -33,7 +33,7 @@ from gns3server.compute.docker.docker_error import DockerError, DockerHttp304Err log = logging.getLogger(__name__) -DOCKER_MINIMUM_API_VERSION = "1.21" +DOCKER_MINIMUM_API_VERSION = "1.27" class Docker(BaseManager): @@ -113,7 +113,7 @@ class Docker(BaseManager): :returns: HTTP response """ data = json.dumps(data) - url = "http://docker/" + path + url = "http://docker/v" + DOCKER_MINIMUM_API_VERSION + "/" + path if timeout is None: timeout = 60 * 60 * 24 * 31 # One month timeout diff --git a/tests/compute/docker/test_docker.py b/tests/compute/docker/test_docker.py index 25779ca6..72c4cb79 100644 --- a/tests/compute/docker/test_docker.py +++ b/tests/compute/docker/test_docker.py @@ -47,7 +47,7 @@ def test_query_success(loop, vm): vm._session.request = AsyncioMagicMock(return_value=response) data = loop.run_until_complete(asyncio.async(vm.query("POST", "test", data={"a": True}, params={"b": 1}))) vm._session.request.assert_called_with('POST', - 'http://docker/test', + 'http://docker/v1.27/test', data='{"a": true}', headers={'content-type': 'application/json'}, params={'b': 1}, @@ -70,7 +70,7 @@ def test_query_error(loop, vm): with pytest.raises(DockerError): data = loop.run_until_complete(asyncio.async(vm.query("POST", "test", data={"a": True}, params={"b": 1}))) vm._session.request.assert_called_with('POST', - 'http://docker/test', + 'http://docker/v1.27/test', data='{"a": true}', headers={'content-type': 'application/json'}, params={'b': 1}, @@ -91,7 +91,7 @@ def test_query_error_json(loop, vm): with pytest.raises(DockerError): data = loop.run_until_complete(asyncio.async(vm.query("POST", "test", data={"a": True}, params={"b": 1}))) vm._session.request.assert_called_with('POST', - 'http://docker/test', + 'http://docker/v1.27/test', data='{"a": true}', headers={'content-type': 'application/json'}, params={'b': 1}, From 1a90305baabdd1720185e0e648e02730eb51bc60 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Mon, 24 Apr 2017 18:43:12 +0200 Subject: [PATCH 52/53] Docker minimum api is 1.25 --- gns3server/compute/docker/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gns3server/compute/docker/__init__.py b/gns3server/compute/docker/__init__.py index ba092a56..53b4a7af 100644 --- a/gns3server/compute/docker/__init__.py +++ b/gns3server/compute/docker/__init__.py @@ -33,7 +33,7 @@ from gns3server.compute.docker.docker_error import DockerError, DockerHttp304Err log = logging.getLogger(__name__) -DOCKER_MINIMUM_API_VERSION = "1.27" +DOCKER_MINIMUM_API_VERSION = "1.25" class Docker(BaseManager): From 2da581139c4c4073a0116a00e533100c36b31c50 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Mon, 24 Apr 2017 18:49:47 +0200 Subject: [PATCH 53/53] Lower docker requirements in tests also --- tests/compute/docker/test_docker.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/compute/docker/test_docker.py b/tests/compute/docker/test_docker.py index 72c4cb79..095a25de 100644 --- a/tests/compute/docker/test_docker.py +++ b/tests/compute/docker/test_docker.py @@ -47,7 +47,7 @@ def test_query_success(loop, vm): vm._session.request = AsyncioMagicMock(return_value=response) data = loop.run_until_complete(asyncio.async(vm.query("POST", "test", data={"a": True}, params={"b": 1}))) vm._session.request.assert_called_with('POST', - 'http://docker/v1.27/test', + 'http://docker/v1.25/test', data='{"a": true}', headers={'content-type': 'application/json'}, params={'b': 1}, @@ -70,7 +70,7 @@ def test_query_error(loop, vm): with pytest.raises(DockerError): data = loop.run_until_complete(asyncio.async(vm.query("POST", "test", data={"a": True}, params={"b": 1}))) vm._session.request.assert_called_with('POST', - 'http://docker/v1.27/test', + 'http://docker/v1.25/test', data='{"a": true}', headers={'content-type': 'application/json'}, params={'b': 1}, @@ -91,7 +91,7 @@ def test_query_error_json(loop, vm): with pytest.raises(DockerError): data = loop.run_until_complete(asyncio.async(vm.query("POST", "test", data={"a": True}, params={"b": 1}))) vm._session.request.assert_called_with('POST', - 'http://docker/v1.27/test', + 'http://docker/v1.25/test', data='{"a": true}', headers={'content-type': 'application/json'}, params={'b': 1},