diff --git a/.gitignore b/.gitignore index 0098f8e6..c19746a9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,8 @@ *.py[cod] +#py.test +.cache + # C extensions *.so diff --git a/.travis.yml b/.travis.yml index 8e41759c..047aa812 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,8 +9,5 @@ install: - pip install coveralls script: - py.test -v -s tests --cov gns3server --cov-report term-missing -notifications: - slack: - secure: gsbGgtNbJ0ElyPawA9DNunVgVD4SHmB/vDeQBMnmnw3g8Z01iNiiFLaaiUo/Vr65Yd0KMujw7ocC5aiSsgPWF3dEC57ntogWcMF2tf9YlbHdY+90HlIs7k7/xf3vRdrE+rNy24svvU7zaxYCL9gO70PQqIT6nI2Dgy4qQFiwr2s= after_success: - coveralls diff --git a/CHANGELOG b/CHANGELOG index f4489a60..1693fb3a 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,8 @@ # Change Log +## 1.4.0 12/01/2016 +* Release 1.4.0 + ## 1.4.0rc3 05/01/2016 * API documentation update diff --git a/dev-requirements.txt b/dev-requirements.txt index 366dee02..ccd510e5 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -3,6 +3,6 @@ sphinx==1.2.3 pytest==2.8.2 pep8==1.5.7 -pytest-timeout -pytest-capturelog -pytest-cov +pytest-capturelog==0.7 +pytest-cov==2.2.0 +pytest-timeout==0.5 diff --git a/gns3server/modules/dynamips/__init__.py b/gns3server/modules/dynamips/__init__.py index 50ed4522..dafd9c0c 100644 --- a/gns3server/modules/dynamips/__init__.py +++ b/gns3server/modules/dynamips/__init__.py @@ -602,6 +602,10 @@ class Dynamips(BaseManager): elif startup_config_content: startup_config_path = self._create_config(vm, default_startup_config_path, startup_config_content) yield from vm.set_configs(startup_config_path) + # An empty startup config crash dynamips + else: + startup_config_path = self._create_config(vm, default_startup_config_path, "!\n") + yield from vm.set_configs(startup_config_path) private_config_path = settings.get("private_config") private_config_content = settings.get("private_config_content") diff --git a/gns3server/modules/dynamips/nodes/router.py b/gns3server/modules/dynamips/nodes/router.py index 59e40b8e..9ebbbac8 100644 --- a/gns3server/modules/dynamips/nodes/router.py +++ b/gns3server/modules/dynamips/nodes/router.py @@ -281,6 +281,7 @@ class Router(BaseVM): yield from self._hypervisor.send('vm stop "{name}"'.format(name=self._name)) self.status = "stopped" log.info('Router "{name}" [{id}] has been stopped'.format(name=self._name, id=self._id)) + yield from self.save_configs() @asyncio.coroutine def reload(self): @@ -352,7 +353,6 @@ class Router(BaseVM): if self._hypervisor and not self._hypervisor.devices: try: yield from self.stop() - yield from self.save_configs() yield from self._hypervisor.send('vm delete "{}"'.format(self._name)) except DynamipsError: pass diff --git a/gns3server/modules/iou/iou_vm.py b/gns3server/modules/iou/iou_vm.py index d6cfefa6..d021b54b 100644 --- a/gns3server/modules/iou/iou_vm.py +++ b/gns3server/modules/iou/iou_vm.py @@ -400,7 +400,7 @@ class IOUVM(BaseVM): raise IOUError("License section not found in iourc file {}".format(self.iourc_path)) hostname = socket.gethostname() if hostname not in config["license"]: - raise IOUError("Hostname key not found in iourc file {}".format(self.iourc_path)) + raise IOUError("Hostname \"{}\" not found in iourc file {}".format(hostname, self.iourc_path)) user_ioukey = config["license"][hostname] if user_ioukey[-1:] != ';': raise IOUError("IOU key not ending with ; in iourc file".format(self.iourc_path)) @@ -1256,10 +1256,10 @@ class IOUVM(BaseVM): nio.startPacketCapture(output_file, data_link_type) log.info('IOU "{name}" [{id}]: starting packet capture on {adapter_number}/{port_number} to {output_file}'.format(name=self._name, - id=self._id, - adapter_number=adapter_number, - port_number=port_number, - output_file=output_file)) + id=self._id, + adapter_number=adapter_number, + port_number=port_number, + output_file=output_file)) if self.is_iouyap_running(): self._update_iouyap_config() diff --git a/gns3server/modules/qemu/qemu_vm.py b/gns3server/modules/qemu/qemu_vm.py index 077d7469..24cbd41b 100644 --- a/gns3server/modules/qemu/qemu_vm.py +++ b/gns3server/modules/qemu/qemu_vm.py @@ -157,7 +157,11 @@ class QemuVM(BaseVM): else: qemu_bin = os.path.basename(qemu_path) qemu_bin = re.sub(r'(w)?\.(exe|EXE)$', '', qemu_bin) - self._platform = re.sub(r'^qemu-system-(.*)$', r'\1', qemu_bin, re.IGNORECASE) + # Old version of GNS3 provide a binary named qemu.exe + if qemu_bin == "qemu": + self._platform = "i386" + else: + self._platform = re.sub(r'^qemu-system-(.*)$', r'\1', qemu_bin, re.IGNORECASE) if self._platform.split(".")[0] not in QEMU_PLATFORMS: raise QemuError("Platform {} is unknown".format(self._platform)) log.info('QEMU VM "{name}" [{id}] has set the QEMU path to {qemu_path}'.format(name=self._name, diff --git a/gns3server/modules/vmware/__init__.py b/gns3server/modules/vmware/__init__.py index 72f1761e..f4b3e533 100644 --- a/gns3server/modules/vmware/__init__.py +++ b/gns3server/modules/vmware/__init__.py @@ -540,7 +540,13 @@ class VMware(BaseManager): """ if sys.platform.startswith("win"): - return os.path.expandvars(r"%USERPROFILE%\Documents\Virtual Machines") + from win32com.shell import shell, shellcon + documents_folder = shell.SHGetSpecialFolderPath(None, shellcon.CSIDL_PERSONAL) + windows_type = sys.getwindowsversion().product_type + if windows_type == 2 or windows_type == 3: + return '{}\My Virtual Machines'.format(documents_folder) + else: + return '{}\Virtual Machines'.format(documents_folder) elif sys.platform.startswith("darwin"): return os.path.expanduser("~/Documents/Virtual Machines.localized") else: diff --git a/gns3server/schemas/qemu.py b/gns3server/schemas/qemu.py index 89362fbe..a50f9271 100644 --- a/gns3server/schemas/qemu.py +++ b/gns3server/schemas/qemu.py @@ -296,7 +296,7 @@ QEMU_UPDATE_SCHEMA = { }, "boot_priority": { "description": "QEMU boot priority", - "enum": ["c", "d"] + "enum": ["c", "d", "n", "cn", "cd"] }, "ram": { "description": "amount of RAM in MB", @@ -468,7 +468,7 @@ QEMU_OBJECT_SCHEMA = { }, "boot_priority": { "description": "QEMU boot priority", - "enum": ["c", "d"] + "enum": ["c", "d", "n", "cn", "cd"] }, "vm_directory": { "decription": "Path to the VM working directory", diff --git a/gns3server/server.py b/gns3server/server.py index a0155b2e..c7474c81 100644 --- a/gns3server/server.py +++ b/gns3server/server.py @@ -226,6 +226,9 @@ class Server: # Asyncio will raise error if coroutine is not called self._loop.set_debug(True) + for key, val in os.environ.items(): + log.debug("ENV %s=%s", key, val) + app = aiohttp.web.Application() for method, route, handler in Route.get_routes(): log.debug("Adding route: {} {}".format(method, route)) diff --git a/tests/modules/qemu/test_qemu_vm.py b/tests/modules/qemu/test_qemu_vm.py index ca1b3b2b..18288aee 100644 --- a/tests/modules/qemu/test_qemu_vm.py +++ b/tests/modules/qemu/test_qemu_vm.py @@ -258,6 +258,18 @@ def test_set_qemu_path_windows(vm, tmpdir): assert vm.platform == "x86_64" +def test_set_qemu_path_old_windows(vm, tmpdir): + + bin_path = os.path.join(os.environ["PATH"], "qemu.exe") + open(bin_path, "w+").close() + os.chmod(bin_path, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR) + + vm.qemu_path = bin_path + + assert vm.qemu_path == bin_path + assert vm.platform == "i386" + + @pytest.mark.skipif(sys.platform.startswith("win"), reason="Not supported on Windows") def test_set_qemu_path_kvm_binary(vm, tmpdir, fake_qemu_binary):