diff --git a/gns3server/handlers/api/dynamips_vm_handler.py b/gns3server/handlers/api/dynamips_vm_handler.py index bd131e18..bf151e41 100644 --- a/gns3server/handlers/api/dynamips_vm_handler.py +++ b/gns3server/handlers/api/dynamips_vm_handler.py @@ -16,6 +16,7 @@ # along with this program. If not, see . import os +import sys import base64 from ...web.route import Route @@ -320,6 +321,14 @@ class DynamipsVMHandler: slot_number = int(request.match_info["adapter_number"]) port_number = int(request.match_info["port_number"]) pcap_file_path = os.path.join(vm.project.capture_working_directory(), request.json["capture_file_name"]) + + if sys.platform.startswith('win'): + #FIXME: Dynamips (Cygwin actually) doesn't like non ascii paths on Windows + try: + pcap_file_path.encode('ascii') + except UnicodeEncodeError: + raise DynamipsError('The capture file path "{}" must only contain ASCII (English) characters'.format(pcap_file_path)) + yield from vm.start_capture(slot_number, port_number, pcap_file_path, request.json["data_link_type"]) response.json({"pcap_file_path": pcap_file_path}) diff --git a/gns3server/modules/base_manager.py b/gns3server/modules/base_manager.py index e8a68b16..fc2b74c4 100644 --- a/gns3server/modules/base_manager.py +++ b/gns3server/modules/base_manager.py @@ -240,7 +240,7 @@ class BaseManager: @asyncio.coroutine def close_vm(self, vm_id): """ - Delete a VM + Close a VM :param vm_id: VM identifier @@ -308,7 +308,8 @@ class BaseManager: vm = yield from self.close_vm(vm_id) vm.project.mark_vm_for_destruction(vm) - del self._vms[vm.id] + if vm.id in self._vms: + del self._vms[vm.id] return vm @staticmethod diff --git a/gns3server/modules/dynamips/__init__.py b/gns3server/modules/dynamips/__init__.py index 28406dfa..946b8c57 100644 --- a/gns3server/modules/dynamips/__init__.py +++ b/gns3server/modules/dynamips/__init__.py @@ -357,6 +357,17 @@ class Dynamips(BaseManager): return hypervisor + @asyncio.coroutine + def ghost_ios_support(self, vm): + + ghost_ios_support = self.config.get_section_config("Dynamips").getboolean("ghost_ios_support", True) + if ghost_ios_support: + with (yield from Dynamips._ghost_ios_lock): + try: + yield from self._set_ghost_ios(vm) + except GeneratorExit: + log.warning("Could not create ghost IOS image {} (GeneratorExit)".format(vm.name)) + @asyncio.coroutine def create_nio(self, node, nio_settings): """ diff --git a/gns3server/modules/qemu/__init__.py b/gns3server/modules/qemu/__init__.py index 13cc8bb7..a0666444 100644 --- a/gns3server/modules/qemu/__init__.py +++ b/gns3server/modules/qemu/__init__.py @@ -97,7 +97,7 @@ class Qemu(BaseManager): os.access(os.path.join(path, f), os.X_OK) and \ os.path.isfile(os.path.join(path, f)): qemu_path = os.path.join(path, f) - version = yield from Qemu._get_qemu_version(qemu_path) + version = yield from Qemu.get_qemu_version(qemu_path) qemus.append({"path": qemu_path, "version": version}) except OSError: continue @@ -128,7 +128,7 @@ class Qemu(BaseManager): @staticmethod @asyncio.coroutine - def _get_qemu_version(qemu_path): + def get_qemu_version(qemu_path): """ Gets the Qemu version. @@ -136,17 +136,30 @@ class Qemu(BaseManager): """ if sys.platform.startswith("win"): + # Qemu on Windows doesn't return anything with parameter -version + # look for a version number in version.txt file in the same directory instead + version_file = os.path.join(os.path.dirname(qemu_path), "version.txt") + if os.path.isfile(version_file): + try: + with open(version_file, "rb") as file: + version = file.read().decode("utf-8").strip() + match = re.search("[0-9\.]+", version) + if match: + return version + except (UnicodeDecodeError, OSError) as e: + log.warn("could not read {}: {}".format(version_file, e)) return "" - try: - output = yield from subprocess_check_output(qemu_path, "-version") - match = re.search("version\s+([0-9a-z\-\.]+)", output) - if match: - version = match.group(1) - return version - else: - raise QemuError("Could not determine the Qemu version for {}".format(qemu_path)) - except subprocess.SubprocessError as e: - raise QemuError("Error while looking for the Qemu version: {}".format(e)) + else: + try: + output = yield from subprocess_check_output(qemu_path, "-version") + match = re.search("version\s+([0-9a-z\-\.]+)", output) + if match: + version = match.group(1) + return version + else: + raise QemuError("Could not determine the Qemu version for {}".format(qemu_path)) + except subprocess.SubprocessError as e: + raise QemuError("Error while looking for the Qemu version: {}".format(e)) @staticmethod @asyncio.coroutine diff --git a/gns3server/modules/qemu/qemu_vm.py b/gns3server/modules/qemu/qemu_vm.py index 19441d2d..43c35628 100644 --- a/gns3server/modules/qemu/qemu_vm.py +++ b/gns3server/modules/qemu/qemu_vm.py @@ -30,6 +30,7 @@ import asyncio import socket import gns3server +from pkg_resources import parse_version from .qemu_error import QemuError from ..adapters.ethernet_adapter import EthernetAdapter from ..nios.nio_udp import NIOUDP @@ -1317,10 +1318,19 @@ class QemuVM(BaseVM): return options + @asyncio.coroutine def _network_options(self): network_options = [] network_options.extend(["-net", "none"]) # we do not want any user networking back-end if no adapter is connected. + + patched_qemu = False + if self._legacy_networking: + version = yield from self.manager.get_qemu_version(self.qemu_path) + if version and parse_version(version) < parse_version("1.1.0"): + # this is a patched Qemu if version is below 1.1.0 + patched_qemu = True + for adapter_number, adapter in enumerate(self._ethernet_adapters): mac = "%s%02x" % (self._mac_address[:-2], (int(self._mac_address[-2:]) + adapter_number) % 255) nio = adapter.get_nio(0) @@ -1329,11 +1339,21 @@ class QemuVM(BaseVM): if nio: network_options.extend(["-net", "nic,vlan={},macaddr={},model={}".format(adapter_number, mac, self._adapter_type)]) if isinstance(nio, NIOUDP): - network_options.extend(["-net", "udp,vlan={},name=gns3-{},sport={},dport={},daddr={}".format(adapter_number, - adapter_number, - nio.lport, - nio.rport, - nio.rhost)]) + if patched_qemu: + # use patched Qemu syntax + network_options.extend(["-net", "udp,vlan={},name=gns3-{},sport={},dport={},daddr={}".format(adapter_number, + adapter_number, + nio.lport, + nio.rport, + nio.rhost)]) + else: + # use UDP tunnel support added in Qemu 1.1.0 + network_options.extend(["-net", "socket,vlan={},name=gns3-{},udp={}:{},localaddr={}:{}".format(adapter_number, + adapter_number, + nio.rhost, + nio.rport, + self._host, + nio.lport)]) elif isinstance(nio, NIOTAP): network_options.extend(["-net", "tap,name=gns3-{},ifname={}".format(adapter_number, nio.tap_device)]) elif isinstance(nio, NIONAT): @@ -1407,9 +1427,9 @@ class QemuVM(BaseVM): command.extend(["-enable-kvm"]) command.extend(["-boot", "order={}".format(self._boot_priority)]) disk_options = yield from self._disk_options() - command.extend(disk_options) cdrom_option = self._cdrom_option() command.extend(cdrom_option) + command.extend((yield from self._disk_options())) command.extend(self._linux_boot_options()) if self._console_type == "telnet": command.extend(self._serial_options()) diff --git a/gns3server/modules/virtualbox/virtualbox_vm.py b/gns3server/modules/virtualbox/virtualbox_vm.py index 4d93995a..eeff2277 100644 --- a/gns3server/modules/virtualbox/virtualbox_vm.py +++ b/gns3server/modules/virtualbox/virtualbox_vm.py @@ -308,7 +308,7 @@ class VirtualBoxVM(BaseVM): try: with open(hdd_info_file, "r", encoding="utf-8") as f: hdd_table = json.load(f) - except OSError as e: + except (ValueError, OSError) as e: raise VirtualBoxError("Could not read HDD info file: {}".format(e)) for hdd_info in hdd_table: diff --git a/tests/modules/qemu/test_qemu_manager.py b/tests/modules/qemu/test_qemu_manager.py index fef5f9ee..36ede5bb 100644 --- a/tests/modules/qemu/test_qemu_manager.py +++ b/tests/modules/qemu/test_qemu_manager.py @@ -40,7 +40,7 @@ def fake_qemu_img_binary(tmpdir): def test_get_qemu_version(loop): with asyncio_patch("gns3server.modules.qemu.subprocess_check_output", return_value="QEMU emulator version 2.2.0, Copyright (c) 2003-2008 Fabrice Bellard") as mock: - version = loop.run_until_complete(asyncio.async(Qemu._get_qemu_version("/tmp/qemu-test"))) + version = loop.run_until_complete(asyncio.async(Qemu.get_qemu_version("/tmp/qemu-test"))) if sys.platform.startswith("win"): assert version == "" else: