From 44f2acffa516a752b4198bf2b74660711dce8da8 Mon Sep 17 00:00:00 2001 From: grossmj Date: Wed, 21 Mar 2018 16:41:25 +0700 Subject: [PATCH] Support Qemu with HAXM acceleration. Ref #1242. --- conf/gns3_server.conf | 10 ++- gns3server/compute/qemu/__init__.py | 28 ++++++ gns3server/compute/qemu/qemu_vm.py | 86 +++++++++++++------ .../handlers/api/compute/qemu_handler.py | 10 ++- 4 files changed, 105 insertions(+), 29 deletions(-) diff --git a/conf/gns3_server.conf b/conf/gns3_server.conf index 5e36fb31..7d60a982 100644 --- a/conf/gns3_server.conf +++ b/conf/gns3_server.conf @@ -69,7 +69,11 @@ iourc_path = /home/gns3/.iourc license_check = True [Qemu] -; !! Remember to add the gns3 user to the KVM group, otherwise you will not have read / write permssions to /dev/kvm !! +; !! Remember to add the gns3 user to the KVM group, otherwise you will not have read / write permissions to /dev/kvm !! (Linux only, has priority over enable_hardware_acceleration) enable_kvm = True -; Require KVM to be installed in order to start VMs -require_kvm = True \ No newline at end of file +; Require KVM to be installed in order to start VMs (Linux only, has priority over require_hardware_acceleration) +require_kvm = True +; Enable hardware acceleration (all platforms) +enable_hardware_acceleration = True +; Require hardware acceleration in order to start VMs (all platforms) +require_hardware_acceleration = True diff --git a/gns3server/compute/qemu/__init__.py b/gns3server/compute/qemu/__init__.py index 749ccf99..47b11dd0 100644 --- a/gns3server/compute/qemu/__init__.py +++ b/gns3server/compute/qemu/__init__.py @@ -217,6 +217,34 @@ class Qemu(BaseManager): except subprocess.SubprocessError as e: raise QemuError("Error while looking for the Qemu-img version: {}".format(e)) + @staticmethod + def get_haxm_windows_version(): + """ + Gets the HAXM version number (Windows). + + :returns: HAXM version number. Returns None if HAXM is not installed. + """ + + assert(sys.platform.startswith("win")) + import winreg + + hkey = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, r"SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Products") + version = None + for index in range(winreg.QueryInfoKey(hkey)[0]): + product_id = winreg.EnumKey(hkey, index) + try: + product_key = winreg.OpenKey(hkey, r"{}\InstallProperties".format(product_id)) + try: + if winreg.QueryValueEx(product_key, "DisplayName")[0].endswith("Hardware Accelerated Execution Manager"): + version = winreg.QueryValueEx(product_key, "DisplayVersion")[0] + break + finally: + winreg.CloseKey(product_key) + except OSError: + continue + winreg.CloseKey(hkey) + return version + @staticmethod def get_legacy_vm_workdir(legacy_vm_id, name): """ diff --git a/gns3server/compute/qemu/qemu_vm.py b/gns3server/compute/qemu/qemu_vm.py index 983171c5..db3ea996 100644 --- a/gns3server/compute/qemu/qemu_vm.py +++ b/gns3server/compute/qemu/qemu_vm.py @@ -711,10 +711,15 @@ class QemuVM(BaseNode): options = options.replace("-no-kvm", "") if "-enable-kvm" in options: options = options.replace("-enable-kvm", "") - elif "-icount" in options and ("-no-kvm" not in options): - # automatically add the -no-kvm option if -icount is detected - # to help with the migration of ASA VMs created before version 1.4 - options = "-no-kvm " + options + else: + if "-no-hax" in options: + options = options.replace("-no-hax", "") + if "-enable-hax" in options: + options = options.replace("-enable-hax", "") + if "-icount" in options and ("-no-kvm" not in options): + # automatically add the -no-kvm option if -icount is detected + # to help with the migration of ASA VMs created before version 1.4 + options = "-no-kvm " + options self._options = options.strip() @property @@ -947,7 +952,7 @@ class QemuVM(BaseNode): if self._cpu_throttling: self._set_cpu_throttling() - if "-enable-kvm" in command_string: + if "-enable-kvm" in command_string or "-enable-hax" in command_string: self._hw_virtualization = True yield from self._start_ubridge() @@ -1642,27 +1647,57 @@ class QemuVM(BaseNode): return ["-nographic"] return [] - def _run_with_kvm(self, qemu_path, options): + def _run_with_hardware_acceleration(self, qemu_path, options): """ - Check if we could run qemu with KVM + Check if we can run Qemu with hardware acceleration :param qemu_path: Path to qemu :param options: String of qemu user options - :returns: Boolean True if we need to enable KVM + :returns: Boolean True if we need to enable hardware acceleration """ - if sys.platform.startswith("linux") and self.manager.config.get_section_config("Qemu").getboolean("enable_kvm", True) \ - and "-no-kvm" not in options: + enable_hardware_accel = self.manager.config.get_section_config("Qemu").getboolean("enable_hardware_acceleration", True) + require_hardware_accel = self.manager.config.get_section_config("Qemu").getboolean("require_hardware_acceleration", True) + if sys.platform.startswith("linux"): + # compatibility: these options were used before version 2.0 and have priority + enable_kvm = self.manager.config.get_section_config("Qemu").getboolean("enable_kvm") + if enable_kvm is not None: + enable_hardware_accel = enable_kvm + require_kvm = self.manager.config.get_section_config("Qemu").getboolean("require_kvm") + if require_kvm is not None: + require_hardware_accel = require_kvm - # Turn OFF kvm for non x86 architectures - if os.path.basename(qemu_path) not in ["qemu-system-x86_64", "qemu-system-i386", "qemu-kvm"]: - return False - - if not os.path.exists("/dev/kvm"): - if self.manager.config.get_section_config("Qemu").getboolean("require_kvm", True): - raise QemuError("KVM acceleration cannot be used (/dev/kvm doesn't exist). You can turn off KVM support in the gns3_server.conf by adding enable_kvm = false to the [Qemu] section.") + if enable_hardware_accel and "-no-kvm" not in options and "-no-hax" not in options: + # Turn OFF hardware acceleration for non x86 architectures + supported_archs = ["qemu-system-x86_64", "qemu-system-i386", "qemu-kvm"] + if os.path.basename(qemu_path) not in supported_archs: + if require_hardware_accel: + raise QemuError("Hardware acceleration can only be used with the following Qemu executables: {}".format(", ".join(supported_archs))) else: return False + + if sys.platform.startswith("linux") and not os.path.exists("/dev/kvm"): + if require_hardware_accel: + raise QemuError("KVM acceleration cannot be used (/dev/kvm doesn't exist). It is possible to turn off KVM support in the gns3_server.conf by adding enable_kvm = false to the [Qemu] section.") + else: + return False + elif sys.platform.startswith("win"): + if require_hardware_accel: + # HAXM is only available starting with Qemu version 2.9.0 + version = yield from self.manager.get_qemu_version(self.qemu_path) + if version and parse_version(version) < parse_version("2.9.0"): + raise QemuError("HAXM acceleration can only be enable for Qemu version 2.9.0 and above (current version: {})".format(version)) + + # check if HAXM is installed + version = self.manager.get_haxm_windows_version() + if not version: + raise QemuError("HAXM acceleration support is not installed on this host") + log.info("HAXM support version {} detected".format(version)) + else: + return False + elif sys.platform.startswith("darwin"): + # TODO: support for macOS + raise QemuError("HAXM acceleration is not yet supported on macOS") return True return False @@ -1682,13 +1717,16 @@ class QemuVM(BaseNode): command.extend(["-name", self._name]) command.extend(["-m", "{}M".format(self._ram)]) command.extend(["-smp", "cpus={}".format(self._cpus)]) - if self._run_with_kvm(self.qemu_path, self._options): - command.extend(["-enable-kvm"]) - version = yield from self.manager.get_qemu_version(self.qemu_path) - # Issue on some combo Intel CPU + KVM + Qemu 2.4.0 - # https://github.com/GNS3/gns3-server/issues/685 - if version and parse_version(version) >= parse_version("2.4.0") and self.platform == "x86_64": - command.extend(["-machine", "smm=off"]) + if self._run_with_hardware_acceleration(self.qemu_path, self._options): + if sys.platform.startswith("linux"): + command.extend(["-enable-kvm"]) + version = yield from self.manager.get_qemu_version(self.qemu_path) + # Issue on some combo Intel CPU + KVM + Qemu 2.4.0 + # https://github.com/GNS3/gns3-server/issues/685 + if version and parse_version(version) >= parse_version("2.4.0") and self.platform == "x86_64": + command.extend(["-machine", "smm=off"]) + elif sys.platform.startswith("win") or sys.platform.startswith("darwin"): + command.extend(["-enable-hax"]) command.extend(["-boot", "order={}".format(self._boot_priority)]) command.extend(self._bios_option()) command.extend(self._cdrom_option()) diff --git a/gns3server/handlers/api/compute/qemu_handler.py b/gns3server/handlers/api/compute/qemu_handler.py index 12db4596..c68c5812 100644 --- a/gns3server/handlers/api/compute/qemu_handler.py +++ b/gns3server/handlers/api/compute/qemu_handler.py @@ -180,10 +180,16 @@ class QEMUHandler: qemu_manager = Qemu.instance() vm = qemu_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"]) - if sys.platform.startswith("linux") and qemu_manager.config.get_section_config("Qemu").getboolean("enable_kvm", True) and "-no-kvm" not in vm.options: + hardware_accel = qemu_manager.config.get_section_config("Qemu").getboolean("enable_hardware_acceleration", True) + if sys.platform.startswith("linux"): + # the enable_kvm option was used before version 2.0 and has priority + enable_kvm = qemu_manager.config.get_section_config("Qemu").getboolean("enable_kvm") + if enable_kvm is not None: + hardware_accel = enable_kvm + if hardware_accel and "-no-kvm" not in vm.options and "-no-hax" not in vm.options: pm = ProjectManager.instance() if pm.check_hardware_virtualization(vm) is False: - raise aiohttp.web.HTTPConflict(text="Cannot start VM with KVM enabled because hardware virtualization (VT-x/AMD-V) is already used by another software like VMware or VirtualBox") + raise aiohttp.web.HTTPConflict(text="Cannot start VM with hardware acceleration (KVM/HAX) enabled because hardware virtualization (VT-x/AMD-V) is already used by another software like VMware or VirtualBox") yield from vm.start() response.json(vm)