mirror of
https://github.com/GNS3/gns3-server
synced 2024-11-28 11:18:11 +00:00
Base for VirtualBox support.
This commit is contained in:
parent
88e03ae312
commit
0ef727ce4b
@ -19,9 +19,9 @@ import sys
|
||||
from .base import IModule
|
||||
from .dynamips import Dynamips
|
||||
from .vpcs import VPCS
|
||||
from .virtualbox import VirtualBox
|
||||
|
||||
MODULES = [Dynamips]
|
||||
MODULES.append(VPCS)
|
||||
MODULES = [Dynamips, VPCS, VirtualBox]
|
||||
|
||||
if sys.platform.startswith("linux"):
|
||||
# IOU runs only on Linux
|
||||
|
540
gns3server/modules/virtualbox/__init__.py
Normal file
540
gns3server/modules/virtualbox/__init__.py
Normal file
@ -0,0 +1,540 @@
|
||||
# -*- 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/>.
|
||||
|
||||
"""
|
||||
VirtualBox server module.
|
||||
"""
|
||||
|
||||
import os
|
||||
import base64
|
||||
import socket
|
||||
import shutil
|
||||
|
||||
from gns3server.modules import IModule
|
||||
from gns3server.config import Config
|
||||
from .virtualbox_vm import VirtualBoxVM
|
||||
from .virtualbox_error import VirtualBoxError
|
||||
from .nios.nio_udp import NIO_UDP
|
||||
from ..attic import find_unused_port
|
||||
|
||||
#from .schemas import VBOX_CREATE_SCHEMA
|
||||
#from .schemas import VBOX_DELETE_SCHEMA
|
||||
#from .schemas import VBOX_UPDATE_SCHEMA
|
||||
#from .schemas import VBOX_START_SCHEMA
|
||||
#from .schemas import VBOX_STOP_SCHEMA
|
||||
#from .schemas import VBOX_RELOAD_SCHEMA
|
||||
#from .schemas import VBOX_ALLOCATE_UDP_PORT_SCHEMA
|
||||
#from .schemas import VBOX_ADD_NIO_SCHEMA
|
||||
#from .schemas import VBOX_DELETE_NIO_SCHEMA
|
||||
|
||||
import logging
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class VirtualBox(IModule):
|
||||
"""
|
||||
VirtualBox module.
|
||||
|
||||
:param name: module name
|
||||
:param args: arguments for the module
|
||||
:param kwargs: named arguments for the module
|
||||
"""
|
||||
|
||||
def __init__(self, name, *args, **kwargs):
|
||||
|
||||
# a new process start when calling IModule
|
||||
IModule.__init__(self, name, *args, **kwargs)
|
||||
self._vbox_instances = {}
|
||||
|
||||
config = Config.instance()
|
||||
vbox_config = config.get_section_config(name.upper())
|
||||
self._console_start_port_range = vbox_config.get("console_start_port_range", 3501)
|
||||
self._console_end_port_range = vbox_config.get("console_end_port_range", 4000)
|
||||
self._allocated_udp_ports = []
|
||||
self._udp_start_port_range = vbox_config.get("udp_start_port_range", 35001)
|
||||
self._udp_end_port_range = vbox_config.get("udp_end_port_range", 35500)
|
||||
self._host = kwargs["host"]
|
||||
self._projects_dir = kwargs["projects_dir"]
|
||||
self._tempdir = kwargs["temp_dir"]
|
||||
self._working_dir = self._projects_dir
|
||||
|
||||
def stop(self, signum=None):
|
||||
"""
|
||||
Properly stops the module.
|
||||
|
||||
:param signum: signal number (if called by the signal handler)
|
||||
"""
|
||||
|
||||
# delete all VirtualBox instances
|
||||
for vbox_id in self._vbox_instances:
|
||||
vbox_instance = self._vbox_instances[vbox_id]
|
||||
vbox_instance.delete()
|
||||
|
||||
IModule.stop(self, signum) # this will stop the I/O loop
|
||||
|
||||
def get_vbox_instance(self, vbox_id):
|
||||
"""
|
||||
Returns a VirtualBox VM instance.
|
||||
|
||||
:param vbox_id: VirtualBox device identifier
|
||||
|
||||
:returns: VBoxDevice instance
|
||||
"""
|
||||
|
||||
if vbox_id not in self._vbox_instances:
|
||||
log.debug("VirtualBox VM ID {} doesn't exist".format(vbox_id), exc_info=1)
|
||||
self.send_custom_error("VirtualBox VM ID {} doesn't exist".format(vbox_id))
|
||||
return None
|
||||
return self._vbox_instances[vbox_id]
|
||||
|
||||
@IModule.route("virtualbox.reset")
|
||||
def reset(self, request):
|
||||
"""
|
||||
Resets the module.
|
||||
|
||||
:param request: JSON request
|
||||
"""
|
||||
|
||||
# delete all VirtualBox instances
|
||||
for vbox_id in self._vbox_instances:
|
||||
vbox_instance = self._vbox_instances[vbox_id]
|
||||
vbox_instance.delete()
|
||||
|
||||
# resets the instance IDs
|
||||
VirtualBoxVM.reset()
|
||||
|
||||
self._vbox_instances.clear()
|
||||
self._allocated_udp_ports.clear()
|
||||
|
||||
log.info("VirtualBox module has been reset")
|
||||
|
||||
@IModule.route("virtualbox.settings")
|
||||
def settings(self, request):
|
||||
"""
|
||||
Set or update settings.
|
||||
|
||||
Optional request parameters:
|
||||
- working_dir (path to a working directory)
|
||||
- project_name
|
||||
- console_start_port_range
|
||||
- console_end_port_range
|
||||
- udp_start_port_range
|
||||
- udp_end_port_range
|
||||
|
||||
:param request: JSON request
|
||||
"""
|
||||
|
||||
if request is None:
|
||||
self.send_param_error()
|
||||
return
|
||||
|
||||
if "working_dir" in request:
|
||||
new_working_dir = request["working_dir"]
|
||||
log.info("this server is local with working directory path to {}".format(new_working_dir))
|
||||
else:
|
||||
new_working_dir = os.path.join(self._projects_dir, request["project_name"])
|
||||
log.info("this server is remote with working directory path to {}".format(new_working_dir))
|
||||
if self._projects_dir != self._working_dir != new_working_dir:
|
||||
if not os.path.isdir(new_working_dir):
|
||||
try:
|
||||
shutil.move(self._working_dir, new_working_dir)
|
||||
except OSError as e:
|
||||
log.error("could not move working directory from {} to {}: {}".format(self._working_dir,
|
||||
new_working_dir,
|
||||
e))
|
||||
return
|
||||
|
||||
# update the working directory if it has changed
|
||||
if self._working_dir != new_working_dir:
|
||||
self._working_dir = new_working_dir
|
||||
for vbox_id in self._vbox_instances:
|
||||
vbox_instance = self._vbox_instances[vbox_id]
|
||||
vbox_instance.working_dir = os.path.join(self._working_dir, "vbox", "vm-{}".format(vbox_instance.id))
|
||||
|
||||
if "console_start_port_range" in request and "console_end_port_range" in request:
|
||||
self._console_start_port_range = request["console_start_port_range"]
|
||||
self._console_end_port_range = request["console_end_port_range"]
|
||||
|
||||
if "udp_start_port_range" in request and "udp_end_port_range" in request:
|
||||
self._udp_start_port_range = request["udp_start_port_range"]
|
||||
self._udp_end_port_range = request["udp_end_port_range"]
|
||||
|
||||
log.debug("received request {}".format(request))
|
||||
|
||||
@IModule.route("virtualbox.create")
|
||||
def vbox_create(self, request):
|
||||
"""
|
||||
Creates a new VirtualBox VM instance.
|
||||
|
||||
Mandatory request parameters:
|
||||
- name (VirtualBox VM name)
|
||||
|
||||
Optional request parameters:
|
||||
- console (VirtualBox VM console port)
|
||||
|
||||
Response parameters:
|
||||
- id (VirtualBox VM instance identifier)
|
||||
- name (VirtualBox VM name)
|
||||
- default settings
|
||||
|
||||
:param request: JSON request
|
||||
"""
|
||||
|
||||
# validate the request
|
||||
#if not self.validate_request(request, VBOX_CREATE_SCHEMA):
|
||||
# return
|
||||
|
||||
name = request["name"]
|
||||
console = request.get("console")
|
||||
vbox_id = request.get("vbox_id")
|
||||
|
||||
try:
|
||||
|
||||
vbox_instance = VirtualBoxVM(name,
|
||||
self._working_dir,
|
||||
self._host,
|
||||
vbox_id,
|
||||
console,
|
||||
self._console_start_port_range,
|
||||
self._console_end_port_range)
|
||||
|
||||
except VirtualBoxError as e:
|
||||
self.send_custom_error(str(e))
|
||||
return
|
||||
|
||||
response = {"name": vbox_instance.name,
|
||||
"id": vbox_instance.id}
|
||||
|
||||
defaults = vbox_instance.defaults()
|
||||
response.update(defaults)
|
||||
self._vbox_instances[vbox_instance.id] = vbox_instance
|
||||
self.send_response(response)
|
||||
|
||||
@IModule.route("virtualbox.delete")
|
||||
def vbox_delete(self, request):
|
||||
"""
|
||||
Deletes a VirtualBox VM instance.
|
||||
|
||||
Mandatory request parameters:
|
||||
- id (VirtualBox VM instance identifier)
|
||||
|
||||
Response parameter:
|
||||
- True on success
|
||||
|
||||
:param request: JSON request
|
||||
"""
|
||||
|
||||
# validate the request
|
||||
#if not self.validate_request(request, VBOX_DELETE_SCHEMA):
|
||||
# return
|
||||
|
||||
# get the instance
|
||||
vbox_instance = self.get_vbox_instance(request["id"])
|
||||
if not vbox_instance:
|
||||
return
|
||||
|
||||
try:
|
||||
vbox_instance.clean_delete()
|
||||
del self._vbox_instances[request["id"]]
|
||||
except VirtualBoxError as e:
|
||||
self.send_custom_error(str(e))
|
||||
return
|
||||
|
||||
self.send_response(True)
|
||||
|
||||
@IModule.route("virtualbox.update")
|
||||
def vbox_update(self, request):
|
||||
"""
|
||||
Updates a VirtualBox VM instance
|
||||
|
||||
Mandatory request parameters:
|
||||
- id (VirtualBox VM instance identifier)
|
||||
|
||||
Optional request parameters:
|
||||
- any setting to update
|
||||
|
||||
Response parameters:
|
||||
- updated settings
|
||||
|
||||
:param request: JSON request
|
||||
"""
|
||||
|
||||
# validate the request
|
||||
#if not self.validate_request(request, VBOX_UPDATE_SCHEMA):
|
||||
# return
|
||||
|
||||
# get the instance
|
||||
vbox_instance = self.get_vbox_instance(request["id"])
|
||||
if not vbox_instance:
|
||||
return
|
||||
|
||||
# update the VirtualBox VM settings
|
||||
response = {}
|
||||
for name, value in request.items():
|
||||
if hasattr(vbox_instance, name) and getattr(vbox_instance, name) != value:
|
||||
try:
|
||||
setattr(vbox_instance, name, value)
|
||||
response[name] = value
|
||||
except VirtualBoxError as e:
|
||||
self.send_custom_error(str(e))
|
||||
return
|
||||
|
||||
self.send_response(response)
|
||||
|
||||
@IModule.route("virtualbox.start")
|
||||
def vbox_start(self, request):
|
||||
"""
|
||||
Starts a VirtualBox VM instance.
|
||||
|
||||
Mandatory request parameters:
|
||||
- id (VirtualBox VM instance identifier)
|
||||
|
||||
Response parameters:
|
||||
- True on success
|
||||
|
||||
:param request: JSON request
|
||||
"""
|
||||
|
||||
# validate the request
|
||||
#if not self.validate_request(request, VBOX_START_SCHEMA):
|
||||
# return
|
||||
|
||||
# get the instance
|
||||
vbox_instance = self.get_vbox_instance(request["id"])
|
||||
if not vbox_instance:
|
||||
return
|
||||
|
||||
try:
|
||||
vbox_instance.start()
|
||||
except VirtualBoxError as e:
|
||||
self.send_custom_error(str(e))
|
||||
return
|
||||
self.send_response(True)
|
||||
|
||||
@IModule.route("virtualbox.stop")
|
||||
def vbox_stop(self, request):
|
||||
"""
|
||||
Stops a VirtualBox VM instance.
|
||||
|
||||
Mandatory request parameters:
|
||||
- id (VirtualBox VM instance identifier)
|
||||
|
||||
Response parameters:
|
||||
- True on success
|
||||
|
||||
:param request: JSON request
|
||||
"""
|
||||
|
||||
# validate the request
|
||||
#if not self.validate_request(request, VBOX_STOP_SCHEMA):
|
||||
# return
|
||||
|
||||
# get the instance
|
||||
vbox_instance = self.get_vbox_instance(request["id"])
|
||||
if not vbox_instance:
|
||||
return
|
||||
|
||||
try:
|
||||
vbox_instance.stop()
|
||||
except VirtualBoxError as e:
|
||||
self.send_custom_error(str(e))
|
||||
return
|
||||
self.send_response(True)
|
||||
|
||||
@IModule.route("virtualbox.reload")
|
||||
def vbox_reload(self, request):
|
||||
"""
|
||||
Reloads a VirtualBox VM instance.
|
||||
|
||||
Mandatory request parameters:
|
||||
- id (VirtualBox VM identifier)
|
||||
|
||||
Response parameters:
|
||||
- True on success
|
||||
|
||||
:param request: JSON request
|
||||
"""
|
||||
|
||||
# validate the request
|
||||
#if not self.validate_request(request, VBOX_RELOAD_SCHEMA):
|
||||
# return
|
||||
|
||||
# get the instance
|
||||
vbox_instance = self.get_vpcs_instance(request["id"])
|
||||
if not vbox_instance:
|
||||
return
|
||||
|
||||
try:
|
||||
if vbox_instance.is_running():
|
||||
vbox_instance.stop()
|
||||
vbox_instance.start()
|
||||
except VirtualBoxError as e:
|
||||
self.send_custom_error(str(e))
|
||||
return
|
||||
self.send_response(True)
|
||||
|
||||
@IModule.route("virtualbox.allocate_udp_port")
|
||||
def allocate_udp_port(self, request):
|
||||
"""
|
||||
Allocates a UDP port in order to create an UDP NIO.
|
||||
|
||||
Mandatory request parameters:
|
||||
- id (VirtualBox VM identifier)
|
||||
- port_id (unique port identifier)
|
||||
|
||||
Response parameters:
|
||||
- port_id (unique port identifier)
|
||||
- lport (allocated local port)
|
||||
|
||||
:param request: JSON request
|
||||
"""
|
||||
|
||||
# validate the request
|
||||
#if not self.validate_request(request, VBOX_ALLOCATE_UDP_PORT_SCHEMA):
|
||||
# return
|
||||
|
||||
# get the instance
|
||||
vbox_instance = self.get_vbox_instance(request["id"])
|
||||
if not vbox_instance:
|
||||
return
|
||||
|
||||
try:
|
||||
port = find_unused_port(self._udp_start_port_range,
|
||||
self._udp_end_port_range,
|
||||
host=self._host,
|
||||
socket_type="UDP",
|
||||
ignore_ports=self._allocated_udp_ports)
|
||||
except Exception as e:
|
||||
self.send_custom_error(str(e))
|
||||
return
|
||||
|
||||
self._allocated_udp_ports.append(port)
|
||||
log.info("{} [id={}] has allocated UDP port {} with host {}".format(vbox_instance.name,
|
||||
vbox_instance.id,
|
||||
port,
|
||||
self._host))
|
||||
|
||||
response = {"lport": port,
|
||||
"port_id": request["port_id"]}
|
||||
self.send_response(response)
|
||||
|
||||
@IModule.route("virtualbox.add_nio")
|
||||
def add_nio(self, request):
|
||||
"""
|
||||
Adds an NIO (Network Input/Output) for a VirtualBox VM instance.
|
||||
|
||||
Mandatory request parameters:
|
||||
- id (VirtualBox VM instance identifier)
|
||||
- port (port number)
|
||||
- port_id (unique port identifier)
|
||||
- nio (one of the following)
|
||||
- type "nio_udp"
|
||||
- lport (local port)
|
||||
- rhost (remote host)
|
||||
- rport (remote port)
|
||||
|
||||
Response parameters:
|
||||
- port_id (unique port identifier)
|
||||
|
||||
:param request: JSON request
|
||||
"""
|
||||
|
||||
# validate the request
|
||||
#if not self.validate_request(request, VBOX_ADD_NIO_SCHEMA):
|
||||
# return
|
||||
|
||||
# get the instance
|
||||
vbox_instance = self.get_vbox_instance(request["id"])
|
||||
if not vbox_instance:
|
||||
return
|
||||
|
||||
port = request["port"]
|
||||
try:
|
||||
nio = None
|
||||
if request["nio"]["type"] == "nio_udp":
|
||||
lport = request["nio"]["lport"]
|
||||
rhost = request["nio"]["rhost"]
|
||||
rport = request["nio"]["rport"]
|
||||
try:
|
||||
#TODO: handle IPv6
|
||||
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as sock:
|
||||
sock.connect((rhost, rport))
|
||||
except OSError as e:
|
||||
raise VirtualBoxError("Could not create an UDP connection to {}:{}: {}".format(rhost, rport, e))
|
||||
nio = NIO_UDP(lport, rhost, rport)
|
||||
if not nio:
|
||||
raise VirtualBoxError("Requested NIO does not exist or is not supported: {}".format(request["nio"]["type"]))
|
||||
except VirtualBoxError as e:
|
||||
self.send_custom_error(str(e))
|
||||
return
|
||||
|
||||
try:
|
||||
vbox_instance.port_add_nio_binding(port, nio)
|
||||
except VirtualBoxError as e:
|
||||
self.send_custom_error(str(e))
|
||||
return
|
||||
|
||||
self.send_response({"port_id": request["port_id"]})
|
||||
|
||||
@IModule.route("virtualbox.delete_nio")
|
||||
def delete_nio(self, request):
|
||||
"""
|
||||
Deletes an NIO (Network Input/Output).
|
||||
|
||||
Mandatory request parameters:
|
||||
- id (VPCS instance identifier)
|
||||
- port (port identifier)
|
||||
|
||||
Response parameters:
|
||||
- True on success
|
||||
|
||||
:param request: JSON request
|
||||
"""
|
||||
|
||||
# validate the request
|
||||
#if not self.validate_request(request, VBOX_DELETE_NIO_SCHEMA):
|
||||
# return
|
||||
|
||||
# get the instance
|
||||
vbox_instance = self.get_vbox_instance(request["id"])
|
||||
if not vbox_instance:
|
||||
return
|
||||
|
||||
port = request["port"]
|
||||
try:
|
||||
nio = vbox_instance.port_remove_nio_binding(port)
|
||||
if isinstance(nio, NIO_UDP) and nio.lport in self._allocated_udp_ports:
|
||||
self._allocated_udp_ports.remove(nio.lport)
|
||||
except VirtualBoxError as e:
|
||||
self.send_custom_error(str(e))
|
||||
return
|
||||
|
||||
self.send_response(True)
|
||||
|
||||
@IModule.route("virtualbox.echo")
|
||||
def echo(self, request):
|
||||
"""
|
||||
Echo end point for testing purposes.
|
||||
|
||||
:param request: JSON request
|
||||
"""
|
||||
|
||||
if request is None:
|
||||
self.send_param_error()
|
||||
else:
|
||||
log.debug("received request {}".format(request))
|
||||
self.send_response(request)
|
0
gns3server/modules/virtualbox/adapters/__init__.py
Normal file
0
gns3server/modules/virtualbox/adapters/__init__.py
Normal file
104
gns3server/modules/virtualbox/adapters/adapter.py
Normal file
104
gns3server/modules/virtualbox/adapters/adapter.py
Normal file
@ -0,0 +1,104 @@
|
||||
# -*- 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/>.
|
||||
|
||||
|
||||
class Adapter(object):
|
||||
"""
|
||||
Base class for adapters.
|
||||
|
||||
:param interfaces: number of interfaces supported by this adapter.
|
||||
"""
|
||||
|
||||
def __init__(self, interfaces=1):
|
||||
|
||||
self._interfaces = interfaces
|
||||
|
||||
self._ports = {}
|
||||
for port_id in range(0, interfaces):
|
||||
self._ports[port_id] = None
|
||||
|
||||
def removable(self):
|
||||
"""
|
||||
Returns True if the adapter can be removed from a slot
|
||||
and False if not.
|
||||
|
||||
:returns: boolean
|
||||
"""
|
||||
|
||||
return True
|
||||
|
||||
def port_exists(self, port_id):
|
||||
"""
|
||||
Checks if a port exists on this adapter.
|
||||
|
||||
:returns: True is the port exists,
|
||||
False otherwise.
|
||||
"""
|
||||
|
||||
if port_id in self._ports:
|
||||
return True
|
||||
return False
|
||||
|
||||
def add_nio(self, port_id, nio):
|
||||
"""
|
||||
Adds a NIO to a port on this adapter.
|
||||
|
||||
:param port_id: port ID (integer)
|
||||
:param nio: NIO instance
|
||||
"""
|
||||
|
||||
self._ports[port_id] = nio
|
||||
|
||||
def remove_nio(self, port_id):
|
||||
"""
|
||||
Removes a NIO from a port on this adapter.
|
||||
|
||||
:param port_id: port ID (integer)
|
||||
"""
|
||||
|
||||
self._ports[port_id] = None
|
||||
|
||||
def get_nio(self, port_id):
|
||||
"""
|
||||
Returns the NIO assigned to a port.
|
||||
|
||||
:params port_id: port ID (integer)
|
||||
|
||||
:returns: NIO instance
|
||||
"""
|
||||
|
||||
return self._ports[port_id]
|
||||
|
||||
@property
|
||||
def ports(self):
|
||||
"""
|
||||
Returns port to NIO mapping
|
||||
|
||||
:returns: dictionary port -> NIO
|
||||
"""
|
||||
|
||||
return self._ports
|
||||
|
||||
@property
|
||||
def interfaces(self):
|
||||
"""
|
||||
Returns the number of interfaces supported by this adapter.
|
||||
|
||||
:returns: number of interfaces
|
||||
"""
|
||||
|
||||
return self._interfaces
|
31
gns3server/modules/virtualbox/adapters/ethernet_adapter.py
Normal file
31
gns3server/modules/virtualbox/adapters/ethernet_adapter.py
Normal file
@ -0,0 +1,31 @@
|
||||
# -*- 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/>.
|
||||
|
||||
from .adapter import Adapter
|
||||
|
||||
|
||||
class EthernetAdapter(Adapter):
|
||||
"""
|
||||
VirtualBox Ethernet adapter.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
Adapter.__init__(self, interfaces=1)
|
||||
|
||||
def __str__(self):
|
||||
|
||||
return "VirtualBox Ethernet adapter"
|
0
gns3server/modules/virtualbox/nios/__init__.py
Normal file
0
gns3server/modules/virtualbox/nios/__init__.py
Normal file
72
gns3server/modules/virtualbox/nios/nio_udp.py
Normal file
72
gns3server/modules/virtualbox/nios/nio_udp.py
Normal file
@ -0,0 +1,72 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2013 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/>.
|
||||
|
||||
"""
|
||||
Interface for UDP NIOs.
|
||||
"""
|
||||
|
||||
|
||||
class NIO_UDP(object):
|
||||
"""
|
||||
IOU UDP NIO.
|
||||
|
||||
:param lport: local port number
|
||||
:param rhost: remote address/host
|
||||
:param rport: remote port number
|
||||
"""
|
||||
|
||||
_instance_count = 0
|
||||
|
||||
def __init__(self, lport, rhost, rport):
|
||||
|
||||
self._lport = lport
|
||||
self._rhost = rhost
|
||||
self._rport = rport
|
||||
|
||||
@property
|
||||
def lport(self):
|
||||
"""
|
||||
Returns the local port
|
||||
|
||||
:returns: local port number
|
||||
"""
|
||||
|
||||
return self._lport
|
||||
|
||||
@property
|
||||
def rhost(self):
|
||||
"""
|
||||
Returns the remote host
|
||||
|
||||
:returns: remote address/host
|
||||
"""
|
||||
|
||||
return self._rhost
|
||||
|
||||
@property
|
||||
def rport(self):
|
||||
"""
|
||||
Returns the remote port
|
||||
|
||||
:returns: remote port number
|
||||
"""
|
||||
|
||||
return self._rport
|
||||
|
||||
def __str__(self):
|
||||
|
||||
return "NIO UDP"
|
357
gns3server/modules/virtualbox/vboxwrapper_client.py
Normal file
357
gns3server/modules/virtualbox/vboxwrapper_client.py
Normal file
@ -0,0 +1,357 @@
|
||||
# -*- 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/>.
|
||||
|
||||
"""
|
||||
Client to VirtualBox wrapper.
|
||||
"""
|
||||
|
||||
import os
|
||||
import time
|
||||
import subprocess
|
||||
import tempfile
|
||||
import socket
|
||||
import re
|
||||
|
||||
from .virtualbox_error import VirtualBoxError
|
||||
|
||||
import logging
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class VboxWrapperClient(object):
|
||||
"""
|
||||
VirtualBox Wrapper client.
|
||||
|
||||
:param path: path to VirtualBox wrapper executable
|
||||
:param working_dir: working directory
|
||||
:param port: port
|
||||
:param host: host/address
|
||||
"""
|
||||
|
||||
# Used to parse the VirtualBox wrapper response codes
|
||||
error_re = re.compile(r"""^2[0-9]{2}-""")
|
||||
success_re = re.compile(r"""^1[0-9]{2}\s{1}""")
|
||||
|
||||
def __init__(self, path, working_dir, host, port=11525, timeout=30.0):
|
||||
|
||||
self._path = path
|
||||
self._command = []
|
||||
self._process = None
|
||||
self._stdout_file = ""
|
||||
self._started = False
|
||||
self._host = host
|
||||
self._port = port
|
||||
self._timeout = timeout
|
||||
self._socket = None
|
||||
|
||||
@property
|
||||
def started(self):
|
||||
"""
|
||||
Returns either VirtualBox wrapper has been started or not.
|
||||
|
||||
:returns: boolean
|
||||
"""
|
||||
|
||||
return self._started
|
||||
|
||||
@property
|
||||
def path(self):
|
||||
"""
|
||||
Returns the path to the VirtualBox wrapper executable.
|
||||
|
||||
:returns: path to VirtualBox wrapper
|
||||
"""
|
||||
|
||||
return self._path
|
||||
|
||||
@path.setter
|
||||
def path(self, path):
|
||||
"""
|
||||
Sets the path to the VirtualBox wrapper executable.
|
||||
|
||||
:param path: path to VirtualBox wrapper
|
||||
"""
|
||||
|
||||
self._path = path
|
||||
|
||||
@property
|
||||
def port(self):
|
||||
"""
|
||||
Returns the port used to start the VirtualBox wrapper.
|
||||
|
||||
:returns: port number (integer)
|
||||
"""
|
||||
|
||||
return self._port
|
||||
|
||||
@port.setter
|
||||
def port(self, port):
|
||||
"""
|
||||
Sets the port used to start the VirtualBox wrapper.
|
||||
|
||||
:param port: port number (integer)
|
||||
"""
|
||||
|
||||
self._port = port
|
||||
|
||||
@property
|
||||
def host(self):
|
||||
"""
|
||||
Returns the host (binding) used to start the VirtualBox wrapper.
|
||||
|
||||
:returns: host/address (string)
|
||||
"""
|
||||
|
||||
return self._host
|
||||
|
||||
@host.setter
|
||||
def host(self, host):
|
||||
"""
|
||||
Sets the host (binding) used to start the VirtualBox wrapper.
|
||||
|
||||
:param host: host/address (string)
|
||||
"""
|
||||
|
||||
self._host = host
|
||||
|
||||
def start(self):
|
||||
"""
|
||||
Starts the VirtualBox wrapper process.
|
||||
"""
|
||||
|
||||
self._command = self._build_command()
|
||||
try:
|
||||
log.info("starting VirtualBox wrapper: {}".format(self._command))
|
||||
with tempfile.NamedTemporaryFile(delete=False) as fd:
|
||||
self._stdout_file = fd.name
|
||||
log.info("VirtualBox wrapper process logging to {}".format(fd.name))
|
||||
self._process = subprocess.Popen(self._command,
|
||||
stdout=fd,
|
||||
stderr=subprocess.STDOUT,
|
||||
cwd=self._working_dir)
|
||||
log.info("VirtualBox wrapper started PID={}".format(self._process.pid))
|
||||
self._started = True
|
||||
except OSError as e:
|
||||
log.error("could not start VirtualBox wrapper: {}".format(e))
|
||||
raise VirtualBoxError("could not start VirtualBox wrapper: {}".format(e))
|
||||
|
||||
def stop(self):
|
||||
"""
|
||||
Stops the VirtualBox wrapper process.
|
||||
"""
|
||||
|
||||
if self.is_running():
|
||||
self.send("hypervisor stop")
|
||||
self._socket.shutdown(socket.SHUT_RDWR)
|
||||
self._socket.close()
|
||||
self._socket = None
|
||||
log.info("stopping VirtualBox wrapper PID={}".format(self._process.pid))
|
||||
try:
|
||||
# give some time for the VirtualBox wrapper to properly stop.
|
||||
time.sleep(0.01)
|
||||
self._process.terminate()
|
||||
self._process.wait(1)
|
||||
except subprocess.TimeoutExpired:
|
||||
self._process.kill()
|
||||
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):
|
||||
try:
|
||||
os.remove(self._stdout_file)
|
||||
except OSError as e:
|
||||
log.warning("could not delete temporary VirtualBox wrapper log file: {}".format(e))
|
||||
self._started = False
|
||||
|
||||
def read_stdout(self):
|
||||
"""
|
||||
Reads the standard 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):
|
||||
try:
|
||||
with open(self._stdout_file, errors="replace") as file:
|
||||
output = file.read()
|
||||
except OSError as e:
|
||||
log.warn("could not read {}: {}".format(self._stdout_file, e))
|
||||
return output
|
||||
|
||||
def is_running(self):
|
||||
"""
|
||||
Checks if the process is running
|
||||
|
||||
:returns: True or False
|
||||
"""
|
||||
|
||||
if self._process and self._process.poll() is None:
|
||||
return True
|
||||
return False
|
||||
|
||||
def _build_command(self):
|
||||
"""
|
||||
Command to start the VirtualBox wrapper process.
|
||||
(to be passed to subprocess.Popen())
|
||||
"""
|
||||
|
||||
command = [self._path]
|
||||
#if self._host != "0.0.0.0" and self._host != "::":
|
||||
# command.extend(["-H", "{}:{}".format(self._host, self._port)])
|
||||
#else:
|
||||
# command.extend(["-H", str(self._port)])
|
||||
return command
|
||||
|
||||
def connect(self):
|
||||
"""
|
||||
Connects to the VirtualBox wrapper.
|
||||
"""
|
||||
|
||||
# connect to a local address by default
|
||||
# if listening to all addresses (IPv4 or IPv6)
|
||||
if self._host == "0.0.0.0":
|
||||
host = "127.0.0.1"
|
||||
elif self._host == "::":
|
||||
host = "::1"
|
||||
else:
|
||||
host = self._host
|
||||
|
||||
try:
|
||||
self._socket = socket.create_connection((host, self._port), self._timeout)
|
||||
except OSError as e:
|
||||
raise VirtualBoxError("Could not connect to the VirtualBox wrapper: {}".format(e))
|
||||
|
||||
def reset(self):
|
||||
"""
|
||||
Resets the VirtualBox wrapper (used to get an empty configuration).
|
||||
"""
|
||||
|
||||
pass
|
||||
|
||||
@property
|
||||
def working_dir(self):
|
||||
"""
|
||||
Returns current working directory
|
||||
|
||||
:returns: path to the working directory
|
||||
"""
|
||||
|
||||
return self._working_dir
|
||||
|
||||
@working_dir.setter
|
||||
def working_dir(self, working_dir):
|
||||
"""
|
||||
Sets the working directory for the VirtualBox wrapper.
|
||||
|
||||
:param working_dir: path to the working directory
|
||||
"""
|
||||
|
||||
# encase working_dir in quotes to protect spaces in the path
|
||||
#self.send("hypervisor working_dir {}".format('"' + working_dir + '"'))
|
||||
self._working_dir = working_dir
|
||||
log.debug("working directory set to {}".format(self._working_dir))
|
||||
|
||||
@property
|
||||
def socket(self):
|
||||
"""
|
||||
Returns the current socket used to communicate with the VirtualBox wrapper.
|
||||
|
||||
:returns: socket instance
|
||||
"""
|
||||
|
||||
assert self._socket
|
||||
return self._socket
|
||||
|
||||
def send(self, command):
|
||||
"""
|
||||
Sends commands to the VirtualBox wrapper.
|
||||
|
||||
:param command: a VirtualBox wrapper command
|
||||
|
||||
:returns: results as a list
|
||||
"""
|
||||
|
||||
# VirtualBox wrapper responses are of the form:
|
||||
# 1xx yyyyyy\r\n
|
||||
# 1xx yyyyyy\r\n
|
||||
# ...
|
||||
# 100-yyyy\r\n
|
||||
# or
|
||||
# 2xx-yyyy\r\n
|
||||
#
|
||||
# Where 1xx is a code from 100-199 for a success or 200-299 for an error
|
||||
# The result might be multiple lines and might be less than the buffer size
|
||||
# but still have more data. The only thing we know for sure is the last line
|
||||
# will begin with '100-' or a '2xx-' and end with '\r\n'
|
||||
|
||||
if not self._socket:
|
||||
raise VirtualBoxError("Not connected")
|
||||
|
||||
try:
|
||||
command = command.strip() + '\n'
|
||||
log.debug("sending {}".format(command))
|
||||
self.socket.sendall(command.encode('utf-8'))
|
||||
except OSError as e:
|
||||
raise VirtualBoxError("Lost communication with {host}:{port} :{error}"
|
||||
.format(host=self._host, port=self._port, error=e))
|
||||
|
||||
# Now retrieve the result
|
||||
data = []
|
||||
buf = ''
|
||||
while True:
|
||||
try:
|
||||
chunk = self.socket.recv(1024)
|
||||
buf += chunk.decode("utf-8")
|
||||
except OSError as e:
|
||||
raise VirtualBoxError("Communication timed out with {host}:{port} :{error}"
|
||||
.format(host=self._host, port=self._port, error=e))
|
||||
|
||||
# If the buffer doesn't end in '\n' then we can't be done
|
||||
try:
|
||||
if buf[-1] != '\n':
|
||||
continue
|
||||
except IndexError:
|
||||
raise VirtualBoxError("Could not communicate with {host}:{port}"
|
||||
.format(host=self._host, port=self._port))
|
||||
|
||||
data += buf.split('\r\n')
|
||||
if data[-1] == '':
|
||||
data.pop()
|
||||
buf = ''
|
||||
|
||||
if len(data) == 0:
|
||||
raise VirtualBoxError("no data returned from {host}:{port}"
|
||||
.format(host=self._host, port=self._port))
|
||||
|
||||
# Does it contain an error code?
|
||||
if self.error_re.search(data[-1]):
|
||||
raise VirtualBoxError(data[-1][4:])
|
||||
|
||||
# Or does the last line begin with '100-'? Then we are done!
|
||||
if data[-1][:4] == '100-':
|
||||
data[-1] = data[-1][4:]
|
||||
if data[-1] == 'OK':
|
||||
data.pop()
|
||||
break
|
||||
|
||||
# Remove success responses codes
|
||||
for index in range(len(data)):
|
||||
if self.success_re.search(data[index]):
|
||||
data[index] = data[index][4:]
|
||||
|
||||
log.debug("returned result {}".format(data))
|
||||
return data
|
39
gns3server/modules/virtualbox/virtualbox_error.py
Normal file
39
gns3server/modules/virtualbox/virtualbox_error.py
Normal file
@ -0,0 +1,39 @@
|
||||
# -*- 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/>.
|
||||
|
||||
"""
|
||||
Custom exceptions for VirtualBox module.
|
||||
"""
|
||||
|
||||
|
||||
class VirtualBoxError(Exception):
|
||||
|
||||
def __init__(self, message, original_exception=None):
|
||||
|
||||
Exception.__init__(self, message)
|
||||
if isinstance(message, Exception):
|
||||
message = str(message)
|
||||
self._message = message
|
||||
self._original_exception = original_exception
|
||||
|
||||
def __repr__(self):
|
||||
|
||||
return self._message
|
||||
|
||||
def __str__(self):
|
||||
|
||||
return self._message
|
318
gns3server/modules/virtualbox/virtualbox_vm.py
Normal file
318
gns3server/modules/virtualbox/virtualbox_vm.py
Normal file
@ -0,0 +1,318 @@
|
||||
# -*- 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/>.
|
||||
|
||||
"""
|
||||
VirtualBox VM instance.
|
||||
"""
|
||||
|
||||
import os
|
||||
import shutil
|
||||
|
||||
from pkg_resources import parse_version
|
||||
from .virtualbox_error import VirtualBoxError
|
||||
from .adapters.ethernet_adapter import EthernetAdapter
|
||||
from .nios.nio_udp import NIO_UDP
|
||||
from ..attic import find_unused_port
|
||||
|
||||
import logging
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class VirtualBoxVM(object):
|
||||
"""
|
||||
VirtualBox VM implementation.
|
||||
|
||||
:param name: name of this VirtualBox VM
|
||||
:param working_dir: path to a working directory
|
||||
:param host: host/address to bind for console and UDP connections
|
||||
:param vbox_id: VirtalBox VM instance ID
|
||||
:param console: TCP console port
|
||||
:param console_start_port_range: TCP console port range start
|
||||
:param console_end_port_range: TCP console port range end
|
||||
"""
|
||||
|
||||
_instances = []
|
||||
_allocated_console_ports = []
|
||||
|
||||
def __init__(self,
|
||||
name,
|
||||
path,
|
||||
working_dir,
|
||||
host="127.0.0.1",
|
||||
vbox_id=None,
|
||||
console=None,
|
||||
console_start_port_range=4512,
|
||||
console_end_port_range=5000):
|
||||
|
||||
if not vbox_id:
|
||||
self._id = 0
|
||||
for identifier in range(1, 1024):
|
||||
if identifier not in self._instances:
|
||||
self._id = identifier
|
||||
self._instances.append(self._id)
|
||||
break
|
||||
|
||||
if self._id == 0:
|
||||
raise VirtualBoxError("Maximum number of VirtualBox VM instances reached")
|
||||
else:
|
||||
if vbox_id in self._instances:
|
||||
raise VirtualBoxError("VirtualBox identifier {} is already used by another VirtualBox VM instance".format(vbox_id))
|
||||
self._id = vbox_id
|
||||
self._instances.append(self._id)
|
||||
|
||||
self._name = name
|
||||
self._console = console
|
||||
self._working_dir = None
|
||||
self._host = host
|
||||
self._command = []
|
||||
self._vboxwrapper_process = None
|
||||
self._vboxwrapper_stdout_file = ""
|
||||
self._host = "127.0.0.1"
|
||||
self._started = False
|
||||
self._console_start_port_range = console_start_port_range
|
||||
self._console_end_port_range = console_end_port_range
|
||||
|
||||
# VirtualBox settings
|
||||
self._ethernet_adapter = EthernetAdapter() # one adapter with 1 Ethernet interface
|
||||
|
||||
working_dir_path = os.path.join(working_dir, "vbox", "vm-{}".format(self._id))
|
||||
|
||||
if vbox_id and not os.path.isdir(working_dir_path):
|
||||
raise VirtualBoxError("Working directory {} doesn't exist".format(working_dir_path))
|
||||
|
||||
# create the device own working directory
|
||||
self.working_dir = working_dir_path
|
||||
|
||||
if not self._console:
|
||||
# allocate a console port
|
||||
try:
|
||||
self._console = find_unused_port(self._console_start_port_range,
|
||||
self._console_end_port_range,
|
||||
self._host,
|
||||
ignore_ports=self._allocated_console_ports)
|
||||
except Exception as e:
|
||||
raise VirtualBoxError(e)
|
||||
|
||||
if self._console in self._allocated_console_ports:
|
||||
raise VirtualBoxError("Console port {} is already used by another VirtualBox VM".format(console))
|
||||
self._allocated_console_ports.append(self._console)
|
||||
|
||||
log.info("VirtualBox VM {name} [id={id}] has been created".format(name=self._name,
|
||||
id=self._id))
|
||||
|
||||
def defaults(self):
|
||||
"""
|
||||
Returns all the default attribute values for this VirtualBox VM.
|
||||
|
||||
:returns: default values (dictionary)
|
||||
"""
|
||||
|
||||
vbox_defaults = {"name": self._name,
|
||||
"console": self._console}
|
||||
|
||||
return vbox_defaults
|
||||
|
||||
@property
|
||||
def id(self):
|
||||
"""
|
||||
Returns the unique ID for this VirtualBox VM.
|
||||
|
||||
:returns: id (integer)
|
||||
"""
|
||||
|
||||
return self._id
|
||||
|
||||
@classmethod
|
||||
def reset(cls):
|
||||
"""
|
||||
Resets allocated instance list.
|
||||
"""
|
||||
|
||||
cls._instances.clear()
|
||||
cls._allocated_console_ports.clear()
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""
|
||||
Returns the name of this VirtualBox VM.
|
||||
|
||||
:returns: name
|
||||
"""
|
||||
|
||||
return self._name
|
||||
|
||||
@name.setter
|
||||
def name(self, new_name):
|
||||
"""
|
||||
Sets the name of this VirtualBox VM.
|
||||
|
||||
:param new_name: name
|
||||
"""
|
||||
|
||||
log.info("VirtualBox VM {name} [id={id}]: renamed to {new_name}".format(name=self._name,
|
||||
id=self._id,
|
||||
new_name=new_name))
|
||||
self._name = new_name
|
||||
|
||||
@property
|
||||
def working_dir(self):
|
||||
"""
|
||||
Returns current working directory
|
||||
|
||||
:returns: path to the working directory
|
||||
"""
|
||||
|
||||
return self._working_dir
|
||||
|
||||
@working_dir.setter
|
||||
def working_dir(self, working_dir):
|
||||
"""
|
||||
Sets the working directory this VirtualBox VM.
|
||||
|
||||
:param working_dir: path to the working directory
|
||||
"""
|
||||
|
||||
try:
|
||||
os.makedirs(working_dir)
|
||||
except FileExistsError:
|
||||
pass
|
||||
except OSError as e:
|
||||
raise VirtualBoxError("Could not create working directory {}: {}".format(working_dir, e))
|
||||
|
||||
self._working_dir = working_dir
|
||||
log.info("VirtualBox VM {name} [id={id}]: working directory changed to {wd}".format(name=self._name,
|
||||
id=self._id,
|
||||
wd=self._working_dir))
|
||||
|
||||
@property
|
||||
def console(self):
|
||||
"""
|
||||
Returns the TCP console port.
|
||||
|
||||
:returns: console port (integer)
|
||||
"""
|
||||
|
||||
return self._console
|
||||
|
||||
@console.setter
|
||||
def console(self, console):
|
||||
"""
|
||||
Sets the TCP console port.
|
||||
|
||||
:param console: console port (integer)
|
||||
"""
|
||||
|
||||
if console in self._allocated_console_ports:
|
||||
raise VirtualBoxError("Console port {} is already used by another VirtualBox VM".format(console))
|
||||
|
||||
self._allocated_console_ports.remove(self._console)
|
||||
self._console = console
|
||||
self._allocated_console_ports.append(self._console)
|
||||
log.info("VirtualBox VM {name} [id={id}]: console port set to {port}".format(name=self._name,
|
||||
id=self._id,
|
||||
port=console))
|
||||
|
||||
def delete(self):
|
||||
"""
|
||||
Deletes this VirtualBox VM.
|
||||
"""
|
||||
|
||||
self.stop()
|
||||
if self._id in self._instances:
|
||||
self._instances.remove(self._id)
|
||||
|
||||
if self.console and self.console in self._allocated_console_ports:
|
||||
self._allocated_console_ports.remove(self.console)
|
||||
|
||||
log.info("VirtualBox VM {name} [id={id}] has been deleted".format(name=self._name,
|
||||
id=self._id))
|
||||
|
||||
def clean_delete(self):
|
||||
"""
|
||||
Deletes this VirtualBox VM & all files.
|
||||
"""
|
||||
|
||||
self.stop()
|
||||
if self._id in self._instances:
|
||||
self._instances.remove(self._id)
|
||||
|
||||
if self.console:
|
||||
self._allocated_console_ports.remove(self.console)
|
||||
|
||||
try:
|
||||
shutil.rmtree(self._working_dir)
|
||||
except OSError as e:
|
||||
log.error("could not delete VirtualBox VM {name} [id={id}]: {error}".format(name=self._name,
|
||||
id=self._id,
|
||||
error=e))
|
||||
return
|
||||
|
||||
log.info("VirtualBox VM {name} [id={id}] has been deleted (including associated files)".format(name=self._name,
|
||||
id=self._id))
|
||||
|
||||
def start(self):
|
||||
"""
|
||||
Starts this VirtualBox VM.
|
||||
"""
|
||||
|
||||
pass
|
||||
|
||||
def stop(self):
|
||||
"""
|
||||
Stops this VirtualBox VM.
|
||||
"""
|
||||
|
||||
pass
|
||||
|
||||
# def port_add_nio_binding(self, port_id, nio):
|
||||
# """
|
||||
# Adds a port NIO binding.
|
||||
#
|
||||
# :param port_id: port ID
|
||||
# :param nio: NIO instance to add to the slot/port
|
||||
# """
|
||||
#
|
||||
# if not self._ethernet_adapter.port_exists(port_id):
|
||||
# raise VPCSError("Port {port_id} doesn't exist in adapter {adapter}".format(adapter=self._ethernet_adapter,
|
||||
# port_id=port_id))
|
||||
#
|
||||
# self._ethernet_adapter.add_nio(port_id, nio)
|
||||
# log.info("VPCS {name} [id={id}]: {nio} added to port {port_id}".format(name=self._name,
|
||||
# id=self._id,
|
||||
# nio=nio,
|
||||
# port_id=port_id))
|
||||
|
||||
# def port_remove_nio_binding(self, port_id):
|
||||
# """
|
||||
# Removes a port NIO binding.
|
||||
#
|
||||
# :param port_id: port ID
|
||||
#
|
||||
# :returns: NIO instance
|
||||
# """
|
||||
#
|
||||
# if not self._ethernet_adapter.port_exists(port_id):
|
||||
# raise VPCSError("Port {port_id} doesn't exist in adapter {adapter}".format(adapter=self._ethernet_adapter,
|
||||
# port_id=port_id))
|
||||
#
|
||||
# nio = self._ethernet_adapter.get_nio(port_id)
|
||||
# self._ethernet_adapter.remove_nio(port_id)
|
||||
# log.info("VPCS {name} [id={id}]: {nio} removed from port {port_id}".format(name=self._name,
|
||||
# id=self._id,
|
||||
# nio=nio,
|
||||
# port_id=port_id))
|
||||
# return nio
|
@ -362,7 +362,7 @@ class VPCS(IModule):
|
||||
self.send_response(response)
|
||||
|
||||
@IModule.route("vpcs.start")
|
||||
def vm_start(self, request):
|
||||
def vpcs_start(self, request):
|
||||
"""
|
||||
Starts a VPCS instance.
|
||||
|
||||
@ -392,7 +392,7 @@ class VPCS(IModule):
|
||||
self.send_response(True)
|
||||
|
||||
@IModule.route("vpcs.stop")
|
||||
def vm_stop(self, request):
|
||||
def vpcs_stop(self, request):
|
||||
"""
|
||||
Stops a VPCS instance.
|
||||
|
||||
@ -422,7 +422,7 @@ class VPCS(IModule):
|
||||
self.send_response(True)
|
||||
|
||||
@IModule.route("vpcs.reload")
|
||||
def vm_reload(self, request):
|
||||
def vpcs_reload(self, request):
|
||||
"""
|
||||
Reloads a VPCS instance.
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user