diff --git a/gns3server/handlers/api/qemu_handler.py b/gns3server/handlers/api/qemu_handler.py index 3be59314..9a8cff10 100644 --- a/gns3server/handlers/api/qemu_handler.py +++ b/gns3server/handlers/api/qemu_handler.py @@ -15,8 +15,10 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +import sys from aiohttp.web import HTTPConflict from ...web.route import Route +from ...modules.project_manager import ProjectManager from ...schemas.nio import NIO_SCHEMA from ...schemas.qemu import QEMU_CREATE_SCHEMA from ...schemas.qemu import QEMU_UPDATE_SCHEMA @@ -146,6 +148,11 @@ class QEMUHandler: qemu_manager = Qemu.instance() vm = qemu_manager.get_vm(request.match_info["vm_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: + pm = ProjectManager.instance() + if pm.check_hardware_virtualization(vm) is False: + raise HTTPConflict(text="Cannot start VM with KVM enabled because hardware virtualization is already used by another software like VMware or VirtualBox") yield from vm.start() response.set_status(204) diff --git a/gns3server/handlers/api/virtualbox_handler.py b/gns3server/handlers/api/virtualbox_handler.py index 5036bef7..a66e472d 100644 --- a/gns3server/handlers/api/virtualbox_handler.py +++ b/gns3server/handlers/api/virtualbox_handler.py @@ -16,6 +16,8 @@ # along with this program. If not, see . import os +import sys + from aiohttp.web import HTTPConflict from ...web.route import Route from ...schemas.nio import NIO_SCHEMA @@ -190,6 +192,10 @@ class VirtualBoxHandler: vbox_manager = VirtualBox.instance() vm = vbox_manager.get_vm(request.match_info["vm_id"], project_id=request.match_info["project_id"]) + if sys.platform.startswith("linux") and (yield from vm.check_hw_virtualization()): + pm = ProjectManager.instance() + if pm.check_hardware_virtualization(vm) is False: + raise HTTPConflict(text="Cannot start VM because KVM is being used by a Qemu VM") yield from vm.start() response.set_status(204) diff --git a/gns3server/modules/base_manager.py b/gns3server/modules/base_manager.py index ebf6d0d3..37478b43 100644 --- a/gns3server/modules/base_manager.py +++ b/gns3server/modules/base_manager.py @@ -372,7 +372,7 @@ class BaseManager: elif nio_settings["type"] == "nio_tap": tap_device = nio_settings["tap_device"] if not is_interface_up(tap_device): - raise aiohttp.web.HTTPConflict(text="TAP interface {} is down".format(tap_device)) + raise aiohttp.web.HTTPConflict(text="TAP interface {} does not exist or is down".format(tap_device)) # FIXME: check for permissions on tap device # if not self._has_privileged_access(executable): # raise aiohttp.web.HTTPForbidden(text="{} has no privileged access to {}.".format(executable, tap_device)) @@ -380,7 +380,7 @@ class BaseManager: elif nio_settings["type"] == "nio_generic_ethernet": ethernet_device = nio_settings["ethernet_device"] if not is_interface_up(ethernet_device): - raise aiohttp.web.HTTPConflict(text="Ethernet interface {} is down".format(ethernet_device)) + raise aiohttp.web.HTTPConflict(text="Ethernet interface {} does not exist or is down".format(ethernet_device)) nio = NIOGenericEthernet(ethernet_device) elif nio_settings["type"] == "nio_nat": nio = NIONAT() diff --git a/gns3server/modules/base_vm.py b/gns3server/modules/base_vm.py index 19c78d5e..197bb84b 100644 --- a/gns3server/modules/base_vm.py +++ b/gns3server/modules/base_vm.py @@ -49,6 +49,7 @@ class BaseVM: self._console = console self._console_type = console_type self._temporary_directory = None + self._hw_virtualization = False self._vm_status = "stopped" if self._console is not None: @@ -262,3 +263,13 @@ class BaseVM: name=self.name, id=self.id, console_type=console_type)) + + @property + def hw_virtualization(self): + """ + Returns either the VM is using hardware virtualization or not. + + :return: boolean + """ + + return self._hw_virtualization diff --git a/gns3server/modules/project_manager.py b/gns3server/modules/project_manager.py index 4ea21612..6d30bc99 100644 --- a/gns3server/modules/project_manager.py +++ b/gns3server/modules/project_manager.py @@ -95,3 +95,27 @@ class ProjectManager: if project_id not in self._projects: raise aiohttp.web.HTTPNotFound(text="Project ID {} doesn't exist".format(project_id)) del self._projects[project_id] + + def check_hardware_virtualization(self, source_vm): + """ + Checks if hardware virtualization can be used. + + :returns: boolean + """ + + from .qemu import QemuVM + from .virtualbox import VirtualBoxVM + from .vmware import VMwareVM + for project in self._projects.values(): + for vm in project.vms: + if vm == source_vm: + continue + if vm.hw_virtualization: + if isinstance(source_vm, QemuVM) and not isinstance(vm, QemuVM): + # A Qemu VM won't start if any other virtualization software uses hardware virtualization + return False + elif isinstance(source_vm, VirtualBoxVM) and not isinstance(vm, VirtualBoxVM) and not isinstance(vm, VMwareVM): + # A VirtualBox VM won't start if KVM is being used + return False + # VMware doesn't seem to be bothered by any other virtualization software. + return True diff --git a/gns3server/modules/qemu/qemu_vm.py b/gns3server/modules/qemu/qemu_vm.py index 979a28ce..54e54297 100644 --- a/gns3server/modules/qemu/qemu_vm.py +++ b/gns3server/modules/qemu/qemu_vm.py @@ -73,7 +73,6 @@ class QemuVM(BaseVM): self._stdout_file = "" # QEMU VM settings - if qemu_path: try: self.qemu_path = qemu_path @@ -483,8 +482,11 @@ class QemuVM(BaseVM): id=self._id, options=options)) - if not sys.platform.startswith("linux") and "-no-kvm" in options: - options = options.replace("-no-kvm") + if not sys.platform.startswith("linux"): + if "-no-kvm" in options: + options = options.replace("-no-kvm") + if "-enable-kvm" in options: + options = options.replace("-enable-kvm") self._options = options.strip() @property @@ -696,6 +698,9 @@ class QemuVM(BaseVM): if self._cpu_throttling: self._set_cpu_throttling() + if "-enable-kvm" in command_string: + self._hw_virtualization = True + def _termination_callback(self, returncode): """ Called when the process has stopped. @@ -706,6 +711,7 @@ class QemuVM(BaseVM): if self.started: log.info("QEMU process has stopped, return code: %d", returncode) self.status = "stopped" + self._hw_virtualization = False self._process = None if returncode != 0: self.project.emit("log.error", {"message": "QEMU process has stopped, return code: {}\n{}".format(returncode, self.read_stdout())}) @@ -717,6 +723,7 @@ class QemuVM(BaseVM): """ # stop the QEMU process + self._hw_virtualization = False if self.is_running(): log.info('Stopping QEMU VM "{}" PID={}'.format(self._name, self._process.pid)) try: diff --git a/gns3server/modules/virtualbox/virtualbox_vm.py b/gns3server/modules/virtualbox/virtualbox_vm.py index 7c48ccdf..8fd750d7 100644 --- a/gns3server/modules/virtualbox/virtualbox_vm.py +++ b/gns3server/modules/virtualbox/virtualbox_vm.py @@ -169,6 +169,19 @@ class VirtualBoxVM(BaseVM): if "memory" in vm_info: self._ram = int(vm_info["memory"]) + @asyncio.coroutine + def check_hw_virtualization(self): + """ + Returns either hardware virtualization is activated or not. + + :returns: boolean + """ + + vm_info = yield from self._get_vm_info() + if "hwvirtex" in vm_info and vm_info["hwvirtex"] == "on": + return True + return False + @asyncio.coroutine def start(self): """ @@ -203,12 +216,16 @@ class VirtualBoxVM(BaseVM): if self._enable_remote_console and self._console is not None: self._start_remote_console() + if (yield from self.check_hw_virtualization()): + self._hw_virtualization = True + @asyncio.coroutine def stop(self): """ Stops this VirtualBox VM. """ + self._hw_virtualization = False self._stop_remote_console() vm_state = yield from self._get_vm_state() if vm_state == "running" or vm_state == "paused" or vm_state == "stuck": diff --git a/gns3server/modules/vmware/vmware_vm.py b/gns3server/modules/vmware/vmware_vm.py index be1f352f..99705ac1 100644 --- a/gns3server/modules/vmware/vmware_vm.py +++ b/gns3server/modules/vmware/vmware_vm.py @@ -28,7 +28,6 @@ import tempfile from pkg_resources import parse_version from gns3server.ubridge.hypervisor import Hypervisor -from gns3server.ubridge.ubridge_error import UbridgeError from gns3server.utils.telnet_server import TelnetServer from gns3server.utils.interfaces import get_windows_interfaces from collections import OrderedDict @@ -387,6 +386,9 @@ class VMwareVM(BaseVM): yield from asyncio.sleep(1) # give some time to VMware to create the pipe file. self._start_remote_console() + if self._get_vmx_setting("vhv.enable", "TRUE"): + self._hw_virtualization = True + self._started = True log.info("VMware VM '{name}' [{id}] started".format(name=self.name, id=self.id)) @@ -396,6 +398,7 @@ class VMwareVM(BaseVM): Stops this VMware VM. """ + self._hw_virtualization = False self._stop_remote_console() if self._ubridge_hypervisor and self._ubridge_hypervisor.is_running(): yield from self._ubridge_hypervisor.stop()