mirror of
https://github.com/GNS3/gns3-server.git
synced 2025-01-31 05:13:49 +02:00
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.
This commit is contained in:
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…
Reference in New Issue
Block a user