Prevent using different hypervisors that leverage hardware virtualization.

- Implemented for Qemu when a VMware or VirtualBox VM with hardware virtualization is already running.
- Implemented for VirtualBox only when a Qemu VM with KVM is already running.
pull/370/head
Jeremy 9 years ago
parent accaa2159b
commit 8e236a7045

@ -15,8 +15,10 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
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)

@ -16,6 +16,8 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
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)

@ -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()

@ -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

@ -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

@ -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:

@ -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":

@ -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()

Loading…
Cancel
Save