mirror of
https://github.com/GNS3/gns3-server
synced 2024-11-28 11:18:11 +00:00
VirtualBox support refactoring.
This commit is contained in:
parent
77c583ca39
commit
569a68a486
@ -24,7 +24,6 @@ from ..dynamips_error import DynamipsError
|
||||
from .router import Router
|
||||
from ..adapters.c7200_io_2fe import C7200_IO_2FE
|
||||
from ..adapters.c7200_io_ge_e import C7200_IO_GE_E
|
||||
from pkg_resources import parse_version
|
||||
|
||||
import logging
|
||||
log = logging.getLogger(__name__)
|
||||
|
@ -24,6 +24,7 @@ import os
|
||||
import socket
|
||||
import shutil
|
||||
|
||||
from pkg_resources import parse_version
|
||||
from gns3server.modules import IModule
|
||||
from gns3server.config import Config
|
||||
from .virtualbox_vm import VirtualBoxVM
|
||||
@ -106,8 +107,10 @@ class VirtualBox(IModule):
|
||||
"""
|
||||
|
||||
if sys.platform.startswith("win"):
|
||||
import pywintypes
|
||||
import win32com.client
|
||||
|
||||
try:
|
||||
if win32com.client.gencache.is_readonly is True:
|
||||
# dynamically generate the cache
|
||||
# http://www.py2exe.org/index.cgi/IncludingTypelibs
|
||||
@ -116,15 +119,18 @@ class VirtualBox(IModule):
|
||||
#win32com.client.gencache.Rebuild()
|
||||
win32com.client.gencache.GetGeneratePath()
|
||||
|
||||
try:
|
||||
win32com.client.gencache.EnsureDispatch("VirtualBox.VirtualBox")
|
||||
except:
|
||||
except pywintypes.com_error:
|
||||
raise VirtualBoxError("VirtualBox is not installed.")
|
||||
|
||||
try:
|
||||
from .vboxapi_py3 import VirtualBoxManager
|
||||
self._vboxmanager = VirtualBoxManager(None, None)
|
||||
vbox_major_version, vbox_minor_version, _ = self._vboxmanager.vbox.version.split('.')
|
||||
if parse_version("{}.{}".format(vbox_major_version, vbox_minor_version)) <= parse_version("4.1"):
|
||||
raise VirtualBoxError("VirtualBox version must be >= 4.2")
|
||||
except Exception as e:
|
||||
self._vboxmanager = None
|
||||
raise VirtualBoxError("Could not initialize the VirtualBox Manager: {}".format(e))
|
||||
|
||||
log.info("VirtualBox Manager has successful started: version is {} r{}".format(self._vboxmanager.vbox.version,
|
||||
@ -139,7 +145,11 @@ class VirtualBox(IModule):
|
||||
|
||||
self._vboxwrapper = VboxWrapperClient(self._vboxwrapper_path, self._tempdir, "127.0.0.1")
|
||||
#self._vboxwrapper.connect()
|
||||
try:
|
||||
self._vboxwrapper.start()
|
||||
except VirtualBoxError:
|
||||
self._vboxwrapper = None
|
||||
raise
|
||||
|
||||
def stop(self, signum=None):
|
||||
"""
|
||||
|
@ -26,6 +26,7 @@ import tempfile
|
||||
import socket
|
||||
import re
|
||||
|
||||
from pkg_resources import parse_version
|
||||
from ..attic import wait_socket_is_ready
|
||||
from .virtualbox_error import VirtualBoxError
|
||||
|
||||
@ -53,7 +54,7 @@ class VboxWrapperClient(object):
|
||||
self._command = []
|
||||
self._process = None
|
||||
self._working_dir = working_dir
|
||||
self._stdout_file = ""
|
||||
self._stderr_file = ""
|
||||
self._started = False
|
||||
self._host = host
|
||||
self._port = port
|
||||
@ -139,19 +140,31 @@ class VboxWrapperClient(object):
|
||||
try:
|
||||
log.info("starting VirtualBox wrapper: {}".format(self._command))
|
||||
with tempfile.NamedTemporaryFile(delete=False) as fd:
|
||||
self._stdout_file = fd.name
|
||||
with open(os.devnull, "w") as null:
|
||||
self._stderr_file = fd.name
|
||||
log.info("VirtualBox wrapper process logging to {}".format(fd.name))
|
||||
self._process = subprocess.Popen(self._command,
|
||||
stdout=fd,
|
||||
stderr=subprocess.STDOUT,
|
||||
stdout=null,
|
||||
stderr=fd,
|
||||
cwd=self._working_dir)
|
||||
log.info("VirtualBox wrapper started PID={}".format(self._process.pid))
|
||||
|
||||
time.sleep(0.1) # give some time for vboxwrapper to start
|
||||
if self._process.poll() is not None:
|
||||
raise VirtualBoxError("Could not start VirtualBox wrapper: {}".format(self.read_stderr()))
|
||||
|
||||
self.wait_for_vboxwrapper(self._host, self._port)
|
||||
self.connect()
|
||||
self._started = True
|
||||
|
||||
version = self.send('vboxwrapper version')[0]
|
||||
if parse_version(version) < parse_version("0.9.2"):
|
||||
self.stop()
|
||||
raise VirtualBoxError("VirtualBox wrapper version must be >= 0.9.1")
|
||||
|
||||
except OSError as e:
|
||||
log.error("could not start VirtualBox wrapper: {}".format(e))
|
||||
raise VirtualBoxError("could not start VirtualBox wrapper: {}".format(e))
|
||||
raise VirtualBoxError("Could not start VirtualBox wrapper: {}".format(e))
|
||||
|
||||
def wait_for_vboxwrapper(self, host, port):
|
||||
"""
|
||||
@ -198,26 +211,26 @@ class VboxWrapperClient(object):
|
||||
if self._process.poll() is None:
|
||||
log.warn("VirtualBox wrapper process {} is still running".format(self._process.pid))
|
||||
|
||||
if self._stdout_file and os.access(self._stdout_file, os.W_OK):
|
||||
if self._stderr_file and os.access(self._stderr_file, os.W_OK):
|
||||
try:
|
||||
os.remove(self._stdout_file)
|
||||
os.remove(self._stderr_file)
|
||||
except OSError as e:
|
||||
log.warning("could not delete temporary VirtualBox wrapper log file: {}".format(e))
|
||||
self._started = False
|
||||
|
||||
def read_stdout(self):
|
||||
def read_stderr(self):
|
||||
"""
|
||||
Reads the standard output of the VirtualBox wrapper process.
|
||||
Reads the standard error output of the VirtualBox wrapper process.
|
||||
Only use when the process has been stopped or has crashed.
|
||||
"""
|
||||
|
||||
output = ""
|
||||
if self._stdout_file and os.access(self._stdout_file, os.R_OK):
|
||||
if self._stderr_file and os.access(self._stderr_file, os.R_OK):
|
||||
try:
|
||||
with open(self._stdout_file, errors="replace") as file:
|
||||
with open(self._stderr_file, errors="replace") as file:
|
||||
output = file.read()
|
||||
except OSError as e:
|
||||
log.warn("could not read {}: {}".format(self._stdout_file, e))
|
||||
log.warn("could not read {}: {}".format(self._stderr_file, e))
|
||||
return output
|
||||
|
||||
def is_running(self):
|
||||
|
529
gns3server/modules/virtualbox/virtualbox_controller.py
Normal file
529
gns3server/modules/virtualbox/virtualbox_controller.py
Normal file
@ -0,0 +1,529 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2014 GNS3 Technologies Inc.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""
|
||||
Controls VirtualBox using the VBox API.
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
import tempfile
|
||||
import re
|
||||
import time
|
||||
import socket
|
||||
import subprocess
|
||||
|
||||
if sys.platform.startswith('win'):
|
||||
import msvcrt
|
||||
import win32file
|
||||
|
||||
from .virtualbox_error import VirtualBoxError
|
||||
from .pipe_proxy import PipeProxy
|
||||
|
||||
import logging
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class VirtualBoxController(object):
|
||||
|
||||
def __init__(self, vmname, vboxmanager, host):
|
||||
|
||||
self._host = host
|
||||
self._machine = None
|
||||
self._session = None
|
||||
self._vboxmanager = vboxmanager
|
||||
self._maximum_adapters = 0
|
||||
self._serial_pipe_thread = None
|
||||
self._serial_pipe = None
|
||||
|
||||
self._vmname = vmname
|
||||
self._console = 0
|
||||
self._adapters = []
|
||||
self._headless = False
|
||||
self._adapter_type = "Automatic"
|
||||
|
||||
try:
|
||||
self._machine = self._vboxmanager.vbox.findMachine(self._vmname)
|
||||
except Exception as e:
|
||||
raise VirtualBoxError("VirtualBox error: {}".format(e))
|
||||
|
||||
# The maximum support network cards depends on the Chipset (PIIX3 or ICH9)
|
||||
self._maximum_adapters = self._vboxmanager.vbox.systemProperties.getMaxNetworkAdapters(self._machine.chipsetType)
|
||||
|
||||
@property
|
||||
def vmname(self):
|
||||
|
||||
return self._vmname
|
||||
|
||||
@vmname.setter
|
||||
def vmname(self, new_vmname):
|
||||
|
||||
self._vmname = new_vmname
|
||||
try:
|
||||
self._machine = self._vboxmanager.vbox.findMachine(new_vmname)
|
||||
except Exception as e:
|
||||
raise VirtualBoxError("VirtualBox error: {}".format(e))
|
||||
|
||||
# The maximum support network cards depends on the Chipset (PIIX3 or ICH9)
|
||||
self._maximum_adapters = self._vboxmanager.vbox.systemProperties.getMaxNetworkAdapters(self._machine.chipsetType)
|
||||
|
||||
@property
|
||||
def console(self):
|
||||
|
||||
return self._console
|
||||
|
||||
@console.setter
|
||||
def console(self, console):
|
||||
|
||||
self._console = console
|
||||
|
||||
@property
|
||||
def headless(self):
|
||||
|
||||
return self._headless
|
||||
|
||||
@headless.setter
|
||||
def headless(self, headless):
|
||||
|
||||
self._headless = headless
|
||||
|
||||
@property
|
||||
def adapters(self):
|
||||
|
||||
return self._adapters
|
||||
|
||||
@adapters.setter
|
||||
def adapters(self, adapters):
|
||||
|
||||
self._adapters = adapters
|
||||
|
||||
@property
|
||||
def adapter_type(self):
|
||||
|
||||
return self._adapter_type
|
||||
|
||||
@adapter_type.setter
|
||||
def adapter_type(self, adapter_type):
|
||||
|
||||
self._adapter_type = adapter_type
|
||||
|
||||
def start(self):
|
||||
|
||||
if self._machine.state == self._vboxmanager.constants.MachineState_Paused:
|
||||
self.resume()
|
||||
return
|
||||
|
||||
self._get_session()
|
||||
self._set_network_options()
|
||||
self._set_console_options()
|
||||
|
||||
progress = self._launch_vm_process()
|
||||
log.info("VM is starting with {}% completed".format(progress.percent))
|
||||
if progress.percent != 100:
|
||||
# This will happen if you attempt to start VirtualBox with unloaded "vboxdrv" module.
|
||||
# or have too little RAM or damaged vHDD, or connected to non-existent network.
|
||||
# We must unlock machine, otherwise it locks the VirtualBox Manager GUI. (on Linux hosts)
|
||||
self._unlock_machine()
|
||||
raise VirtualBoxError("Unable to start the VM (failed at {}%)".format(progress.percent))
|
||||
|
||||
try:
|
||||
self._machine.setGuestPropertyValue("NameInGNS3", self._name)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# starts the Telnet to pipe thread
|
||||
pipe_name = self._get_pipe_name()
|
||||
if sys.platform.startswith('win'):
|
||||
try:
|
||||
self._serial_pipe = open(pipe_name, "a+b")
|
||||
except OSError as e:
|
||||
raise VirtualBoxError("Could not open the pipe {}: {}".format(pipe_name, e))
|
||||
self._serial_pipe_thread = PipeProxy(self._vmname, msvcrt.get_osfhandle(self._serial_pipe.fileno()), self._host, self._console)
|
||||
#self._serial_pipe_thread.setDaemon(True)
|
||||
self._serial_pipe_thread.start()
|
||||
else:
|
||||
try:
|
||||
self._serial_pipe = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
||||
self._serial_pipe.connect(pipe_name)
|
||||
except OSError as e:
|
||||
raise VirtualBoxError("Could not connect to the pipe {}: {}".format(pipe_name, e))
|
||||
self._serial_pipe_thread = PipeProxy(self._vmname, self._serial_pipe, self._host, self._console)
|
||||
#self._serial_pipe_thread.setDaemon(True)
|
||||
self._serial_pipe_thread.start()
|
||||
|
||||
def stop(self):
|
||||
|
||||
if self._serial_pipe_thread:
|
||||
self._serial_pipe_thread.stop()
|
||||
self._serial_pipe_thread.join(1)
|
||||
if self._serial_pipe_thread.isAlive():
|
||||
log.warn("Serial pire thread is still alive!")
|
||||
self._serial_pipe_thread = None
|
||||
|
||||
if self._serial_pipe:
|
||||
if sys.platform.startswith('win'):
|
||||
win32file.CloseHandle(msvcrt.get_osfhandle(self._serial_pipe.fileno()))
|
||||
else:
|
||||
self._serial_pipe.close()
|
||||
self._serial_pipe = None
|
||||
|
||||
if self._machine.state >= self._vboxmanager.constants.MachineState_FirstOnline and \
|
||||
self._machine.state <= self._vboxmanager.constants.MachineState_LastOnline:
|
||||
try:
|
||||
if sys.platform.startswith('win') and "VBOX_INSTALL_PATH" in os.environ:
|
||||
# work around VirtualBox bug #9239
|
||||
vboxmanage_path = os.path.join(os.environ["VBOX_INSTALL_PATH"], "VBoxManage.exe")
|
||||
command = '"{}" controlvm "{}" poweroff'.format(vboxmanage_path, self._vmname)
|
||||
subprocess.call(command, timeout=3)
|
||||
else:
|
||||
progress = self._session.console.powerDown()
|
||||
# wait for VM to actually go down
|
||||
progress.waitForCompletion(3000)
|
||||
log.info("VM is stopping with {}% completed".format(self.vmname, progress.percent))
|
||||
|
||||
self._lock_machine()
|
||||
for adapter_id in range(0, len(self._adapters)):
|
||||
self._disable_adapter(adapter_id, disable=True)
|
||||
self._session.machine.saveSettings()
|
||||
self._unlock_machine()
|
||||
except Exception as e:
|
||||
# Do not crash "vboxwrapper", if stopping VM fails.
|
||||
# But return True anyway, so VM state in GNS3 can become "stopped"
|
||||
# This can happen, if user manually kills VBox VM.
|
||||
log.warn("could not stop VM for {}: {}".format(self._vmname, e))
|
||||
return
|
||||
|
||||
def suspend(self):
|
||||
|
||||
try:
|
||||
self._session.console.pause()
|
||||
except Exception as e:
|
||||
raise VirtualBoxError("VirtualBox error: {}".format(e))
|
||||
|
||||
def reload(self):
|
||||
|
||||
try:
|
||||
self._session.console.reset()
|
||||
except Exception as e:
|
||||
raise VirtualBoxError("VirtualBox error: {}".format(e))
|
||||
|
||||
def resume(self):
|
||||
|
||||
try:
|
||||
self._session.console.resume()
|
||||
except Exception as e:
|
||||
raise VirtualBoxError("VirtualBox error: {}".format(e))
|
||||
|
||||
def _get_session(self):
|
||||
|
||||
log.debug("getting session for {}".format(self._vmname))
|
||||
try:
|
||||
self._session = self._vboxmanager.mgr.getSessionObject(self._vboxmanager.vbox)
|
||||
except Exception as e:
|
||||
# fails on heavily loaded hosts...
|
||||
raise VirtualBoxError("VirtualBox error: {}".format(e))
|
||||
|
||||
def _set_network_options(self):
|
||||
|
||||
log.debug("setting network options for {}".format(self._vmname))
|
||||
|
||||
self._lock_machine()
|
||||
|
||||
first_adapter_type = self._vboxmanager.constants.NetworkAdapterType_I82540EM
|
||||
try:
|
||||
first_adapter = self._session.machine.getNetworkAdapter(0)
|
||||
first_adapter_type = first_adapter.adapterType
|
||||
except Exception as e:
|
||||
pass
|
||||
#raise VirtualBoxError("VirtualBox error: {}".format(e))
|
||||
|
||||
for adapter_id in range(0, len(self._adapters)):
|
||||
try:
|
||||
# VirtualBox starts counting from 0
|
||||
adapter = self._session.machine.getNetworkAdapter(adapter_id)
|
||||
vbox_adapter_type = adapter.adapterType
|
||||
|
||||
if self._adapter_type == "PCnet-PCI II (Am79C970A)":
|
||||
vbox_adapter_type = self._vboxmanager.constants.NetworkAdapterType_Am79C970A
|
||||
if self._adapter_type == "PCNet-FAST III (Am79C973)":
|
||||
vbox_adapter_type = self._vboxmanager.constants.NetworkAdapterType_Am79C973
|
||||
if self._adapter_type == "Intel PRO/1000 MT Desktop (82540EM)":
|
||||
vbox_adapter_type = self._vboxmanager.constants.NetworkAdapterType_I82540EM
|
||||
if self._adapter_type == "Intel PRO/1000 T Server (82543GC)":
|
||||
vbox_adapter_type = self._vboxmanager.constants.NetworkAdapterType_I82543GC
|
||||
if self._adapter_type == "Intel PRO/1000 MT Server (82545EM)":
|
||||
vbox_adapter_type = self._vboxmanager.constants.NetworkAdapterType_I82545EM
|
||||
if self._adapter_type == "Paravirtualized Network (virtio-net)":
|
||||
vbox_adapter_type = self._vboxmanager.constants.NetworkAdapterType_Virtio
|
||||
if self._adapter_type == "Automatic": # "Auto-guess, based on first NIC"
|
||||
vbox_adapter_type = first_adapter_type
|
||||
|
||||
adapter.adapterType = vbox_adapter_type
|
||||
|
||||
except Exception as e:
|
||||
raise VirtualBoxError("VirtualBox error: {}".format(e))
|
||||
|
||||
nio = self._adapters[adapter_id].get_nio(0)
|
||||
if nio:
|
||||
log.debug("setting UDP params on adapter {}".format(adapter_id))
|
||||
try:
|
||||
adapter.enabled = True
|
||||
adapter.cableConnected = True
|
||||
adapter.traceEnabled = False
|
||||
# Temporary hack around VBox-UDP patch limitation: inability to use DNS
|
||||
if nio.rhost == 'localhost':
|
||||
rhost = '127.0.0.1'
|
||||
else:
|
||||
rhost = nio.rhost
|
||||
adapter.attachmentType = self._vboxmanager.constants.NetworkAttachmentType_Generic
|
||||
adapter.genericDriver = "UDPTunnel"
|
||||
adapter.setProperty("sport", str(nio.lport))
|
||||
adapter.setProperty("dest", rhost)
|
||||
adapter.setProperty("dport", str(nio.rport))
|
||||
except Exception as e:
|
||||
# usually due to COM Error: "The object is not ready"
|
||||
raise VirtualBoxError("VirtualBox error: {}".format(e))
|
||||
|
||||
if nio.capturing:
|
||||
self._enable_capture(adapter, nio.pcap_output_file)
|
||||
|
||||
else:
|
||||
# shutting down unused adapters...
|
||||
try:
|
||||
adapter.enabled = True
|
||||
adapter.attachmentType = self._vboxmanager.constants.NetworkAttachmentType_Null
|
||||
adapter.cableConnected = False
|
||||
except Exception as e:
|
||||
raise VirtualBoxError("VirtualBox error: {}".format(e))
|
||||
|
||||
#for adapter_id in range(len(self._ethernet_adapters), self._maximum_adapters):
|
||||
# log.debug("disabling remaining adapter {}".format(adapter_id))
|
||||
# self._disable_adapter(adapter_id)
|
||||
|
||||
try:
|
||||
self._session.machine.saveSettings()
|
||||
except Exception as e:
|
||||
raise VirtualBoxError("VirtualBox error: {}".format(e))
|
||||
|
||||
self._unlock_machine()
|
||||
|
||||
def _disable_adapter(self, adapter_id, disable=True):
|
||||
|
||||
log.debug("disabling network adapter for {}".format(self._vmname))
|
||||
# this command is retried several times, because it fails more often...
|
||||
retries = 6
|
||||
last_exception = None
|
||||
for retry in range(retries):
|
||||
if retry == (retries - 1):
|
||||
raise VirtualBoxError("Could not disable network adapter after 4 retries: {}".format(last_exception))
|
||||
try:
|
||||
adapter = self._session.machine.getNetworkAdapter(adapter_id)
|
||||
adapter.traceEnabled = False
|
||||
adapter.attachmentType = self._vboxmanager.constants.NetworkAttachmentType_Null
|
||||
if disable:
|
||||
adapter.enabled = False
|
||||
break
|
||||
except Exception as e:
|
||||
# usually due to COM Error: "The object is not ready"
|
||||
log.warn("cannot disable network adapter for {}, retrying {}: {}".format(self._vmname, retry + 1, e))
|
||||
last_exception = e
|
||||
time.sleep(1)
|
||||
continue
|
||||
|
||||
def _enable_capture(self, adapter, output_file):
|
||||
|
||||
log.debug("enabling capture for {}".format(self._vmname))
|
||||
# this command is retried several times, because it fails more often...
|
||||
retries = 4
|
||||
last_exception = None
|
||||
for retry in range(retries):
|
||||
if retry == (retries - 1):
|
||||
raise VirtualBoxError("Could not enable packet capture after 4 retries: {}".format(last_exception))
|
||||
try:
|
||||
adapter.traceEnabled = True
|
||||
adapter.traceFile = output_file
|
||||
break
|
||||
except Exception as e:
|
||||
log.warn("cannot enable packet capture for {}, retrying {}: {}".format(self._vmname, retry + 1, e))
|
||||
last_exception = e
|
||||
time.sleep(0.75)
|
||||
continue
|
||||
|
||||
def create_udp(self, adapter_id, sport, daddr, dport):
|
||||
|
||||
if self._machine.state >= self._vboxmanager.constants.MachineState_FirstOnline and \
|
||||
self._machine.state <= self._vboxmanager.constants.MachineState_LastOnline:
|
||||
# the machine is being executed
|
||||
retries = 4
|
||||
last_exception = None
|
||||
for retry in range(retries):
|
||||
if retry == (retries - 1):
|
||||
raise VirtualBoxError("Could not create an UDP tunnel after 4 retries :{}".format(last_exception))
|
||||
try:
|
||||
adapter = self._session.machine.getNetworkAdapter(adapter_id)
|
||||
adapter.cableConnected = True
|
||||
adapter.attachmentType = self._vboxmanager.constants.NetworkAttachmentType_Null
|
||||
self._session.machine.saveSettings()
|
||||
adapter.attachmentType = self._vboxmanager.constants.NetworkAttachmentType_Generic
|
||||
adapter.genericDriver = "UDPTunnel"
|
||||
adapter.setProperty("sport", str(sport))
|
||||
adapter.setProperty("dest", daddr)
|
||||
adapter.setProperty("dport", str(dport))
|
||||
self._session.machine.saveSettings()
|
||||
break
|
||||
except Exception as e:
|
||||
# usually due to COM Error: "The object is not ready"
|
||||
log.warn("cannot create UDP tunnel for {}: {}".format(self._vmname, e))
|
||||
last_exception = e
|
||||
time.sleep(0.75)
|
||||
continue
|
||||
|
||||
def delete_udp(self, adapter_id):
|
||||
|
||||
if self._machine.state >= self._vboxmanager.constants.MachineState_FirstOnline and \
|
||||
self._machine.state <= self._vboxmanager.constants.MachineState_LastOnline:
|
||||
# the machine is being executed
|
||||
retries = 4
|
||||
last_exception = None
|
||||
for retry in range(retries):
|
||||
if retry == (retries - 1):
|
||||
raise VirtualBoxError("Could not delete an UDP tunnel after 4 retries :{}".format(last_exception))
|
||||
try:
|
||||
adapter = self._session.machine.getNetworkAdapter(adapter_id)
|
||||
adapter.attachmentType = self._vboxmanager.constants.NetworkAttachmentType_Null
|
||||
adapter.cableConnected = False
|
||||
self._session.machine.saveSettings()
|
||||
break
|
||||
except Exception as e:
|
||||
# usually due to COM Error: "The object is not ready"
|
||||
log.debug("cannot delete UDP tunnel for {}: {}".format(self._vmname, e))
|
||||
last_exception = e
|
||||
time.sleep(0.75)
|
||||
continue
|
||||
|
||||
def _get_pipe_name(self):
|
||||
|
||||
p = re.compile('\s+', re.UNICODE)
|
||||
pipe_name = p.sub("_", self._vmname)
|
||||
if sys.platform.startswith('win'):
|
||||
pipe_name = r"\\.\pipe\VBOX\{}".format(pipe_name)
|
||||
else:
|
||||
pipe_name = os.path.join(tempfile.gettempdir(), "pipe_{}".format(pipe_name))
|
||||
return pipe_name
|
||||
|
||||
def _set_console_options(self):
|
||||
"""
|
||||
# Example to manually set serial parameters using Python
|
||||
|
||||
from vboxapi import VirtualBoxManager
|
||||
mgr = VirtualBoxManager(None, None)
|
||||
mach = mgr.vbox.findMachine("My VM")
|
||||
session = mgr.mgr.getSessionObject(mgr.vbox)
|
||||
mach.lockMachine(session, 1)
|
||||
mach2=session.machine
|
||||
serial_port = mach2.getSerialPort(0)
|
||||
serial_port.enabled = True
|
||||
serial_port.path = "/tmp/test_pipe"
|
||||
serial_port.hostMode = 1
|
||||
serial_port.server = True
|
||||
session.unlockMachine()
|
||||
"""
|
||||
|
||||
log.info("setting console options for {}".format(self._vmname))
|
||||
|
||||
self._lock_machine()
|
||||
pipe_name = self._get_pipe_name()
|
||||
|
||||
try:
|
||||
serial_port = self._session.machine.getSerialPort(0)
|
||||
serial_port.enabled = True
|
||||
serial_port.path = pipe_name
|
||||
serial_port.hostMode = 1
|
||||
serial_port.server = True
|
||||
self._session.machine.saveSettings()
|
||||
except Exception as e:
|
||||
raise VirtualBoxError("VirtualBox error: {}".format(e))
|
||||
|
||||
self._unlock_machine()
|
||||
|
||||
def _launch_vm_process(self):
|
||||
|
||||
log.debug("launching VM {}".format(self._vmname))
|
||||
# this command is retried several times, because it fails more often...
|
||||
retries = 4
|
||||
last_exception = None
|
||||
for retry in range(retries):
|
||||
if retry == (retries - 1):
|
||||
raise VirtualBoxError("Could not launch the VM after 4 retries: {}".format(last_exception))
|
||||
try:
|
||||
if self._headless:
|
||||
mode = "headless"
|
||||
else:
|
||||
mode = "gui"
|
||||
log.info("starting {} in {} mode".format(self._vmname, mode))
|
||||
progress = self._machine.launchVMProcess(self._session, mode, "")
|
||||
break
|
||||
except Exception as e:
|
||||
# This will usually happen if you try to start the same VM twice,
|
||||
# but may happen on loaded hosts too...
|
||||
log.warn("cannot launch VM {}, retrying {}: {}".format(self._vmname, retry + 1, e))
|
||||
last_exception = e
|
||||
time.sleep(0.6)
|
||||
continue
|
||||
|
||||
try:
|
||||
progress.waitForCompletion(-1)
|
||||
except Exception as e:
|
||||
raise VirtualBoxError("VirtualBox error: {}".format(e))
|
||||
|
||||
return progress
|
||||
|
||||
def _lock_machine(self):
|
||||
|
||||
log.debug("locking machine for {}".format(self._vmname))
|
||||
# this command is retried several times, because it fails more often...
|
||||
retries = 4
|
||||
last_exception = None
|
||||
for retry in range(retries):
|
||||
if retry == (retries - 1):
|
||||
raise VirtualBoxError("Could not lock the machine after 4 retries: {}".format(last_exception))
|
||||
try:
|
||||
self._machine.lockMachine(self._session, 1)
|
||||
break
|
||||
except Exception as e:
|
||||
log.warn("cannot lock the machine for {}, retrying {}: {}".format(self._vmname, retry + 1, e))
|
||||
last_exception = e
|
||||
time.sleep(1)
|
||||
continue
|
||||
|
||||
def _unlock_machine(self):
|
||||
|
||||
log.debug("unlocking machine for {}".format(self._vmname))
|
||||
# this command is retried several times, because it fails more often...
|
||||
retries = 4
|
||||
last_exception = None
|
||||
for retry in range(retries):
|
||||
if retry == (retries - 1):
|
||||
raise VirtualBoxError("Could not unlock the machine after 4 retries: {}".format(last_exception))
|
||||
try:
|
||||
self._session.unlockMachine()
|
||||
break
|
||||
except Exception as e:
|
||||
log.warn("cannot unlock the machine for {}, retrying {}: {}".format(self._vmname, retry + 1, e))
|
||||
time.sleep(1)
|
||||
last_exception = e
|
||||
continue
|
@ -19,24 +19,14 @@
|
||||
VirtualBox VM instance.
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
import re
|
||||
import time
|
||||
import socket
|
||||
import subprocess
|
||||
|
||||
from .pipe_proxy import PipeProxy
|
||||
from .virtualbox_error import VirtualBoxError
|
||||
from .virtualbox_controller import VirtualBoxController
|
||||
from .adapters.ethernet_adapter import EthernetAdapter
|
||||
from ..attic import find_unused_port
|
||||
|
||||
if sys.platform.startswith('win'):
|
||||
import msvcrt
|
||||
import win32file
|
||||
|
||||
import logging
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@ -97,16 +87,6 @@ class VirtualBoxVM(object):
|
||||
self._console_start_port_range = console_start_port_range
|
||||
self._console_end_port_range = console_end_port_range
|
||||
|
||||
# Telnet to pipe mini-server
|
||||
self._serial_pipe_thread = None
|
||||
self._serial_pipe = None
|
||||
|
||||
# VirtualBox API variables
|
||||
self._machine = None
|
||||
self._session = None
|
||||
self._vboxmanager = vboxmanager
|
||||
self._maximum_adapters = 0
|
||||
|
||||
# VirtualBox settings
|
||||
self._console = console
|
||||
self._ethernet_adapters = []
|
||||
@ -140,19 +120,11 @@ class VirtualBoxVM(object):
|
||||
self._vboxwrapper.send('vbox create vbox "{}"'.format(self._name))
|
||||
self._vboxwrapper.send('vbox setattr "{}" image "{}"'.format(self._name, vmname))
|
||||
self._vboxwrapper.send('vbox setattr "{}" console {}'.format(self._name, self._console))
|
||||
self._vboxwrapper.send('vbox setattr "{}" console_support True'.format(self._name))
|
||||
self._vboxwrapper.send('vbox setattr "{}" console_telnet_server True'.format(self._name))
|
||||
else:
|
||||
try:
|
||||
self._machine = self._vboxmanager.vbox.findMachine(self._vmname)
|
||||
except Exception as e:
|
||||
raise VirtualBoxError("VirtualBox error: {}".format(e))
|
||||
|
||||
# The maximum support network cards depends on the Chipset (PIIX3 or ICH9)
|
||||
self._maximum_adapters = self._vboxmanager.vbox.systemProperties.getMaxNetworkAdapters(self._machine.chipsetType)
|
||||
|
||||
self.adapters = 2
|
||||
self._vboxcontroller = VirtualBoxController(self._vmname, vboxmanager, self._host)
|
||||
self._vboxcontroller.console = self._console
|
||||
|
||||
self.adapters = 2 # creates 2 adapters by default
|
||||
log.info("VirtualBox VM {name} [id={id}] has been created".format(name=self._name,
|
||||
id=self._id))
|
||||
|
||||
@ -274,6 +246,8 @@ class VirtualBoxVM(object):
|
||||
|
||||
if self._vboxwrapper:
|
||||
self._vboxwrapper.send('vbox setattr "{}" console {}'.format(self._name, self._console))
|
||||
else:
|
||||
self._vboxcontroller.console = console
|
||||
|
||||
log.info("VirtualBox VM {name} [id={id}]: console port set to {port}".format(name=self._name,
|
||||
id=self._id,
|
||||
@ -344,10 +318,14 @@ class VirtualBoxVM(object):
|
||||
if headless:
|
||||
if self._vboxwrapper:
|
||||
self._vboxwrapper.send('vbox setattr "{}" headless_mode True'.format(self._name))
|
||||
else:
|
||||
self._vboxcontroller.headless = True
|
||||
log.info("VirtualBox VM {name} [id={id}] has enabled the headless mode".format(name=self._name, id=self._id))
|
||||
else:
|
||||
if self._vboxwrapper:
|
||||
self._vboxwrapper.send('vbox setattr "{}" headless_mode False'.format(self._name))
|
||||
else:
|
||||
self._vboxcontroller.headless = False
|
||||
log.info("VirtualBox VM {name} [id={id}] has disabled the headless mode".format(name=self._name, id=self._id))
|
||||
self._headless = headless
|
||||
|
||||
@ -372,13 +350,7 @@ class VirtualBoxVM(object):
|
||||
if self._vboxwrapper:
|
||||
self._vboxwrapper.send('vbox setattr "{}" image "{}"'.format(self._name, vmname))
|
||||
else:
|
||||
try:
|
||||
self._machine = self._vboxmanager.vbox.findMachine(vmname)
|
||||
except Exception as e:
|
||||
raise VirtualBoxError("VirtualBox error: {}".format(e))
|
||||
|
||||
# The maximum support network cards depends on the Chipset (PIIX3 or ICH9)
|
||||
self._maximum_adapters = self._vboxmanager.vbox.systemProperties.getMaxNetworkAdapters(self._machine.chipsetType)
|
||||
self._vboxcontroller.vmname = vmname
|
||||
|
||||
log.info("VirtualBox VM {name} [id={id}] has set the VM name to {vmname}".format(name=self._name, id=self._id, vmname=vmname))
|
||||
self._vmname = vmname
|
||||
@ -407,6 +379,8 @@ class VirtualBoxVM(object):
|
||||
|
||||
if self._vboxwrapper:
|
||||
self._vboxwrapper.send('vbox setattr "{}" nics {}'.format(self._name, len(self._ethernet_adapters)))
|
||||
else:
|
||||
self._vboxcontroller.adapters = self._ethernet_adapters
|
||||
|
||||
log.info("VirtualBox VM {name} [id={id}]: number of Ethernet adapters changed to {adapters}".format(name=self._name,
|
||||
id=self._id,
|
||||
@ -434,6 +408,8 @@ class VirtualBoxVM(object):
|
||||
|
||||
if self._vboxwrapper:
|
||||
self._vboxwrapper.send('vbox setattr "{}" netcard "{}"'.format(self._name, adapter_type))
|
||||
else:
|
||||
self._vboxcontroller.adapter_type = adapter_type
|
||||
|
||||
log.info("VirtualBox VM {name} [id={id}]: adapter type changed to {adapter_type}".format(name=self._name,
|
||||
id=self._id,
|
||||
@ -445,54 +421,9 @@ class VirtualBoxVM(object):
|
||||
"""
|
||||
|
||||
if self._vboxwrapper:
|
||||
status = int(self._vboxwrapper.send('vbox status "{}"'.format(self._name))[0])
|
||||
if status == 6: # paused
|
||||
self.resume()
|
||||
return
|
||||
self._vboxwrapper.send('vbox start "{}"'.format(self._name))
|
||||
else:
|
||||
|
||||
if self._machine.state == self._vboxmanager.constants.MachineState_Paused:
|
||||
self.resume()
|
||||
return
|
||||
|
||||
self._get_session()
|
||||
self._set_network_options()
|
||||
self._set_console_options()
|
||||
|
||||
progress = self._launch_vm_process()
|
||||
log.info("VM is starting with {}% completed".format(progress.percent))
|
||||
if progress.percent != 100:
|
||||
# This will happen if you attempt to start VirtualBox with unloaded "vboxdrv" module.
|
||||
# or have too little RAM or damaged vHDD, or connected to non-existent network.
|
||||
# We must unlock machine, otherwise it locks the VirtualBox Manager GUI. (on Linux hosts)
|
||||
self._unlock_machine()
|
||||
raise VirtualBoxError("Unable to start the VM (failed at {}%)".format(progress.percent))
|
||||
|
||||
try:
|
||||
self._machine.setGuestPropertyValue("NameInGNS3", self._name)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# starts the Telnet to pipe thread
|
||||
pipe_name = self._get_pipe_name()
|
||||
if sys.platform.startswith('win'):
|
||||
try:
|
||||
self._serial_pipe = open(pipe_name, "a+b")
|
||||
except OSError as e:
|
||||
raise VirtualBoxError("Could not open the pipe {}: {}".format(pipe_name, e))
|
||||
self._serial_pipe_thread = PipeProxy(self._vmname, msvcrt.get_osfhandle(self._serial_pipe.fileno()), self._host, self._console)
|
||||
#self._serial_pipe_thread.setDaemon(True)
|
||||
self._serial_pipe_thread.start()
|
||||
else:
|
||||
try:
|
||||
self._serial_pipe = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
||||
self._serial_pipe.connect(pipe_name)
|
||||
except OSError as e:
|
||||
raise VirtualBoxError("Could not connect to the pipe {}: {}".format(pipe_name, e))
|
||||
self._serial_pipe_thread = PipeProxy(self._vmname, self._serial_pipe, self._host, self._console)
|
||||
#self._serial_pipe_thread.setDaemon(True)
|
||||
self._serial_pipe_thread.start()
|
||||
self._vboxcontroller.start()
|
||||
|
||||
def stop(self):
|
||||
"""
|
||||
@ -500,48 +431,13 @@ class VirtualBoxVM(object):
|
||||
"""
|
||||
|
||||
if self._vboxwrapper:
|
||||
self._vboxwrapper.send('vbox stop "{}"'.format(self._name))
|
||||
else:
|
||||
|
||||
if self._serial_pipe_thread:
|
||||
self._serial_pipe_thread.stop()
|
||||
self._serial_pipe_thread.join(1)
|
||||
if self._serial_pipe_thread.isAlive():
|
||||
log.warn("Serial pire thread is still alive!")
|
||||
self._serial_pipe_thread = None
|
||||
|
||||
if self._serial_pipe:
|
||||
if sys.platform.startswith('win'):
|
||||
win32file.CloseHandle(msvcrt.get_osfhandle(self._serial_pipe.fileno()))
|
||||
else:
|
||||
self._serial_pipe.close()
|
||||
self._serial_pipe = None
|
||||
|
||||
if self._machine.state >= self._vboxmanager.constants.MachineState_FirstOnline and \
|
||||
self._machine.state <= self._vboxmanager.constants.MachineState_LastOnline:
|
||||
try:
|
||||
if sys.platform.startswith('win') and "VBOX_INSTALL_PATH" in os.environ:
|
||||
# work around VirtualBox bug #9239
|
||||
vboxmanage_path = os.path.join(os.environ["VBOX_INSTALL_PATH"], "VBoxManage.exe")
|
||||
command = '"{}" controlvm "{}" poweroff'.format(vboxmanage_path, self._vmname)
|
||||
subprocess.call(command, timeout=3)
|
||||
else:
|
||||
progress = self._session.console.powerDown()
|
||||
# wait for VM to actually go down
|
||||
progress.waitForCompletion(3000)
|
||||
log.info("VM is stopping with {}% completed".format(self.vmname, progress.percent))
|
||||
|
||||
self._lock_machine()
|
||||
for adapter_id in range(0, len(self._ethernet_adapters)):
|
||||
self._disable_adapter(adapter_id, disable=True)
|
||||
self._session.machine.saveSettings()
|
||||
self._unlock_machine()
|
||||
except Exception as e:
|
||||
# Do not crash "vboxwrapper", if stopping VM fails.
|
||||
# But return True anyway, so VM state in GNS3 can become "stopped"
|
||||
# This can happen, if user manually kills VBox VM.
|
||||
log.warn("could not stop VM for {}: {}".format(self._vmname, e))
|
||||
self._vboxwrapper.send('vbox stop "{}"'.format(self._name))
|
||||
except VirtualBoxError:
|
||||
# probably lost the connection
|
||||
return
|
||||
else:
|
||||
self._vboxcontroller.stop()
|
||||
|
||||
def suspend(self):
|
||||
"""
|
||||
@ -551,10 +447,7 @@ class VirtualBoxVM(object):
|
||||
if self._vboxwrapper:
|
||||
self._vboxwrapper.send('vbox suspend "{}"'.format(self._name))
|
||||
else:
|
||||
try:
|
||||
self._session.console.pause()
|
||||
except Exception as e:
|
||||
raise VirtualBoxError("VirtualBox error: {}".format(e))
|
||||
self._vboxcontroller.suspend()
|
||||
|
||||
def reload(self):
|
||||
"""
|
||||
@ -564,11 +457,7 @@ class VirtualBoxVM(object):
|
||||
if self._vboxwrapper:
|
||||
self._vboxwrapper.send('vbox reset "{}"'.format(self._name))
|
||||
else:
|
||||
try:
|
||||
progress = self._session.console.reset()
|
||||
progress.waitForCompletion(-1)
|
||||
except Exception as e:
|
||||
raise VirtualBoxError("VirtualBox error: {}".format(e))
|
||||
self._vboxcontroller.reload()
|
||||
|
||||
def resume(self):
|
||||
"""
|
||||
@ -578,10 +467,7 @@ class VirtualBoxVM(object):
|
||||
if self._vboxwrapper:
|
||||
self._vboxwrapper.send('vbox resume "{}"'.format(self._name))
|
||||
else:
|
||||
try:
|
||||
self._session.console.resume()
|
||||
except Exception as e:
|
||||
raise VirtualBoxError("VirtualBox error: {}".format(e))
|
||||
self._vboxcontroller.resume()
|
||||
|
||||
def port_add_nio_binding(self, adapter_id, nio):
|
||||
"""
|
||||
@ -604,7 +490,7 @@ class VirtualBoxVM(object):
|
||||
nio.rhost,
|
||||
nio.rport))
|
||||
else:
|
||||
self._create_udp(adapter_id, nio.lport, nio.rhost, nio.rport)
|
||||
self._vboxcontroller.create_udp(adapter_id, nio.lport, nio.rhost, nio.rport)
|
||||
|
||||
adapter.add_nio(0, nio)
|
||||
log.info("VirtualBox VM {name} [id={id}]: {nio} added to adapter {adapter_id}".format(name=self._name,
|
||||
@ -631,7 +517,7 @@ class VirtualBoxVM(object):
|
||||
self._vboxwrapper.send('vbox delete_udp "{}" {}'.format(self._name,
|
||||
adapter_id))
|
||||
else:
|
||||
self._delete_udp(adapter_id)
|
||||
self._vboxcontroller.delete_udp(adapter_id)
|
||||
|
||||
nio = adapter.get_nio(0)
|
||||
adapter.remove_nio(0)
|
||||
@ -700,287 +586,3 @@ class VirtualBoxVM(object):
|
||||
log.info("VirtualBox VM {name} [id={id}]: stopping packet capture on adapter {adapter_id}".format(name=self._name,
|
||||
id=self._id,
|
||||
adapter_id=adapter_id))
|
||||
|
||||
def _get_session(self):
|
||||
|
||||
log.debug("getting session for {}".format(self._vmname))
|
||||
try:
|
||||
self._session = self._vboxmanager.mgr.getSessionObject(self._vboxmanager.vbox)
|
||||
except Exception as e:
|
||||
# fails on heavily loaded hosts...
|
||||
raise VirtualBoxError("VirtualBox error: {}".format(e))
|
||||
|
||||
def _set_network_options(self):
|
||||
|
||||
log.debug("setting network options for {}".format(self._vmname))
|
||||
|
||||
self._lock_machine()
|
||||
|
||||
first_adapter_type = self._vboxmanager.constants.NetworkAdapterType_I82540EM
|
||||
try:
|
||||
first_adapter = self._session.machine.getNetworkAdapter(0)
|
||||
first_adapter_type = first_adapter.adapterType
|
||||
except Exception as e:
|
||||
pass
|
||||
#raise VirtualBoxError("VirtualBox error: {}".format(e))
|
||||
|
||||
for adapter_id in range(0, len(self._ethernet_adapters)):
|
||||
try:
|
||||
# VirtualBox starts counting from 0
|
||||
adapter = self._session.machine.getNetworkAdapter(adapter_id)
|
||||
adapter_type = adapter.adapterType
|
||||
|
||||
if self._adapter_type == "PCnet-PCI II (Am79C970A)":
|
||||
adapter_type = self._vboxmanager.constants.NetworkAdapterType_Am79C970A
|
||||
if self._adapter_type == "PCNet-FAST III (Am79C973)":
|
||||
adapter_type = self._vboxmanager.constants.NetworkAdapterType_Am79C973
|
||||
if self._adapter_type == "Intel PRO/1000 MT Desktop (82540EM)":
|
||||
adapter_type = self._vboxmanager.constants.NetworkAdapterType_I82540EM
|
||||
if self._adapter_type == "Intel PRO/1000 T Server (82543GC)":
|
||||
adapter_type = self._vboxmanager.constants.NetworkAdapterType_I82543GC
|
||||
if self._adapter_type == "Intel PRO/1000 MT Server (82545EM)":
|
||||
adapter_type = self._vboxmanager.constants.NetworkAdapterType_I82545EM
|
||||
if self._adapter_type == "Paravirtualized Network (virtio-net)":
|
||||
adapter_type = self._vboxmanager.constants.NetworkAdapterType_Virtio
|
||||
if self._adapter_type == "Automatic": # "Auto-guess, based on first NIC"
|
||||
adapter_type = first_adapter_type
|
||||
|
||||
adapter.adapterType = adapter_type
|
||||
|
||||
except Exception as e:
|
||||
raise VirtualBoxError("VirtualBox error: {}".format(e))
|
||||
|
||||
nio = self._ethernet_adapters[adapter_id].get_nio(0)
|
||||
if nio:
|
||||
log.debug("setting UDP params on adapter {}".format(adapter_id))
|
||||
try:
|
||||
adapter.enabled = True
|
||||
adapter.cableConnected = True
|
||||
adapter.traceEnabled = False
|
||||
# Temporary hack around VBox-UDP patch limitation: inability to use DNS
|
||||
if nio.rhost == 'localhost':
|
||||
rhost = '127.0.0.1'
|
||||
else:
|
||||
rhost = nio.rhost
|
||||
adapter.attachmentType = self._vboxmanager.constants.NetworkAttachmentType_Generic
|
||||
adapter.genericDriver = "UDPTunnel"
|
||||
adapter.setProperty("sport", str(nio.lport))
|
||||
adapter.setProperty("dest", rhost)
|
||||
adapter.setProperty("dport", str(nio.rport))
|
||||
except Exception as e:
|
||||
# usually due to COM Error: "The object is not ready"
|
||||
raise VirtualBoxError("VirtualBox error: {}".format(e))
|
||||
|
||||
if nio.capturing:
|
||||
self._enable_capture(adapter, nio.pcap_output_file)
|
||||
|
||||
else:
|
||||
# shutting down unused adapters...
|
||||
try:
|
||||
adapter.enabled = True
|
||||
adapter.attachmentType = self._vboxmanager.constants.NetworkAttachmentType_Null
|
||||
adapter.cableConnected = False
|
||||
except Exception as e:
|
||||
raise VirtualBoxError("VirtualBox error: {}".format(e))
|
||||
|
||||
#for adapter_id in range(len(self._ethernet_adapters), self._maximum_adapters):
|
||||
# log.debug("disabling remaining adapter {}".format(adapter_id))
|
||||
# self._disable_adapter(adapter_id)
|
||||
|
||||
try:
|
||||
self._session.machine.saveSettings()
|
||||
except Exception as e:
|
||||
raise VirtualBoxError("VirtualBox error: {}".format(e))
|
||||
|
||||
self._unlock_machine()
|
||||
|
||||
def _disable_adapter(self, adapter_id, disable=True):
|
||||
|
||||
log.debug("disabling network adapter for {}".format(self._vmname))
|
||||
# this command is retried several times, because it fails more often...
|
||||
retries = 6
|
||||
last_exception = None
|
||||
for retry in range(retries):
|
||||
if retry == (retries - 1):
|
||||
raise VirtualBoxError("Could not disable network adapter after 4 retries: {}".format(last_exception))
|
||||
try:
|
||||
adapter = self._session.machine.getNetworkAdapter(adapter_id)
|
||||
adapter.traceEnabled = False
|
||||
adapter.attachmentType = self._vboxmanager.constants.NetworkAttachmentType_Null
|
||||
if disable:
|
||||
adapter.enabled = False
|
||||
break
|
||||
except Exception as e:
|
||||
# usually due to COM Error: "The object is not ready"
|
||||
log.warn("cannot disable network adapter for {}, retrying {}: {}".format(self._vmname, retry + 1, e))
|
||||
last_exception = e
|
||||
time.sleep(1)
|
||||
continue
|
||||
|
||||
def _enable_capture(self, adapter, output_file):
|
||||
|
||||
log.debug("enabling capture for {}".format(self._vmname))
|
||||
# this command is retried several times, because it fails more often...
|
||||
retries = 4
|
||||
last_exception = None
|
||||
for retry in range(retries):
|
||||
if retry == (retries - 1):
|
||||
raise VirtualBoxError("Could not enable packet capture after 4 retries: {}".format(last_exception))
|
||||
try:
|
||||
adapter.traceEnabled = True
|
||||
adapter.traceFile = output_file
|
||||
break
|
||||
except Exception as e:
|
||||
log.warn("cannot enable packet capture for {}, retrying {}: {}".format(self._vmname, retry + 1, e))
|
||||
last_exception = e
|
||||
time.sleep(0.75)
|
||||
continue
|
||||
|
||||
def _create_udp(self, adapter_id, sport, daddr, dport):
|
||||
|
||||
if self._machine.state >= self._vboxmanager.constants.MachineState_FirstOnline and \
|
||||
self._machine.state <= self._vboxmanager.constants.MachineState_LastOnline:
|
||||
# the machine is being executed
|
||||
retries = 4
|
||||
last_exception = None
|
||||
for retry in range(retries):
|
||||
if retry == (retries - 1):
|
||||
raise VirtualBoxError("Could not create an UDP tunnel after 4 retries :{}".format(last_exception))
|
||||
try:
|
||||
adapter = self._session.machine.getNetworkAdapter(adapter_id)
|
||||
adapter.cableConnected = True
|
||||
adapter.attachmentType = self._vboxmanager.constants.NetworkAttachmentType_Null
|
||||
self._session.machine.saveSettings()
|
||||
adapter.attachmentType = self._vboxmanager.constants.NetworkAttachmentType_Generic
|
||||
adapter.genericDriver = "UDPTunnel"
|
||||
adapter.setProperty("sport", str(sport))
|
||||
adapter.setProperty("dest", daddr)
|
||||
adapter.setProperty("dport", str(dport))
|
||||
self._session.machine.saveSettings()
|
||||
break
|
||||
except Exception as e:
|
||||
# usually due to COM Error: "The object is not ready"
|
||||
log.warn("cannot create UDP tunnel for {}: {}".format(self._vmname, e))
|
||||
last_exception = e
|
||||
time.sleep(0.75)
|
||||
continue
|
||||
|
||||
def _delete_udp(self, adapter_id):
|
||||
|
||||
if self._machine.state >= self._vboxmanager.constants.MachineState_FirstOnline and \
|
||||
self._machine.state <= self._vboxmanager.constants.MachineState_LastOnline:
|
||||
# the machine is being executed
|
||||
retries = 4
|
||||
last_exception = None
|
||||
for retry in range(retries):
|
||||
if retry == (retries - 1):
|
||||
raise VirtualBoxError("Could not delete an UDP tunnel after 4 retries :{}".format(last_exception))
|
||||
try:
|
||||
adapter = self._session.machine.getNetworkAdapter(adapter_id)
|
||||
adapter.attachmentType = self._vboxmanager.constants.NetworkAttachmentType_Null
|
||||
adapter.cableConnected = False
|
||||
self._session.machine.saveSettings()
|
||||
break
|
||||
except Exception as e:
|
||||
# usually due to COM Error: "The object is not ready"
|
||||
log.debug("cannot delete UDP tunnel for {}: {}".format(self._vmname, e))
|
||||
last_exception = e
|
||||
time.sleep(0.75)
|
||||
continue
|
||||
|
||||
def _get_pipe_name(self):
|
||||
|
||||
p = re.compile('\s+', re.UNICODE)
|
||||
pipe_name = p.sub("_", self._vmname)
|
||||
if sys.platform.startswith('win'):
|
||||
pipe_name = r"\\.\pipe\VBOX\{}".format(pipe_name)
|
||||
else:
|
||||
pipe_name = os.path.join(tempfile.gettempdir(), "pipe_{}".format(pipe_name))
|
||||
return pipe_name
|
||||
|
||||
def _set_console_options(self):
|
||||
|
||||
log.info("setting console options for {}".format(self.vmname))
|
||||
|
||||
self._lock_machine()
|
||||
pipe_name = self._get_pipe_name()
|
||||
|
||||
try:
|
||||
serial_port = self._session.machine.getSerialPort(0)
|
||||
serial_port.enabled = True
|
||||
serial_port.path = pipe_name
|
||||
serial_port.hostMode = 1
|
||||
serial_port.server = True
|
||||
self._session.machine.saveSettings()
|
||||
except Exception as e:
|
||||
raise VirtualBoxError("VirtualBox error: {}".format(e))
|
||||
|
||||
self._unlock_machine()
|
||||
|
||||
def _launch_vm_process(self):
|
||||
|
||||
log.debug("launching VM {}".format(self._vmname))
|
||||
# this command is retried several times, because it fails more often...
|
||||
retries = 4
|
||||
last_exception = None
|
||||
for retry in range(retries):
|
||||
if retry == (retries - 1):
|
||||
raise VirtualBoxError("Could not launch the VM after 4 retries: {}".format(last_exception))
|
||||
try:
|
||||
if self._headless:
|
||||
mode = "headless"
|
||||
else:
|
||||
mode = "gui"
|
||||
log.info("starting {} in {} mode".format(self._vmname, mode))
|
||||
progress = self._machine.launchVMProcess(self._session, mode, "")
|
||||
break
|
||||
except Exception as e:
|
||||
# This will usually happen if you try to start the same VM twice,
|
||||
# but may happen on loaded hosts too...
|
||||
log.warn("cannot launch VM {}, retrying {}: {}".format(self._vmname, retry + 1, e))
|
||||
last_exception = e
|
||||
time.sleep(0.6)
|
||||
continue
|
||||
|
||||
try:
|
||||
progress.waitForCompletion(-1)
|
||||
except Exception as e:
|
||||
raise VirtualBoxError("VirtualBox error: {}".format(e))
|
||||
|
||||
return progress
|
||||
|
||||
def _lock_machine(self):
|
||||
|
||||
log.debug("locking machine for {}".format(self._vmname))
|
||||
# this command is retried several times, because it fails more often...
|
||||
retries = 4
|
||||
last_exception = None
|
||||
for retry in range(retries):
|
||||
if retry == (retries - 1):
|
||||
raise VirtualBoxError("Could not lock the machine after 4 retries: {}".format(last_exception))
|
||||
try:
|
||||
self._machine.lockMachine(self._session, 1)
|
||||
break
|
||||
except Exception as e:
|
||||
log.warn("cannot lock the machine for {}, retrying {}: {}".format(self._vmname, retry + 1, e))
|
||||
last_exception = e
|
||||
time.sleep(1)
|
||||
continue
|
||||
|
||||
def _unlock_machine(self):
|
||||
|
||||
log.debug("unlocking machine for {}".format(self._vmname))
|
||||
# this command is retried several times, because it fails more often...
|
||||
retries = 4
|
||||
last_exception = None
|
||||
for retry in range(retries):
|
||||
if retry == (retries - 1):
|
||||
raise VirtualBoxError("Could not unlock the machine after 4 retries: {}".format(last_exception))
|
||||
try:
|
||||
self._session.unlockMachine()
|
||||
break
|
||||
except Exception as e:
|
||||
log.warn("cannot unlock the machine for {}, retrying {}: {}".format(self._vmname, retry + 1, e))
|
||||
time.sleep(1)
|
||||
last_exception = e
|
||||
continue
|
||||
|
@ -23,5 +23,5 @@
|
||||
# or negative for a release candidate or beta (after the base version
|
||||
# number has been incremented)
|
||||
|
||||
__version__ = "1.0beta2.dev1"
|
||||
__version__ = "1.0beta2.dev2"
|
||||
__version_info__ = (1, 0, 0, -99)
|
||||
|
Loading…
Reference in New Issue
Block a user