diff --git a/gns3server/modules/attic.py b/gns3server/modules/attic.py index af87e771..d0ebf1ed 100644 --- a/gns3server/modules/attic.py +++ b/gns3server/modules/attic.py @@ -19,10 +19,16 @@ Useful functions... in the attic ;) """ +import sys +import os +import struct import socket import errno import time +import logging +log = logging.getLogger(__name__) + def find_unused_port(start_port, end_port, host='127.0.0.1', socket_type="TCP", ignore_ports=[]): """ @@ -102,3 +108,31 @@ def wait_socket_is_ready(host, port, wait=2.0, socket_timeout=10): break return (connection_success, last_exception) + + +def has_privileged_access(executable, device): + """ + Check if an executable can access Ethernet and TAP devices in + RAW mode. + + :param executable: executable path + :param device: device name + + :returns: True or False + """ + + # we are root, so we should have privileged access too + if os.geteuid() == 0: + return True + + # test if the executable has the CAP_NET_RAW capability (Linux only) + if sys.platform.startswith("linux") and "security.capability" in os.listxattr(executable): + try: + caps = os.getxattr(executable, "security.capability") + # test the 2nd byte and check if the 13th bit (CAP_NET_RAW) is set + if struct.unpack(" self._console_end_port_range: - self._current_console_port = self._console_start_port_range - try: - vpcs_instance.console = find_unused_port(self._current_console_port, self._console_end_port_range, self._host) - except Exception as e: - raise VPCSError(e) - self._current_console_port += 1 + vpcs_instance = VPCSDevice(self._vpcs, + self._working_dir, + self._host, + name, + self._console_start_port_range, + self._console_end_port_range) + except VPCSError as e: self.send_custom_error(str(e)) return @@ -367,7 +302,7 @@ class VPCS(IModule): Optional request parameters: - any setting to update - - base_script_file_base64 (script-file base64 encoded) + - script_file_base64 (base64 encoded) Response parameters: - updated settings @@ -384,28 +319,42 @@ class VPCS(IModule): if not vpcs_instance: return - response = {} + config_path = os.path.join(vpcs_instance.working_dir, "startup.vpc") try: - # a new base-script-file has been pushed - if "base_script_file_base64" in request: - config = base64.decodestring(request["base_script_file_base64"].encode("utf-8")).decode("utf-8") - config = "!\n" + config.replace("\r", "") + if "script_file_base64" in request: + # a new startup-config has been pushed + config = base64.decodestring(request["script_file_base64"].encode("utf-8")).decode("utf-8") + config = config.replace("\r", "") config = config.replace('%h', vpcs_instance.name) - config_path = os.path.join(vpcs_instance.working_dir, "base-script-file") try: with open(config_path, "w") as f: - log.info("saving base-script-file to {}".format(config_path)) + log.info("saving script file to {}".format(config_path)) f.write(config) except OSError as e: raise VPCSError("Could not save the configuration {}: {}".format(config_path, e)) - # update the request with the new local base-script-file path - request["base_script_file"] = os.path.basename(config_path) - + # update the request with the new local startup-config path + request["script_file"] = os.path.basename(config_path) + elif "script_file" in request: + if os.path.isfile(request["script_file"]) and request["script_file"] != config_path: + # this is a local file set in the GUI + try: + with open(request["script_file"], "r") as f: + config = f.read() + with open(config_path, "w") as f: + config = config.replace("\r", "") + config = config.replace('%h', vpcs_instance.name) + f.write(config) + request["script_file"] = os.path.basename(config_path) + except OSError as e: + raise VPCSError("Could not save the configuration from {} to {}: {}".format(request["script_file"], config_path, e)) + elif not os.path.isfile(config_path): + raise VPCSError("Startup-config {} could not be found on this server".format(config_path)) except VPCSError as e: self.send_custom_error(str(e)) return - + # update the VPCS settings + response = {} for name, value in request.items(): if hasattr(vpcs_instance, name) and getattr(vpcs_instance, name) != value: try: @@ -442,7 +391,6 @@ class VPCS(IModule): try: log.debug("starting VPCS with command: {}".format(vpcs_instance.command())) - vpcs_instance.vpcs = self._vpcs vpcs_instance.start() except VPCSError as e: self.send_custom_error(str(e)) @@ -537,53 +485,24 @@ class VPCS(IModule): return try: - - # find a UDP port - if self._current_udp_port >= self._udp_end_port_range: - self._current_udp_port = self._udp_start_port_range - try: - port = find_unused_port(self._current_udp_port, self._udp_end_port_range, host=self._host, socket_type="UDP") - except Exception as e: - raise VPCSError(e) - self._current_udp_port += 1 - - log.info("{} [id={}] has allocated UDP port {} with host {}".format(vpcs_instance.name, - vpcs_instance.id, - port, - self._host)) - response = {"lport": port} - - except VPCSError as e: + 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(vpcs_instance.name, + vpcs_instance.id, + port, + self._host)) + + response = {"lport": port} response["port_id"] = request["port_id"] self.send_response(response) - def _check_for_privileged_access(self, device): - """ - Check if VPCS can access Ethernet and TAP devices. - - :param device: device name - """ - - # we are root, so vpcs should have privileged access too - if os.geteuid() == 0: - return - - # test if VPCS has the CAP_NET_RAW capability - if "security.capability" in os.listxattr(self._vpcs): - try: - caps = os.getxattr(self._vpcs, "security.capability") - # test the 2nd byte and check if the 13th bit (CAP_NET_RAW) is set - if struct.unpack("