mirror of
https://github.com/GNS3/gns3-server.git
synced 2025-02-07 08:43:48 +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
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import sys
|
||||||
from aiohttp.web import HTTPConflict
|
from aiohttp.web import HTTPConflict
|
||||||
from ...web.route import Route
|
from ...web.route import Route
|
||||||
|
from ...modules.project_manager import ProjectManager
|
||||||
from ...schemas.nio import NIO_SCHEMA
|
from ...schemas.nio import NIO_SCHEMA
|
||||||
from ...schemas.qemu import QEMU_CREATE_SCHEMA
|
from ...schemas.qemu import QEMU_CREATE_SCHEMA
|
||||||
from ...schemas.qemu import QEMU_UPDATE_SCHEMA
|
from ...schemas.qemu import QEMU_UPDATE_SCHEMA
|
||||||
@ -146,6 +148,11 @@ class QEMUHandler:
|
|||||||
|
|
||||||
qemu_manager = Qemu.instance()
|
qemu_manager = Qemu.instance()
|
||||||
vm = qemu_manager.get_vm(request.match_info["vm_id"], project_id=request.match_info["project_id"])
|
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()
|
yield from vm.start()
|
||||||
response.set_status(204)
|
response.set_status(204)
|
||||||
|
|
||||||
|
@ -16,6 +16,8 @@
|
|||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
from aiohttp.web import HTTPConflict
|
from aiohttp.web import HTTPConflict
|
||||||
from ...web.route import Route
|
from ...web.route import Route
|
||||||
from ...schemas.nio import NIO_SCHEMA
|
from ...schemas.nio import NIO_SCHEMA
|
||||||
@ -190,6 +192,10 @@ class VirtualBoxHandler:
|
|||||||
|
|
||||||
vbox_manager = VirtualBox.instance()
|
vbox_manager = VirtualBox.instance()
|
||||||
vm = vbox_manager.get_vm(request.match_info["vm_id"], project_id=request.match_info["project_id"])
|
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()
|
yield from vm.start()
|
||||||
response.set_status(204)
|
response.set_status(204)
|
||||||
|
|
||||||
|
@ -372,7 +372,7 @@ class BaseManager:
|
|||||||
elif nio_settings["type"] == "nio_tap":
|
elif nio_settings["type"] == "nio_tap":
|
||||||
tap_device = nio_settings["tap_device"]
|
tap_device = nio_settings["tap_device"]
|
||||||
if not is_interface_up(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
|
# FIXME: check for permissions on tap device
|
||||||
# if not self._has_privileged_access(executable):
|
# if not self._has_privileged_access(executable):
|
||||||
# raise aiohttp.web.HTTPForbidden(text="{} has no privileged access to {}.".format(executable, tap_device))
|
# 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":
|
elif nio_settings["type"] == "nio_generic_ethernet":
|
||||||
ethernet_device = nio_settings["ethernet_device"]
|
ethernet_device = nio_settings["ethernet_device"]
|
||||||
if not is_interface_up(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)
|
nio = NIOGenericEthernet(ethernet_device)
|
||||||
elif nio_settings["type"] == "nio_nat":
|
elif nio_settings["type"] == "nio_nat":
|
||||||
nio = NIONAT()
|
nio = NIONAT()
|
||||||
|
@ -49,6 +49,7 @@ class BaseVM:
|
|||||||
self._console = console
|
self._console = console
|
||||||
self._console_type = console_type
|
self._console_type = console_type
|
||||||
self._temporary_directory = None
|
self._temporary_directory = None
|
||||||
|
self._hw_virtualization = False
|
||||||
self._vm_status = "stopped"
|
self._vm_status = "stopped"
|
||||||
|
|
||||||
if self._console is not None:
|
if self._console is not None:
|
||||||
@ -262,3 +263,13 @@ class BaseVM:
|
|||||||
name=self.name,
|
name=self.name,
|
||||||
id=self.id,
|
id=self.id,
|
||||||
console_type=console_type))
|
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:
|
if project_id not in self._projects:
|
||||||
raise aiohttp.web.HTTPNotFound(text="Project ID {} doesn't exist".format(project_id))
|
raise aiohttp.web.HTTPNotFound(text="Project ID {} doesn't exist".format(project_id))
|
||||||
del self._projects[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 = ""
|
self._stdout_file = ""
|
||||||
|
|
||||||
# QEMU VM settings
|
# QEMU VM settings
|
||||||
|
|
||||||
if qemu_path:
|
if qemu_path:
|
||||||
try:
|
try:
|
||||||
self.qemu_path = qemu_path
|
self.qemu_path = qemu_path
|
||||||
@ -483,8 +482,11 @@ class QemuVM(BaseVM):
|
|||||||
id=self._id,
|
id=self._id,
|
||||||
options=options))
|
options=options))
|
||||||
|
|
||||||
if not sys.platform.startswith("linux") and "-no-kvm" in options:
|
if not sys.platform.startswith("linux"):
|
||||||
options = options.replace("-no-kvm")
|
if "-no-kvm" in options:
|
||||||
|
options = options.replace("-no-kvm")
|
||||||
|
if "-enable-kvm" in options:
|
||||||
|
options = options.replace("-enable-kvm")
|
||||||
self._options = options.strip()
|
self._options = options.strip()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -696,6 +698,9 @@ class QemuVM(BaseVM):
|
|||||||
if self._cpu_throttling:
|
if self._cpu_throttling:
|
||||||
self._set_cpu_throttling()
|
self._set_cpu_throttling()
|
||||||
|
|
||||||
|
if "-enable-kvm" in command_string:
|
||||||
|
self._hw_virtualization = True
|
||||||
|
|
||||||
def _termination_callback(self, returncode):
|
def _termination_callback(self, returncode):
|
||||||
"""
|
"""
|
||||||
Called when the process has stopped.
|
Called when the process has stopped.
|
||||||
@ -706,6 +711,7 @@ class QemuVM(BaseVM):
|
|||||||
if self.started:
|
if self.started:
|
||||||
log.info("QEMU process has stopped, return code: %d", returncode)
|
log.info("QEMU process has stopped, return code: %d", returncode)
|
||||||
self.status = "stopped"
|
self.status = "stopped"
|
||||||
|
self._hw_virtualization = False
|
||||||
self._process = None
|
self._process = None
|
||||||
if returncode != 0:
|
if returncode != 0:
|
||||||
self.project.emit("log.error", {"message": "QEMU process has stopped, return code: {}\n{}".format(returncode, self.read_stdout())})
|
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
|
# stop the QEMU process
|
||||||
|
self._hw_virtualization = False
|
||||||
if self.is_running():
|
if self.is_running():
|
||||||
log.info('Stopping QEMU VM "{}" PID={}'.format(self._name, self._process.pid))
|
log.info('Stopping QEMU VM "{}" PID={}'.format(self._name, self._process.pid))
|
||||||
try:
|
try:
|
||||||
|
@ -169,6 +169,19 @@ class VirtualBoxVM(BaseVM):
|
|||||||
if "memory" in vm_info:
|
if "memory" in vm_info:
|
||||||
self._ram = int(vm_info["memory"])
|
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
|
@asyncio.coroutine
|
||||||
def start(self):
|
def start(self):
|
||||||
"""
|
"""
|
||||||
@ -203,12 +216,16 @@ class VirtualBoxVM(BaseVM):
|
|||||||
if self._enable_remote_console and self._console is not None:
|
if self._enable_remote_console and self._console is not None:
|
||||||
self._start_remote_console()
|
self._start_remote_console()
|
||||||
|
|
||||||
|
if (yield from self.check_hw_virtualization()):
|
||||||
|
self._hw_virtualization = True
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def stop(self):
|
def stop(self):
|
||||||
"""
|
"""
|
||||||
Stops this VirtualBox VM.
|
Stops this VirtualBox VM.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
self._hw_virtualization = False
|
||||||
self._stop_remote_console()
|
self._stop_remote_console()
|
||||||
vm_state = yield from self._get_vm_state()
|
vm_state = yield from self._get_vm_state()
|
||||||
if vm_state == "running" or vm_state == "paused" or vm_state == "stuck":
|
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 pkg_resources import parse_version
|
||||||
from gns3server.ubridge.hypervisor import Hypervisor
|
from gns3server.ubridge.hypervisor import Hypervisor
|
||||||
from gns3server.ubridge.ubridge_error import UbridgeError
|
|
||||||
from gns3server.utils.telnet_server import TelnetServer
|
from gns3server.utils.telnet_server import TelnetServer
|
||||||
from gns3server.utils.interfaces import get_windows_interfaces
|
from gns3server.utils.interfaces import get_windows_interfaces
|
||||||
from collections import OrderedDict
|
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.
|
yield from asyncio.sleep(1) # give some time to VMware to create the pipe file.
|
||||||
self._start_remote_console()
|
self._start_remote_console()
|
||||||
|
|
||||||
|
if self._get_vmx_setting("vhv.enable", "TRUE"):
|
||||||
|
self._hw_virtualization = True
|
||||||
|
|
||||||
self._started = True
|
self._started = True
|
||||||
log.info("VMware VM '{name}' [{id}] started".format(name=self.name, id=self.id))
|
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.
|
Stops this VMware VM.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
self._hw_virtualization = False
|
||||||
self._stop_remote_console()
|
self._stop_remote_console()
|
||||||
if self._ubridge_hypervisor and self._ubridge_hypervisor.is_running():
|
if self._ubridge_hypervisor and self._ubridge_hypervisor.is_running():
|
||||||
yield from self._ubridge_hypervisor.stop()
|
yield from self._ubridge_hypervisor.stop()
|
||||||
|
Loading…
Reference in New Issue
Block a user