1
0
mirror of https://github.com/GNS3/gns3-server synced 2024-11-24 09:18:08 +00:00

Fixed first round of bugs/comments from first pull request

This commit is contained in:
Joe Bowen 2014-05-13 10:17:28 -06:00
parent 588ee8eed0
commit 5926bfbd07
6 changed files with 206 additions and 220 deletions

View File

@ -18,10 +18,10 @@
import sys import sys
from .base import IModule from .base import IModule
from .dynamips import Dynamips from .dynamips import Dynamips
from .vpcs import VPCS from .vpcs import vpcs
MODULES = [Dynamips] MODULES = [Dynamips]
MODULES.append(VPCS) MODULES.append(vpcs)
if sys.platform.startswith("linux"): if sys.platform.startswith("linux"):
# IOU runs only on Linux # IOU runs only on Linux

View File

@ -16,14 +16,13 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
""" """
VPCS server module. vpcs server module.
""" """
import os import os
import sys import sys
import base64 import base64
import tempfile import tempfile
import fcntl
import struct import struct
import socket import socket
import shutil import shutil
@ -31,8 +30,8 @@ import shutil
from gns3server.modules import IModule from gns3server.modules import IModule
from gns3server.config import Config from gns3server.config import Config
import gns3server.jsonrpc as jsonrpc import gns3server.jsonrpc as jsonrpc
from .vpcs_device import VPCSDevice from .vpcs_device import vpcsDevice
from .vpcs_error import VPCSError from .vpcs_error import vpcsError
from .nios.nio_udp import NIO_UDP from .nios.nio_udp import NIO_UDP
from .nios.nio_tap import NIO_TAP from .nios.nio_tap import NIO_TAP
from ..attic import find_unused_port from ..attic import find_unused_port
@ -51,9 +50,9 @@ import logging
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
class VPCS(IModule): class vpcs(IModule):
""" """
VPCS module. vpcs module.
:param name: module name :param name: module name
:param args: arguments for the module :param args: arguments for the module
@ -62,32 +61,32 @@ class VPCS(IModule):
def __init__(self, name, *args, **kwargs): def __init__(self, name, *args, **kwargs):
# get the VPCS location # get the vpcs location
config = Config.instance() config = Config.instance()
VPCS_config = config.get_section_config(name.upper()) vpcs_config = config.get_section_config(name.upper())
self._VPCS = VPCS_config.get("vpcs") self._vpcs = vpcs_config.get("vpcs")
if not self._VPCS or not os.path.isfile(self._VPCS): if not self._vpcs or not os.path.isfile(self._vpcs):
VPCS_in_cwd = os.path.join(os.getcwd(), "vpcs") vpcs_in_cwd = os.path.join(os.getcwd(), "vpcs")
if os.path.isfile(VPCS_in_cwd): if os.path.isfile(vpcs_in_cwd):
self._VPCS = VPCS_in_cwd self._vpcs = vpcs_in_cwd
else: else:
# look for VPCS if none is defined or accessible # look for vpcs if none is defined or accessible
for path in os.environ["PATH"].split(":"): for path in os.environ["PATH"].split(":"):
try: try:
if "vpcs" in os.listdir(path) and os.access(os.path.join(path, "vpcs"), os.X_OK): if "vpcs" in os.listdir(path) and os.access(os.path.join(path, "vpcs"), os.X_OK):
self._VPCS = os.path.join(path, "vpcs") self._vpcs = os.path.join(path, "vpcs")
break break
except OSError: except OSError:
continue continue
if not self._VPCS: if not self._vpcs:
log.warning("VPCS binary couldn't be found!") log.warning("vpcs binary couldn't be found!")
elif not os.access(self._VPCS, os.X_OK): elif not os.access(self._vpcs, os.X_OK):
log.warning("VPCS is not executable") log.warning("vpcs is not executable")
# a new process start when calling IModule # a new process start when calling IModule
IModule.__init__(self, name, *args, **kwargs) IModule.__init__(self, name, *args, **kwargs)
self._VPCS_instances = {} self._vpcs_instances = {}
self._console_start_port_range = 4001 self._console_start_port_range = 4001
self._console_end_port_range = 4512 self._console_end_port_range = 4512
self._allocated_console_ports = [] self._allocated_console_ports = []
@ -99,11 +98,11 @@ class VPCS(IModule):
self._projects_dir = kwargs["projects_dir"] self._projects_dir = kwargs["projects_dir"]
self._tempdir = kwargs["temp_dir"] self._tempdir = kwargs["temp_dir"]
self._working_dir = self._projects_dir self._working_dir = self._projects_dir
self._VPCSrc = "" self._vpcsrc = ""
# check every 5 seconds # check every 5 seconds
#self._VPCS_callback = self.add_periodic_callback(self._check_VPCS_is_alive, 5000) #self._vpcs_callback = self.add_periodic_callback(self._check_vpcs_is_alive, 5000)
#self._VPCS_callback.start() #self._vpcs_callback.start()
def stop(self, signum=None): def stop(self, signum=None):
""" """
@ -112,54 +111,49 @@ class VPCS(IModule):
:param signum: signal number (if called by the signal handler) :param signum: signal number (if called by the signal handler)
""" """
self._VPCS_callback.stop() self._vpcs_callback.stop()
# delete all VPCS instances # delete all vpcs instances
for VPCS_id in self._VPCS_instances: for vpcs_id in self._vpcs_instances:
VPCS_instance = self._VPCS_instances[VPCS_id] vpcs_instance = self._vpcs_instances[vpcs_id]
VPCS_instance.delete() vpcs_instance.delete()
IModule.stop(self, signum) # this will stop the I/O loop IModule.stop(self, signum) # this will stop the I/O loop
def _check_VPCS_is_alive(self): def _check_vpcs_is_alive(self):
""" """
Periodic callback to check if VPCS and VPCS are alive Periodic callback to check if vpcs and vpcs are alive
for each VPCS instance. for each vpcs instance.
Sends a notification to the client if not. Sends a notification to the client if not.
""" """
for VPCS_id in self._VPCS_instances: for vpcs_id in self._vpcs_instances:
VPCS_instance = self._VPCS_instances[VPCS_id] vpcs_instance = self._vpcs_instances[vpcs_id]
if VPCS_instance.started and (not VPCS_instance.is_running() or not VPCS_instance.is_VPCS_running()): if vpcs_instance.started and (not vpcs_instance.is_running() or not vpcs_instance.is_vpcs_running()):
notification = {"module": self.name, notification = {"module": self.name,
"id": VPCS_id, "id": vpcs_id,
"name": VPCS_instance.name} "name": vpcs_instance.name}
if not VPCS_instance.is_running(): if not vpcs_instance.is_running():
stdout = VPCS_instance.read_vpcs_stdout() stdout = vpcs_instance.read_vpcs_stdout()
notification["message"] = "VPCS has stopped running" notification["message"] = "vpcs has stopped running"
notification["details"] = stdout notification["details"] = stdout
self.send_notification("{}.VPCS_stopped".format(self.name), notification) self.send_notification("{}.vpcs_stopped".format(self.name), notification)
elif not VPCS_instance.is_VPCS_running(): vpcs_instance.stop()
stdout = VPCS_instance.read_vpcs_stdout()
notification["message"] = "VPCS has stopped running"
notification["details"] = stdout
self.send_notification("{}.VPCS_stopped".format(self.name), notification)
VPCS_instance.stop()
def get_VPCS_instance(self, VPCS_id): def get_vpcs_instance(self, vpcs_id):
""" """
Returns an VPCS device instance. Returns an vpcs device instance.
:param VPCS_id: VPCS device identifier :param vpcs_id: vpcs device identifier
:returns: VPCSDevice instance :returns: vpcsDevice instance
""" """
if VPCS_id not in self._VPCS_instances: if vpcs_id not in self._vpcs_instances:
log.debug("VPCS device ID {} doesn't exist".format(VPCS_id), exc_info=1) log.debug("vpcs device ID {} doesn't exist".format(vpcs_id), exc_info=1)
self.send_custom_error("VPCS device ID {} doesn't exist".format(VPCS_id)) self.send_custom_error("vpcs device ID {} doesn't exist".format(vpcs_id))
return None return None
return self._VPCS_instances[VPCS_id] return self._vpcs_instances[vpcs_id]
@IModule.route("vpcs.reset") @IModule.route("vpcs.reset")
def reset(self, request): def reset(self, request):
@ -169,20 +163,20 @@ class VPCS(IModule):
:param request: JSON request :param request: JSON request
""" """
# delete all VPCS instances # delete all vpcs instances
for VPCS_id in self._VPCS_instances: for vpcs_id in self._vpcs_instances:
VPCS_instance = self._VPCS_instances[VPCS_id] vpcs_instance = self._vpcs_instances[vpcs_id]
VPCS_instance.delete() vpcs_instance.delete()
# resets the instance IDs # resets the instance IDs
VPCSDevice.reset() vpcsDevice.reset()
self._VPCS_instances.clear() self._vpcs_instances.clear()
self._remote_server = False self._remote_server = False
self._current_console_port = self._console_start_port_range self._current_console_port = self._console_start_port_range
self._current_udp_port = self._udp_start_port_range self._current_udp_port = self._udp_start_port_range
log.info("VPCS module has been reset") log.info("vpcs module has been reset")
@IModule.route("vpcs.settings") @IModule.route("vpcs.settings")
def settings(self, request): def settings(self, request):
@ -204,9 +198,9 @@ class VPCS(IModule):
self.send_param_error() self.send_param_error()
return return
if "VPCS" in request and request["VPCS"]: if "vpcs" in request and request["vpcs"]:
self._VPCS = request["VPCS"] self._vpcs = request["vpcs"]
log.info("VPCS path set to {}".format(self._VPCS)) log.info("vpcs path set to {}".format(self._vpcs))
if "working_dir" in request: if "working_dir" in request:
new_working_dir = request["working_dir"] new_working_dir = request["working_dir"]
@ -227,9 +221,9 @@ class VPCS(IModule):
# update the working directory if it has changed # update the working directory if it has changed
if self._working_dir != new_working_dir: if self._working_dir != new_working_dir:
self._working_dir = new_working_dir self._working_dir = new_working_dir
for VPCS_id in self._VPCS_instances: for vpcs_id in self._vpcs_instances:
VPCS_instance = self._VPCS_instances[VPCS_id] vpcs_instance = self._vpcs_instances[vpcs_id]
VPCS_instance.working_dir = self._working_dir vpcs_instance.working_dir = self._working_dir
if "console_start_port_range" in request and "console_end_port_range" in request: 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_start_port_range = request["console_start_port_range"]
@ -257,19 +251,19 @@ class VPCS(IModule):
self.send_response(response) self.send_response(response)
@IModule.route("vpcs.create") @IModule.route("vpcs.create")
def VPCS_create(self, request): def vpcs_create(self, request):
""" """
Creates a new VPCS instance. Creates a new vpcs instance.
Mandatory request parameters: Mandatory request parameters:
- path (path to the VPCS executable) - path (path to the vpcs executable)
Optional request parameters: Optional request parameters:
- name (VPCS name) - name (vpcs name)
Response parameters: Response parameters:
- id (VPCS instance identifier) - id (vpcs instance identifier)
- name (VPCS name) - name (vpcs name)
- default settings - default settings
:param request: JSON request :param request: JSON request
@ -282,7 +276,7 @@ class VPCS(IModule):
name = None name = None
if "name" in request: if "name" in request:
name = request["name"] name = request["name"]
VPCS_path = request["path"] vpcs_path = request["path"]
try: try:
try: try:
@ -290,36 +284,36 @@ class VPCS(IModule):
except FileExistsError: except FileExistsError:
pass pass
except OSError as e: except OSError as e:
raise VPCSError("Could not create working directory {}".format(e)) raise vpcsError("Could not create working directory {}".format(e))
VPCS_instance = VPCSDevice(VPCS_path, self._working_dir, host=self._host, name=name) vpcs_instance = vpcsDevice(vpcs_path, self._working_dir, host=self._host, name=name)
# find a console port # find a console port
if self._current_console_port > self._console_end_port_range: if self._current_console_port > self._console_end_port_range:
self._current_console_port = self._console_start_port_range self._current_console_port = self._console_start_port_range
try: try:
VPCS_instance.console = find_unused_port(self._current_console_port, self._console_end_port_range, self._host) vpcs_instance.console = find_unused_port(self._current_console_port, self._console_end_port_range, self._host)
except Exception as e: except Exception as e:
raise VPCSError(e) raise vpcsError(e)
self._current_console_port += 1 self._current_console_port += 1
except VPCSError as e: except vpcsError as e:
self.send_custom_error(str(e)) self.send_custom_error(str(e))
return return
response = {"name": VPCS_instance.name, response = {"name": vpcs_instance.name,
"id": VPCS_instance.id} "id": vpcs_instance.id}
defaults = VPCS_instance.defaults() defaults = vpcs_instance.defaults()
response.update(defaults) response.update(defaults)
self._VPCS_instances[VPCS_instance.id] = VPCS_instance self._vpcs_instances[vpcs_instance.id] = vpcs_instance
self.send_response(response) self.send_response(response)
@IModule.route("vpcs.delete") @IModule.route("vpcs.delete")
def VPCS_delete(self, request): def vpcs_delete(self, request):
""" """
Deletes an VPCS instance. Deletes an vpcs instance.
Mandatory request parameters: Mandatory request parameters:
- id (VPCS instance identifier) - id (vpcs instance identifier)
Response parameter: Response parameter:
- True on success - True on success
@ -332,26 +326,26 @@ class VPCS(IModule):
return return
# get the instance # get the instance
VPCS_instance = self.get_VPCS_instance(request["id"]) vpcs_instance = self.get_vpcs_instance(request["id"])
if not VPCS_instance: if not vpcs_instance:
return return
try: try:
VPCS_instance.delete() vpcs_instance.delete()
del self._VPCS_instances[request["id"]] del self._vpcs_instances[request["id"]]
except VPCSError as e: except vpcsError as e:
self.send_custom_error(str(e)) self.send_custom_error(str(e))
return return
self.send_response(True) self.send_response(True)
@IModule.route("vpcs.update") @IModule.route("vpcs.update")
def VPCS_update(self, request): def vpcs_update(self, request):
""" """
Updates an VPCS instance Updates an vpcs instance
Mandatory request parameters: Mandatory request parameters:
- id (VPCS instance identifier) - id (vpcs instance identifier)
Optional request parameters: Optional request parameters:
- any setting to update - any setting to update
@ -368,8 +362,8 @@ class VPCS(IModule):
return return
# get the instance # get the instance
VPCS_instance = self.get_VPCS_instance(request["id"]) vpcs_instance = self.get_vpcs_instance(request["id"])
if not VPCS_instance: if not vpcs_instance:
return return
response = {} response = {}
@ -378,28 +372,28 @@ class VPCS(IModule):
if "script_file_base64" in request: if "script_file_base64" in request:
config = base64.decodestring(request["script_file_base64"].encode("utf-8")).decode("utf-8") config = base64.decodestring(request["script_file_base64"].encode("utf-8")).decode("utf-8")
config = "!\n" + config.replace("\r", "") config = "!\n" + config.replace("\r", "")
config = config.replace('%h', VPCS_instance.name) config = config.replace('%h', vpcs_instance.name)
config_path = os.path.join(VPCS_instance.working_dir, "script-file") config_path = os.path.join(vpcs_instance.working_dir, "script-file")
try: try:
with open(config_path, "w") as f: with open(config_path, "w") as f:
log.info("saving script-file to {}".format(config_path)) log.info("saving script-file to {}".format(config_path))
f.write(config) f.write(config)
except OSError as e: except OSError as e:
raise VPCSError("Could not save the configuration {}: {}".format(config_path, e)) raise vpcsError("Could not save the configuration {}: {}".format(config_path, e))
# update the request with the new local script-file path # update the request with the new local script-file path
request["script_file"] = os.path.basename(config_path) request["script_file"] = os.path.basename(config_path)
except VPCSError as e: except vpcsError as e:
self.send_custom_error(str(e)) self.send_custom_error(str(e))
return return
# update the VPCS settings # update the vpcs settings
for name, value in request.items(): for name, value in request.items():
if hasattr(VPCS_instance, name) and getattr(VPCS_instance, name) != value: if hasattr(vpcs_instance, name) and getattr(vpcs_instance, name) != value:
try: try:
setattr(VPCS_instance, name, value) setattr(vpcs_instance, name, value)
response[name] = value response[name] = value
except VPCSError as e: except vpcsError as e:
self.send_custom_error(str(e)) self.send_custom_error(str(e))
return return
@ -408,10 +402,10 @@ class VPCS(IModule):
@IModule.route("vpcs.start") @IModule.route("vpcs.start")
def vm_start(self, request): def vm_start(self, request):
""" """
Starts an VPCS instance. Starts an vpcs instance.
Mandatory request parameters: Mandatory request parameters:
- id (VPCS instance identifier) - id (vpcs instance identifier)
Response parameters: Response parameters:
- True on success - True on success
@ -424,16 +418,15 @@ class VPCS(IModule):
return return
# get the instance # get the instance
VPCS_instance = self.get_VPCS_instance(request["id"]) vpcs_instance = self.get_vpcs_instance(request["id"])
if not VPCS_instance: if not vpcs_instance:
return return
try: try:
log.debug("starting VPCS with command: {}".format(VPCS_instance.command())) log.debug("starting vpcs with command: {}".format(vpcs_instance.command()))
VPCS_instance.VPCS = self._VPCS vpcs_instance.vpcs = self._vpcs
VPCS_instance.VPCSrc = self._VPCSrc vpcs_instance.start()
VPCS_instance.start() except vpcsError as e:
except VPCSError as e:
self.send_custom_error(str(e)) self.send_custom_error(str(e))
return return
self.send_response(True) self.send_response(True)
@ -441,10 +434,10 @@ class VPCS(IModule):
@IModule.route("vpcs.stop") @IModule.route("vpcs.stop")
def vm_stop(self, request): def vm_stop(self, request):
""" """
Stops an VPCS instance. Stops an vpcs instance.
Mandatory request parameters: Mandatory request parameters:
- id (VPCS instance identifier) - id (vpcs instance identifier)
Response parameters: Response parameters:
- True on success - True on success
@ -457,13 +450,13 @@ class VPCS(IModule):
return return
# get the instance # get the instance
VPCS_instance = self.get_VPCS_instance(request["id"]) vpcs_instance = self.get_vpcs_instance(request["id"])
if not VPCS_instance: if not vpcs_instance:
return return
try: try:
VPCS_instance.stop() vpcs_instance.stop()
except VPCSError as e: except vpcsError as e:
self.send_custom_error(str(e)) self.send_custom_error(str(e))
return return
self.send_response(True) self.send_response(True)
@ -471,10 +464,10 @@ class VPCS(IModule):
@IModule.route("vpcs.reload") @IModule.route("vpcs.reload")
def vm_reload(self, request): def vm_reload(self, request):
""" """
Reloads an VPCS instance. Reloads an vpcs instance.
Mandatory request parameters: Mandatory request parameters:
- id (VPCS identifier) - id (vpcs identifier)
Response parameters: Response parameters:
- True on success - True on success
@ -487,15 +480,15 @@ class VPCS(IModule):
return return
# get the instance # get the instance
VPCS_instance = self.get_VPCS_instance(request["id"]) vpcs_instance = self.get_vpcs_instance(request["id"])
if not VPCS_instance: if not vpcs_instance:
return return
try: try:
if VPCS_instance.is_running(): if vpcs_instance.is_running():
VPCS_instance.stop() vpcs_instance.stop()
VPCS_instance.start() vpcs_instance.start()
except VPCSError as e: except vpcsError as e:
self.send_custom_error(str(e)) self.send_custom_error(str(e))
return return
self.send_response(True) self.send_response(True)
@ -506,7 +499,7 @@ class VPCS(IModule):
Allocates a UDP port in order to create an UDP NIO. Allocates a UDP port in order to create an UDP NIO.
Mandatory request parameters: Mandatory request parameters:
- id (VPCS identifier) - id (vpcs identifier)
- port_id (unique port identifier) - port_id (unique port identifier)
Response parameters: Response parameters:
@ -521,8 +514,8 @@ class VPCS(IModule):
return return
# get the instance # get the instance
VPCS_instance = self.get_VPCS_instance(request["id"]) vpcs_instance = self.get_vpcs_instance(request["id"])
if not VPCS_instance: if not vpcs_instance:
return return
try: try:
@ -533,16 +526,16 @@ class VPCS(IModule):
try: try:
port = find_unused_port(self._current_udp_port, self._udp_end_port_range, host=self._host, socket_type="UDP") port = find_unused_port(self._current_udp_port, self._udp_end_port_range, host=self._host, socket_type="UDP")
except Exception as e: except Exception as e:
raise VPCSError(e) raise vpcsError(e)
self._current_udp_port += 1 self._current_udp_port += 1
log.info("{} [id={}] has allocated UDP port {} with host {}".format(VPCS_instance.name, log.info("{} [id={}] has allocated UDP port {} with host {}".format(vpcs_instance.name,
VPCS_instance.id, vpcs_instance.id,
port, port,
self._host)) self._host))
response = {"lport": port} response = {"lport": port}
except VPCSError as e: except vpcsError as e:
self.send_custom_error(str(e)) self.send_custom_error(str(e))
return return
@ -551,35 +544,35 @@ class VPCS(IModule):
def _check_for_privileged_access(self, device): def _check_for_privileged_access(self, device):
""" """
Check if VPCS can access Ethernet and TAP devices. Check if vpcs can access Ethernet and TAP devices.
:param device: device name :param device: device name
""" """
# we are root, so VPCS should have privileged access too # we are root, so vpcs should have privileged access too
if os.geteuid() == 0: if os.geteuid() == 0:
return return
# test if VPCS has the CAP_NET_RAW capability # test if vpcs has the CAP_NET_RAW capability
if "security.capability" in os.listxattr(self._VPCS): if "security.capability" in os.listxattr(self._vpcs):
try: try:
caps = os.getxattr(self._VPCS, "security.capability") caps = os.getxattr(self._vpcs, "security.capability")
# test the 2nd byte and check if the 13th bit (CAP_NET_RAW) is set # test the 2nd byte and check if the 13th bit (CAP_NET_RAW) is set
if struct.unpack("<IIIII", caps)[1] & 1 << 13: if struct.unpack("<IIIII", caps)[1] & 1 << 13:
return return
except Exception as e: except Exception as e:
log.error("could not determine if CAP_NET_RAW capability is set for {}: {}".format(self._VPCS, e)) log.error("could not determine if CAP_NET_RAW capability is set for {}: {}".format(self._vpcs, e))
return return
raise VPCSError("{} has no privileged access to {}.".format(self._VPCS, device)) raise vpcsError("{} has no privileged access to {}.".format(self._vpcs, device))
@IModule.route("vpcs.add_nio") @IModule.route("vpcs.add_nio")
def add_nio(self, request): def add_nio(self, request):
""" """
Adds an NIO (Network Input/Output) for an VPCS instance. Adds an NIO (Network Input/Output) for an vpcs instance.
Mandatory request parameters: Mandatory request parameters:
- id (VPCS instance identifier) - id (vpcs instance identifier)
- slot (slot number) - slot (slot number)
- port (port number) - port (port number)
- port_id (unique port identifier) - port_id (unique port identifier)
@ -602,8 +595,8 @@ class VPCS(IModule):
return return
# get the instance # get the instance
VPCS_instance = self.get_VPCS_instance(request["id"]) vpcs_instance = self.get_vpcs_instance(request["id"])
if not VPCS_instance: if not vpcs_instance:
return return
slot = request["slot"] slot = request["slot"]
@ -620,14 +613,14 @@ class VPCS(IModule):
self._check_for_privileged_access(tap_device) self._check_for_privileged_access(tap_device)
nio = NIO_TAP(tap_device) nio = NIO_TAP(tap_device)
if not nio: if not nio:
raise VPCSError("Requested NIO does not exist or is not supported: {}".format(request["nio"]["type"])) raise vpcsError("Requested NIO does not exist or is not supported: {}".format(request["nio"]["type"]))
except VPCSError as e: except vpcsError as e:
self.send_custom_error(str(e)) self.send_custom_error(str(e))
return return
try: try:
VPCS_instance.slot_add_nio_binding(slot, port, nio) vpcs_instance.slot_add_nio_binding(slot, port, nio)
except VPCSError as e: except vpcsError as e:
self.send_custom_error(str(e)) self.send_custom_error(str(e))
return return
@ -639,7 +632,7 @@ class VPCS(IModule):
Deletes an NIO (Network Input/Output). Deletes an NIO (Network Input/Output).
Mandatory request parameters: Mandatory request parameters:
- id (VPCS instance identifier) - id (vpcs instance identifier)
- slot (slot identifier) - slot (slot identifier)
- port (port identifier) - port (port identifier)
@ -654,15 +647,15 @@ class VPCS(IModule):
return return
# get the instance # get the instance
VPCS_instance = self.get_VPCS_instance(request["id"]) vpcs_instance = self.get_vpcs_instance(request["id"])
if not VPCS_instance: if not vpcs_instance:
return return
slot = request["slot"] slot = request["slot"]
port = request["port"] port = request["port"]
try: try:
VPCS_instance.slot_remove_nio_binding(slot, port) vpcs_instance.slot_remove_nio_binding(slot, port)
except VPCSError as e: except vpcsError as e:
self.send_custom_error(str(e)) self.send_custom_error(str(e))
return return

View File

@ -23,7 +23,7 @@ class Adapter(object):
:param interfaces: number of interfaces supported by this adapter. :param interfaces: number of interfaces supported by this adapter.
""" """
def __init__(self, interfaces=4): def __init__(self, interfaces=1):
self._interfaces = interfaces self._interfaces = interfaces

View File

@ -16,8 +16,8 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
""" """
VPCS device management (creates command line, processes, files etc.) in vpcs device management (creates command line, processes, files etc.) in
order to run an VPCS instance. order to run an vpcs instance.
""" """
import os import os
@ -29,7 +29,7 @@ import threading
import configparser import configparser
import sys import sys
import socket import socket
from .vpcs_error import VPCSError from .vpcs_error import vpcsError
from .adapters.ethernet_adapter import EthernetAdapter from .adapters.ethernet_adapter import EthernetAdapter
from .nios.nio_udp import NIO_UDP from .nios.nio_udp import NIO_UDP
from .nios.nio_tap import NIO_TAP from .nios.nio_tap import NIO_TAP
@ -38,14 +38,14 @@ import logging
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
class VPCSDevice(object): class vpcsDevice(object):
""" """
VPCS device implementation. vpcs device implementation.
:param path: path to VPCS executable :param path: path to vpcs executable
:param working_dir: path to a working directory :param working_dir: path to a working directory
:param host: host/address to bind for console and UDP connections :param host: host/address to bind for console and UDP connections
:param name: name of this VPCS device :param name: name of this vpcs device
""" """
_instances = [] _instances = []
@ -54,7 +54,7 @@ class VPCSDevice(object):
# find an instance identifier (1 <= id <= 255) # find an instance identifier (1 <= id <= 255)
# This 255 limit is due to a restriction on the number of possible # This 255 limit is due to a restriction on the number of possible
# mac addresses given in VPCS using the -m option # mac addresses given in vpcs using the -m option
self._id = 0 self._id = 0
for identifier in range(1, 256): for identifier in range(1, 256):
if identifier not in self._instances: if identifier not in self._instances:
@ -63,12 +63,12 @@ class VPCSDevice(object):
break break
if self._id == 0: if self._id == 0:
raise VPCSError("Maximum number of VPCS instances reached") raise vpcsError("Maximum number of vpcs instances reached")
if name: if name:
self._name = name self._name = name
else: else:
self._name = "VPCS{}".format(self._id) self._name = "vpcs{}".format(self._id)
self._path = path self._path = path
self._console = None self._console = None
self._working_dir = None self._working_dir = None
@ -78,7 +78,7 @@ class VPCSDevice(object):
self._host = "127.0.0.1" self._host = "127.0.0.1"
self._started = False self._started = False
# VPCS settings # vpcs settings
self._script_file = "" self._script_file = ""
self._ethernet_adapters = [EthernetAdapter()] # one adapter = 1 interfaces self._ethernet_adapters = [EthernetAdapter()] # one adapter = 1 interfaces
self._slots = self._ethernet_adapters self._slots = self._ethernet_adapters
@ -86,12 +86,12 @@ class VPCSDevice(object):
# update the working directory # update the working directory
self.working_dir = working_dir self.working_dir = working_dir
log.info("VPCS device {name} [id={id}] has been created".format(name=self._name, log.info("vpcs device {name} [id={id}] has been created".format(name=self._name,
id=self._id)) id=self._id))
def defaults(self): def defaults(self):
""" """
Returns all the default attribute values for VPCS. Returns all the default attribute values for vpcs.
:returns: default values (dictionary) :returns: default values (dictionary)
""" """
@ -106,7 +106,7 @@ class VPCSDevice(object):
@property @property
def id(self): def id(self):
""" """
Returns the unique ID for this VPCS device. Returns the unique ID for this vpcs device.
:returns: id (integer) :returns: id (integer)
""" """
@ -124,7 +124,7 @@ class VPCSDevice(object):
@property @property
def name(self): def name(self):
""" """
Returns the name of this VPCS device. Returns the name of this vpcs device.
:returns: name :returns: name
""" """
@ -134,22 +134,22 @@ class VPCSDevice(object):
@name.setter @name.setter
def name(self, new_name): def name(self, new_name):
""" """
Sets the name of this VPCS device. Sets the name of this vpcs device.
:param new_name: name :param new_name: name
""" """
self._name = new_name self._name = new_name
log.info("VPCS {name} [id={id}]: renamed to {new_name}".format(name=self._name, log.info("vpcs {name} [id={id}]: renamed to {new_name}".format(name=self._name,
id=self._id, id=self._id,
new_name=new_name)) new_name=new_name))
@property @property
def path(self): def path(self):
""" """
Returns the path to the VPCS executable. Returns the path to the vpcs executable.
:returns: path to VPCS :returns: path to vpcs
""" """
return(self._path) return(self._path)
@ -157,13 +157,13 @@ class VPCSDevice(object):
@path.setter @path.setter
def path(self, path): def path(self, path):
""" """
Sets the path to the VPCS executable. Sets the path to the vpcs executable.
:param path: path to VPCS :param path: path to vpcs
""" """
self._path = path self._path = path
log.info("VPCS {name} [id={id}]: path changed to {path}".format(name=self._name, log.info("vpcs {name} [id={id}]: path changed to {path}".format(name=self._name,
id=self._id, id=self._id,
path=path)) path=path))
@ -180,7 +180,7 @@ class VPCSDevice(object):
@working_dir.setter @working_dir.setter
def working_dir(self, working_dir): def working_dir(self, working_dir):
""" """
Sets the working directory for VPCS. Sets the working directory for vpcs.
:param working_dir: path to the working directory :param working_dir: path to the working directory
""" """
@ -192,10 +192,10 @@ class VPCSDevice(object):
except FileExistsError: except FileExistsError:
pass pass
except OSError as e: except OSError as e:
raise VPCSError("Could not create working directory {}: {}".format(working_dir, e)) raise vpcsError("Could not create working directory {}: {}".format(working_dir, e))
self._working_dir = working_dir self._working_dir = working_dir
log.info("VPCS {name} [id={id}]: working directory changed to {wd}".format(name=self._name, log.info("vpcs {name} [id={id}]: working directory changed to {wd}".format(name=self._name,
id=self._id, id=self._id,
wd=self._working_dir)) wd=self._working_dir))
@ -218,33 +218,33 @@ class VPCSDevice(object):
""" """
self._console = console self._console = console
log.info("VPCS {name} [id={id}]: console port set to {port}".format(name=self._name, log.info("vpcs {name} [id={id}]: console port set to {port}".format(name=self._name,
id=self._id, id=self._id,
port=console)) port=console))
def command(self): def command(self):
""" """
Returns the VPCS command line. Returns the vpcs command line.
:returns: VPCS command line (string) :returns: vpcs command line (string)
""" """
return " ".join(self._build_command()) return " ".join(self._build_command())
def delete(self): def delete(self):
""" """
Deletes this VPCS device. Deletes this vpcs device.
""" """
self.stop() self.stop()
self._instances.remove(self._id) self._instances.remove(self._id)
log.info("VPCS device {name} [id={id}] has been deleted".format(name=self._name, log.info("vpcs device {name} [id={id}] has been deleted".format(name=self._name,
id=self._id)) id=self._id))
@property @property
def started(self): def started(self):
""" """
Returns either this VPCS device has been started or not. Returns either this vpcs device has been started or not.
:returns: boolean :returns: boolean
""" """
@ -253,20 +253,20 @@ class VPCSDevice(object):
def start(self): def start(self):
""" """
Starts the VPCS process. Starts the vpcs process.
""" """
if not self.is_running(): if not self.is_running():
if not os.path.isfile(self._path): if not os.path.isfile(self._path):
raise VPCSError("VPCS image '{}' is not accessible".format(self._path)) raise vpcsError("vpcs image '{}' is not accessible".format(self._path))
if not os.access(self._path, os.X_OK): if not os.access(self._path, os.X_OK):
raise VPCSError("VPCS image '{}' is not executable".format(self._path)) raise vpcsError("vpcs image '{}' is not executable".format(self._path))
self._command = self._build_command() self._command = self._build_command()
try: try:
log.info("starting VPCS: {}".format(self._command)) log.info("starting vpcs: {}".format(self._command))
self._vpcs_stdout_file = os.path.join(self._working_dir, "vpcs.log") self._vpcs_stdout_file = os.path.join(self._working_dir, "vpcs.log")
log.info("logging to {}".format(self._vpcs_stdout_file)) log.info("logging to {}".format(self._vpcs_stdout_file))
with open(self._vpcs_stdout_file, "w") as fd: with open(self._vpcs_stdout_file, "w") as fd:
@ -274,30 +274,28 @@ class VPCSDevice(object):
stdout=fd, stdout=fd,
stderr=subprocess.STDOUT, stderr=subprocess.STDOUT,
cwd=self._working_dir) cwd=self._working_dir)
log.info("VPCS instance {} started PID={}".format(self._id, self._process.pid)) log.info("vpcs instance {} started PID={}".format(self._id, self._process.pid))
self._started = True self._started = True
except FileNotFoundError as e:
raise VPCSError("could not start VPCS: {}: 32-bit binary support is probably not installed".format(e))
except OSError as e: except OSError as e:
vpcs_stdout = self.read_vpcs_stdout() vpcs_stdout = self.read_vpcs_stdout()
log.error("could not start VPCS {}: {}\n{}".format(self._path, e, vpcs_stdout)) log.error("could not start vpcs {}: {}\n{}".format(self._path, e, vpcs_stdout))
raise VPCSError("could not start VPCS {}: {}\n{}".format(self._path, e, vpcs_stdout)) raise vpcsError("could not start vpcs {}: {}\n{}".format(self._path, e, vpcs_stdout))
def stop(self): def stop(self):
""" """
Stops the VPCS process. Stops the vpcs process.
""" """
# stop the VPCS process # stop the vpcs process
if self.is_running(): if self.is_running():
log.info("stopping VPCS instance {} PID={}".format(self._id, self._process.pid)) log.info("stopping vpcs instance {} PID={}".format(self._id, self._process.pid))
try: try:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((self._host, self._console)) sock.connect((self._host, self._console))
sock.send(bytes("quit\n", 'UTF-8')) sock.send(bytes("quit\n", 'UTF-8'))
sock.close() sock.close()
except TypeError as e: except TypeError as e:
log.warn("VPCS instance {} PID={} is still running. Error: {}".format(self._id, log.warn("vpcs instance {} PID={} is still running. Error: {}".format(self._id,
self._process.pid, e)) self._process.pid, e))
self._process = None self._process = None
self._started = False self._started = False
@ -305,7 +303,7 @@ class VPCSDevice(object):
def read_vpcs_stdout(self): def read_vpcs_stdout(self):
""" """
Reads the standard output of the VPCS process. Reads the standard output of the vpcs process.
Only use when the process has been stopped or has crashed. Only use when the process has been stopped or has crashed.
""" """
@ -320,7 +318,7 @@ class VPCSDevice(object):
def is_running(self): def is_running(self):
""" """
Checks if the VPCS process is running Checks if the vpcs process is running
:returns: True or False :returns: True or False
""" """
@ -350,15 +348,15 @@ class VPCSDevice(object):
try: try:
adapter = self._slots[slot_id] adapter = self._slots[slot_id]
except IndexError: except IndexError:
raise VPCSError("Slot {slot_id} doesn't exist on VPCS {name}".format(name=self._name, raise vpcsError("Slot {slot_id} doesn't exist on vpcs {name}".format(name=self._name,
slot_id=slot_id)) slot_id=slot_id))
if not adapter.port_exists(port_id): if not adapter.port_exists(port_id):
raise VPCSError("Port {port_id} doesn't exist in adapter {adapter}".format(adapter=adapter, raise vpcsError("Port {port_id} doesn't exist in adapter {adapter}".format(adapter=adapter,
port_id=port_id)) port_id=port_id))
adapter.add_nio(port_id, nio) adapter.add_nio(port_id, nio)
log.info("VPCS {name} [id={id}]: {nio} added to {slot_id}/{port_id}".format(name=self._name, log.info("vpcs {name} [id={id}]: {nio} added to {slot_id}/{port_id}".format(name=self._name,
id=self._id, id=self._id,
nio=nio, nio=nio,
slot_id=slot_id, slot_id=slot_id,
@ -375,16 +373,16 @@ class VPCSDevice(object):
try: try:
adapter = self._slots[slot_id] adapter = self._slots[slot_id]
except IndexError: except IndexError:
raise VPCSError("Slot {slot_id} doesn't exist on VPCS {name}".format(name=self._name, raise vpcsError("Slot {slot_id} doesn't exist on vpcs {name}".format(name=self._name,
slot_id=slot_id)) slot_id=slot_id))
if not adapter.port_exists(port_id): if not adapter.port_exists(port_id):
raise VPCSError("Port {port_id} doesn't exist in adapter {adapter}".format(adapter=adapter, raise vpcsError("Port {port_id} doesn't exist in adapter {adapter}".format(adapter=adapter,
port_id=port_id)) port_id=port_id))
nio = adapter.get_nio(port_id) nio = adapter.get_nio(port_id)
adapter.remove_nio(port_id) adapter.remove_nio(port_id)
log.info("VPCS {name} [id={id}]: {nio} removed from {slot_id}/{port_id}".format(name=self._name, log.info("vpcs {name} [id={id}]: {nio} removed from {slot_id}/{port_id}".format(name=self._name,
id=self._id, id=self._id,
nio=nio, nio=nio,
slot_id=slot_id, slot_id=slot_id,
@ -392,10 +390,10 @@ class VPCSDevice(object):
def _build_command(self): def _build_command(self):
""" """
Command to start the VPCS process. Command to start the vpcs process.
(to be passed to subprocess.Popen()) (to be passed to subprocess.Popen())
VPCS command line: vpcs command line:
usage: vpcs [options] [scriptfile] usage: vpcs [options] [scriptfile]
Option: Option:
-h print this help then exit -h print this help then exit
@ -448,7 +446,7 @@ class VPCSDevice(object):
@property @property
def script_file(self): def script_file(self):
""" """
Returns the script-file for this VPCS instance. Returns the script-file for this vpcs instance.
:returns: path to script-file file :returns: path to script-file file
""" """
@ -458,13 +456,13 @@ class VPCSDevice(object):
@script_file.setter @script_file.setter
def script_file(self, script_file): def script_file(self, script_file):
""" """
Sets the script-file for this VPCS instance. Sets the script-file for this vpcs instance.
:param script_file: path to script-file file :param script_file: path to script-file file
""" """
self._script_file = script_file self._script_file = script_file
log.info("VPCS {name} [id={id}]: script_file set to {config}".format(name=self._name, log.info("vpcs {name} [id={id}]: script_file set to {config}".format(name=self._name,
id=self._id, id=self._id,
config=self._script_file)) config=self._script_file))

View File

@ -20,7 +20,7 @@ Custom exceptions for VPCS module.
""" """
class VPCSError(Exception): class vpcsError(Exception):
def __init__(self, message, original_exception=None): def __init__(self, message, original_exception=None):

View File

@ -1,5 +0,0 @@
show
quit
show
quot
quit