Support Qemu with HAXM acceleration. Ref #1242.

This commit is contained in:
grossmj 2018-03-21 16:41:25 +07:00
parent 85dcb985eb
commit 44f2acffa5
4 changed files with 105 additions and 29 deletions

View File

@ -69,7 +69,11 @@ iourc_path = /home/gns3/.iourc
license_check = True license_check = True
[Qemu] [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 enable_kvm = True
; Require KVM to be installed in order to start VMs ; Require KVM to be installed in order to start VMs (Linux only, has priority over require_hardware_acceleration)
require_kvm = True 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

View File

@ -217,6 +217,34 @@ class Qemu(BaseManager):
except subprocess.SubprocessError as e: except subprocess.SubprocessError as e:
raise QemuError("Error while looking for the Qemu-img version: {}".format(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 @staticmethod
def get_legacy_vm_workdir(legacy_vm_id, name): def get_legacy_vm_workdir(legacy_vm_id, name):
""" """

View File

@ -711,10 +711,15 @@ class QemuVM(BaseNode):
options = options.replace("-no-kvm", "") options = options.replace("-no-kvm", "")
if "-enable-kvm" in options: if "-enable-kvm" in options:
options = options.replace("-enable-kvm", "") options = options.replace("-enable-kvm", "")
elif "-icount" in options and ("-no-kvm" not in options): else:
# automatically add the -no-kvm option if -icount is detected if "-no-hax" in options:
# to help with the migration of ASA VMs created before version 1.4 options = options.replace("-no-hax", "")
options = "-no-kvm " + options 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() self._options = options.strip()
@property @property
@ -947,7 +952,7 @@ class QemuVM(BaseNode):
if self._cpu_throttling: if self._cpu_throttling:
self._set_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 self._hw_virtualization = True
yield from self._start_ubridge() yield from self._start_ubridge()
@ -1642,27 +1647,57 @@ class QemuVM(BaseNode):
return ["-nographic"] return ["-nographic"]
return [] 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 qemu_path: Path to qemu
:param options: String of qemu user options :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) \ enable_hardware_accel = self.manager.config.get_section_config("Qemu").getboolean("enable_hardware_acceleration", True)
and "-no-kvm" not in options: 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 enable_hardware_accel and "-no-kvm" not in options and "-no-hax" not in options:
if os.path.basename(qemu_path) not in ["qemu-system-x86_64", "qemu-system-i386", "qemu-kvm"]: # Turn OFF hardware acceleration for non x86 architectures
return False supported_archs = ["qemu-system-x86_64", "qemu-system-i386", "qemu-kvm"]
if os.path.basename(qemu_path) not in supported_archs:
if not os.path.exists("/dev/kvm"): if require_hardware_accel:
if self.manager.config.get_section_config("Qemu").getboolean("require_kvm", True): raise QemuError("Hardware acceleration can only be used with the following Qemu executables: {}".format(", ".join(supported_archs)))
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.")
else: else:
return False 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 True
return False return False
@ -1682,13 +1717,16 @@ class QemuVM(BaseNode):
command.extend(["-name", self._name]) command.extend(["-name", self._name])
command.extend(["-m", "{}M".format(self._ram)]) command.extend(["-m", "{}M".format(self._ram)])
command.extend(["-smp", "cpus={}".format(self._cpus)]) command.extend(["-smp", "cpus={}".format(self._cpus)])
if self._run_with_kvm(self.qemu_path, self._options): if self._run_with_hardware_acceleration(self.qemu_path, self._options):
command.extend(["-enable-kvm"]) if sys.platform.startswith("linux"):
version = yield from self.manager.get_qemu_version(self.qemu_path) command.extend(["-enable-kvm"])
# Issue on some combo Intel CPU + KVM + Qemu 2.4.0 version = yield from self.manager.get_qemu_version(self.qemu_path)
# https://github.com/GNS3/gns3-server/issues/685 # Issue on some combo Intel CPU + KVM + Qemu 2.4.0
if version and parse_version(version) >= parse_version("2.4.0") and self.platform == "x86_64": # https://github.com/GNS3/gns3-server/issues/685
command.extend(["-machine", "smm=off"]) 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(["-boot", "order={}".format(self._boot_priority)])
command.extend(self._bios_option()) command.extend(self._bios_option())
command.extend(self._cdrom_option()) command.extend(self._cdrom_option())

View File

@ -180,10 +180,16 @@ class QEMUHandler:
qemu_manager = Qemu.instance() qemu_manager = Qemu.instance()
vm = qemu_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"]) 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() pm = ProjectManager.instance()
if pm.check_hardware_virtualization(vm) is False: 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() yield from vm.start()
response.json(vm) response.json(vm)