diff --git a/gns3server/old_modules/iou/__init__.py b/gns3server/old_modules/iou/__init__.py deleted file mode 100644 index 04c7e4c0..00000000 --- a/gns3server/old_modules/iou/__init__.py +++ /dev/null @@ -1,843 +0,0 @@ -# -*- 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 . - -""" -IOU server module. -""" - -import os -import base64 -import ntpath -import stat -import tempfile -import socket -import shutil - -from gns3server.modules import IModule -from gns3server.config import Config -from gns3dms.cloud.rackspace_ctrl import get_provider -from .iou_device import IOUDevice -from .iou_error import IOUError -from .nios.nio_udp import NIO_UDP -from .nios.nio_tap import NIO_TAP -from .nios.nio_generic_ethernet import NIO_GenericEthernet -from ..attic import find_unused_port -from ..attic import has_privileged_access - -from .schemas import IOU_CREATE_SCHEMA -from .schemas import IOU_DELETE_SCHEMA -from .schemas import IOU_UPDATE_SCHEMA -from .schemas import IOU_START_SCHEMA -from .schemas import IOU_STOP_SCHEMA -from .schemas import IOU_RELOAD_SCHEMA -from .schemas import IOU_ALLOCATE_UDP_PORT_SCHEMA -from .schemas import IOU_ADD_NIO_SCHEMA -from .schemas import IOU_DELETE_NIO_SCHEMA -from .schemas import IOU_START_CAPTURE_SCHEMA -from .schemas import IOU_STOP_CAPTURE_SCHEMA -from .schemas import IOU_EXPORT_CONFIG_SCHEMA - -import logging -log = logging.getLogger(__name__) - - -class IOU(IModule): - - """ - IOU module. - - :param name: module name - :param args: arguments for the module - :param kwargs: named arguments for the module - """ - - def __init__(self, name, *args, **kwargs): - - # get the iouyap location - config = Config.instance() - iou_config = config.get_section_config(name.upper()) - self._iouyap = iou_config.get("iouyap_path") - if not self._iouyap or not os.path.isfile(self._iouyap): - paths = [os.getcwd()] + os.environ["PATH"].split(os.pathsep) - # look for iouyap in the current working directory and $PATH - for path in paths: - try: - if "iouyap" in os.listdir(path) and os.access(os.path.join(path, "iouyap"), os.X_OK): - self._iouyap = os.path.join(path, "iouyap") - break - except OSError: - continue - - if not self._iouyap: - log.warning("iouyap binary couldn't be found!") - elif not os.access(self._iouyap, os.X_OK): - log.warning("iouyap is not executable") - - # a new process start when calling IModule - IModule.__init__(self, name, *args, **kwargs) - self._iou_instances = {} - self._console_start_port_range = iou_config.get("console_start_port_range", 4001) - self._console_end_port_range = iou_config.get("console_end_port_range", 4500) - self._allocated_udp_ports = [] - self._udp_start_port_range = iou_config.get("udp_start_port_range", 30001) - self._udp_end_port_range = iou_config.get("udp_end_port_range", 35000) - self._host = iou_config.get("host", kwargs["host"]) - self._console_host = iou_config.get("console_host", kwargs["console_host"]) - self._projects_dir = kwargs["projects_dir"] - self._tempdir = kwargs["temp_dir"] - self._working_dir = self._projects_dir - self._server_iourc_path = iou_config.get("iourc", "") - self._iourc = "" - - # check every 5 seconds - self._iou_callback = self.add_periodic_callback(self._check_iou_is_alive, 5000) - self._iou_callback.start() - - def stop(self, signum=None): - """ - Properly stops the module. - - :param signum: signal number (if called by the signal handler) - """ - - self._iou_callback.stop() - - # delete all IOU instances - for iou_id in self._iou_instances: - iou_instance = self._iou_instances[iou_id] - iou_instance.delete() - - self.delete_iourc_file() - - IModule.stop(self, signum) # this will stop the I/O loop - - def _check_iou_is_alive(self): - """ - Periodic callback to check if IOU and iouyap are alive - for each IOU instance. - - Sends a notification to the client if not. - """ - - for iou_id in self._iou_instances: - iou_instance = self._iou_instances[iou_id] - if iou_instance.started and (not iou_instance.is_running() or not iou_instance.is_iouyap_running()): - notification = {"module": self.name, - "id": iou_id, - "name": iou_instance.name} - if not iou_instance.is_running(): - stdout = iou_instance.read_iou_stdout() - notification["message"] = "IOU has stopped running" - notification["details"] = stdout - self.send_notification("{}.iou_stopped".format(self.name), notification) - elif not iou_instance.is_iouyap_running(): - stdout = iou_instance.read_iouyap_stdout() - notification["message"] = "iouyap has stopped running" - notification["details"] = stdout - self.send_notification("{}.iouyap_stopped".format(self.name), notification) - iou_instance.stop() - - def get_iou_instance(self, iou_id): - """ - Returns an IOU device instance. - - :param iou_id: IOU device identifier - - :returns: IOUDevice instance - """ - - if iou_id not in self._iou_instances: - log.debug("IOU device ID {} doesn't exist".format(iou_id), exc_info=1) - self.send_custom_error("IOU device ID {} doesn't exist".format(iou_id)) - return None - return self._iou_instances[iou_id] - - def delete_iourc_file(self): - """ - Deletes the IOURC file. - """ - - if self._iourc and os.path.isfile(self._iourc): - try: - log.info("deleting iourc file {}".format(self._iourc)) - os.remove(self._iourc) - except OSError as e: - log.warn("could not delete iourc file {}: {}".format(self._iourc, e)) - - @IModule.route("iou.reset") - def reset(self, request=None): - """ - Resets the module (JSON-RPC notification). - - :param request: JSON request (not used) - """ - - # delete all IOU instances - for iou_id in self._iou_instances: - iou_instance = self._iou_instances[iou_id] - iou_instance.delete() - - # resets the instance IDs - IOUDevice.reset() - - self._iou_instances.clear() - self._allocated_udp_ports.clear() - self.delete_iourc_file() - - self._working_dir = self._projects_dir - log.info("IOU module has been reset") - - @IModule.route("iou.settings") - def settings(self, request): - """ - Set or update settings. - - Mandatory request parameters: - - iourc (base64 encoded iourc file) - - Optional request parameters: - - iouyap (path to iouyap) - - 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 "iourc" in request: - iourc_content = base64.decodebytes(request["iourc"].encode("utf-8")).decode("utf-8") - iourc_content = iourc_content.replace("\r\n", "\n") # dos2unix - try: - with tempfile.NamedTemporaryFile(mode="w", delete=False) as f: - log.info("saving iourc file content to {}".format(f.name)) - f.write(iourc_content) - self._iourc = f.name - except OSError as e: - raise IOUError("Could not create the iourc file: {}".format(e)) - - if "iouyap" in request and request["iouyap"]: - self._iouyap = request["iouyap"] - log.info("iouyap path set to {}".format(self._iouyap)) - - 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 iou_id in self._iou_instances: - iou_instance = self._iou_instances[iou_id] - iou_instance.working_dir = os.path.join(self._working_dir, "iou", "device-{}".format(iou_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("iou.create") - def iou_create(self, request): - """ - Creates a new IOU instance. - - Mandatory request parameters: - - path (path to the IOU executable) - - Optional request parameters: - - name (IOU name) - - console (IOU console port) - - Response parameters: - - id (IOU instance identifier) - - name (IOU name) - - default settings - - :param request: JSON request - """ - - # validate the request - if not self.validate_request(request, IOU_CREATE_SCHEMA): - return - - name = request["name"] - iou_path = request["path"] - console = request.get("console") - iou_id = request.get("iou_id") - - updated_iou_path = os.path.join(self.images_directory, iou_path) - if os.path.isfile(updated_iou_path): - iou_path = updated_iou_path - else: - if not os.path.exists(self.images_directory): - os.mkdir(self.images_directory) - cloud_path = request.get("cloud_path", None) - if cloud_path is not None: - # Download the image from cloud files - _, filename = ntpath.split(iou_path) - src = '{}/{}'.format(cloud_path, filename) - provider = get_provider(self._cloud_settings) - log.debug("Downloading file from {} to {}...".format(src, updated_iou_path)) - provider.download_file(src, updated_iou_path) - log.debug("Download of {} complete.".format(src)) - # Make file executable - st = os.stat(updated_iou_path) - os.chmod(updated_iou_path, st.st_mode | stat.S_IEXEC) - iou_path = updated_iou_path - - try: - iou_instance = IOUDevice(name, - iou_path, - self._working_dir, - iou_id, - console, - self._console_host, - self._console_start_port_range, - self._console_end_port_range) - - except IOUError as e: - self.send_custom_error(str(e)) - return - - response = {"name": iou_instance.name, - "id": iou_instance.id} - - defaults = iou_instance.defaults() - response.update(defaults) - self._iou_instances[iou_instance.id] = iou_instance - self.send_response(response) - - @IModule.route("iou.delete") - def iou_delete(self, request): - """ - Deletes an IOU instance. - - Mandatory request parameters: - - id (IOU instance identifier) - - Response parameter: - - True on success - - :param request: JSON request - """ - - # validate the request - if not self.validate_request(request, IOU_DELETE_SCHEMA): - return - - # get the instance - iou_instance = self.get_iou_instance(request["id"]) - if not iou_instance: - return - - try: - iou_instance.clean_delete() - del self._iou_instances[request["id"]] - except IOUError as e: - self.send_custom_error(str(e)) - return - - self.send_response(True) - - @IModule.route("iou.update") - def iou_update(self, request): - """ - Updates an IOU instance - - Mandatory request parameters: - - id (IOU instance identifier) - - Optional request parameters: - - any setting to update - - initial_config_base64 (initial-config base64 encoded) - - Response parameters: - - updated settings - - :param request: JSON request - """ - - # validate the request - if not self.validate_request(request, IOU_UPDATE_SCHEMA): - return - - # get the instance - iou_instance = self.get_iou_instance(request["id"]) - if not iou_instance: - return - - config_path = os.path.join(iou_instance.working_dir, "initial-config.cfg") - try: - if "initial_config_base64" in request: - # a new initial-config has been pushed - config = base64.decodebytes(request["initial_config_base64"].encode("utf-8")).decode("utf-8") - config = "!\n" + config.replace("\r", "") - config = config.replace('%h', iou_instance.name) - try: - with open(config_path, "w") as f: - log.info("saving initial-config to {}".format(config_path)) - f.write(config) - except OSError as e: - raise IOUError("Could not save the configuration {}: {}".format(config_path, e)) - # update the request with the new local initial-config path - request["initial_config"] = os.path.basename(config_path) - elif "initial_config" in request: - if os.path.isfile(request["initial_config"]) and request["initial_config"] != config_path: - # this is a local file set in the GUI - try: - with open(request["initial_config"], "r", errors="replace") as f: - config = f.read() - with open(config_path, "w") as f: - config = "!\n" + config.replace("\r", "") - config = config.replace('%h', iou_instance.name) - f.write(config) - request["initial_config"] = os.path.basename(config_path) - except OSError as e: - raise IOUError("Could not save the configuration from {} to {}: {}".format(request["initial_config"], config_path, e)) - elif not os.path.isfile(config_path): - raise IOUError("Startup-config {} could not be found on this server".format(request["initial_config"])) - except IOUError as e: - self.send_custom_error(str(e)) - return - - # update the IOU settings - response = {} - for name, value in request.items(): - if hasattr(iou_instance, name) and getattr(iou_instance, name) != value: - try: - setattr(iou_instance, name, value) - response[name] = value - except IOUError as e: - self.send_custom_error(str(e)) - return - - self.send_response(response) - - @IModule.route("iou.start") - def vm_start(self, request): - """ - Starts an IOU instance. - - Mandatory request parameters: - - id (IOU instance identifier) - - Response parameters: - - True on success - - :param request: JSON request - """ - - # validate the request - if not self.validate_request(request, IOU_START_SCHEMA): - return - - # get the instance - iou_instance = self.get_iou_instance(request["id"]) - if not iou_instance: - return - - try: - iou_instance.iouyap = self._iouyap - if self._iourc: - iou_instance.iourc = self._iourc - else: - # if there is no IOURC file pushed by the client then use the server IOURC file - iou_instance.iourc = self._server_iourc_path - iou_instance.start() - except IOUError as e: - self.send_custom_error(str(e)) - return - self.send_response(True) - - @IModule.route("iou.stop") - def vm_stop(self, request): - """ - Stops an IOU instance. - - Mandatory request parameters: - - id (IOU instance identifier) - - Response parameters: - - True on success - - :param request: JSON request - """ - - # validate the request - if not self.validate_request(request, IOU_STOP_SCHEMA): - return - - # get the instance - iou_instance = self.get_iou_instance(request["id"]) - if not iou_instance: - return - - try: - iou_instance.stop() - except IOUError as e: - self.send_custom_error(str(e)) - return - self.send_response(True) - - @IModule.route("iou.reload") - def vm_reload(self, request): - """ - Reloads an IOU instance. - - Mandatory request parameters: - - id (IOU identifier) - - Response parameters: - - True on success - - :param request: JSON request - """ - - # validate the request - if not self.validate_request(request, IOU_RELOAD_SCHEMA): - return - - # get the instance - iou_instance = self.get_iou_instance(request["id"]) - if not iou_instance: - return - - try: - if iou_instance.is_running(): - iou_instance.stop() - iou_instance.start() - except IOUError as e: - self.send_custom_error(str(e)) - return - self.send_response(True) - - @IModule.route("iou.allocate_udp_port") - def allocate_udp_port(self, request): - """ - Allocates a UDP port in order to create an UDP NIO. - - Mandatory request parameters: - - id (IOU 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, IOU_ALLOCATE_UDP_PORT_SCHEMA): - return - - # get the instance - iou_instance = self.get_iou_instance(request["id"]) - if not iou_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(iou_instance.name, - iou_instance.id, - port, - self._host)) - response = {"lport": port, - "port_id": request["port_id"]} - self.send_response(response) - - @IModule.route("iou.add_nio") - def add_nio(self, request): - """ - Adds an NIO (Network Input/Output) for an IOU instance. - - Mandatory request parameters: - - id (IOU instance identifier) - - slot (slot number) - - 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) - - type "nio_generic_ethernet" - - ethernet_device (Ethernet device name e.g. eth0) - - type "nio_tap" - - tap_device (TAP device name e.g. tap0) - - Response parameters: - - port_id (unique port identifier) - - :param request: JSON request - """ - - # validate the request - if not self.validate_request(request, IOU_ADD_NIO_SCHEMA): - return - - # get the instance - iou_instance = self.get_iou_instance(request["id"]) - if not iou_instance: - return - - slot = request["slot"] - 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 IOUError("Could not create an UDP connection to {}:{}: {}".format(rhost, rport, e)) - nio = NIO_UDP(lport, rhost, rport) - elif request["nio"]["type"] == "nio_tap": - tap_device = request["nio"]["tap_device"] - if not has_privileged_access(self._iouyap): - raise IOUError("{} has no privileged access to {}.".format(self._iouyap, tap_device)) - nio = NIO_TAP(tap_device) - elif request["nio"]["type"] == "nio_generic_ethernet": - ethernet_device = request["nio"]["ethernet_device"] - if not has_privileged_access(self._iouyap): - raise IOUError("{} has no privileged access to {}.".format(self._iouyap, ethernet_device)) - nio = NIO_GenericEthernet(ethernet_device) - if not nio: - raise IOUError("Requested NIO does not exist or is not supported: {}".format(request["nio"]["type"])) - except IOUError as e: - self.send_custom_error(str(e)) - return - - try: - iou_instance.slot_add_nio_binding(slot, port, nio) - except IOUError as e: - self.send_custom_error(str(e)) - return - - self.send_response({"port_id": request["port_id"]}) - - @IModule.route("iou.delete_nio") - def delete_nio(self, request): - """ - Deletes an NIO (Network Input/Output). - - Mandatory request parameters: - - id (IOU instance identifier) - - slot (slot identifier) - - port (port identifier) - - Response parameters: - - True on success - - :param request: JSON request - """ - - # validate the request - if not self.validate_request(request, IOU_DELETE_NIO_SCHEMA): - return - - # get the instance - iou_instance = self.get_iou_instance(request["id"]) - if not iou_instance: - return - - slot = request["slot"] - port = request["port"] - try: - nio = iou_instance.slot_remove_nio_binding(slot, port) - if isinstance(nio, NIO_UDP) and nio.lport in self._allocated_udp_ports: - self._allocated_udp_ports.remove(nio.lport) - except IOUError as e: - self.send_custom_error(str(e)) - return - - self.send_response(True) - - @IModule.route("iou.start_capture") - def start_capture(self, request): - """ - Starts a packet capture. - - Mandatory request parameters: - - id (vm identifier) - - slot (slot number) - - port (port number) - - port_id (port identifier) - - capture_file_name - - Optional request parameters: - - data_link_type (PCAP DLT_* value) - - Response parameters: - - port_id (port identifier) - - capture_file_path (path to the capture file) - - :param request: JSON request - """ - - # validate the request - if not self.validate_request(request, IOU_START_CAPTURE_SCHEMA): - return - - # get the instance - iou_instance = self.get_iou_instance(request["id"]) - if not iou_instance: - return - - slot = request["slot"] - port = request["port"] - capture_file_name = request["capture_file_name"] - data_link_type = request.get("data_link_type") - - try: - capture_file_path = os.path.join(self._working_dir, "captures", capture_file_name) - iou_instance.start_capture(slot, port, capture_file_path, data_link_type) - except IOUError as e: - self.send_custom_error(str(e)) - return - - response = {"port_id": request["port_id"], - "capture_file_path": capture_file_path} - self.send_response(response) - - @IModule.route("iou.stop_capture") - def stop_capture(self, request): - """ - Stops a packet capture. - - Mandatory request parameters: - - id (vm identifier) - - slot (slot number) - - port (port number) - - port_id (port identifier) - - Response parameters: - - port_id (port identifier) - - :param request: JSON request - """ - - # validate the request - if not self.validate_request(request, IOU_STOP_CAPTURE_SCHEMA): - return - - # get the instance - iou_instance = self.get_iou_instance(request["id"]) - if not iou_instance: - return - - slot = request["slot"] - port = request["port"] - try: - iou_instance.stop_capture(slot, port) - except IOUError as e: - self.send_custom_error(str(e)) - return - - response = {"port_id": request["port_id"]} - self.send_response(response) - - @IModule.route("iou.export_config") - def export_config(self, request): - """ - Exports the initial-config from an IOU instance. - - Mandatory request parameters: - - id (vm identifier) - - Response parameters: - - initial_config_base64 (initial-config base64 encoded) - - False if no configuration can be exported - """ - - # validate the request - if not self.validate_request(request, IOU_EXPORT_CONFIG_SCHEMA): - return - - # get the instance - iou_instance = self.get_iou_instance(request["id"]) - if not iou_instance: - return - - if not iou_instance.initial_config: - self.send_custom_error("unable to export the initial-config because it doesn't exist") - return - - response = {} - initial_config_path = os.path.join(iou_instance.working_dir, iou_instance.initial_config) - try: - with open(initial_config_path, "rb") as f: - config = f.read() - response["initial_config_base64"] = base64.encodebytes(config).decode("utf-8") - except OSError as e: - self.send_custom_error("unable to export the initial-config: {}".format(e)) - return - - if not response: - self.send_response(False) - else: - self.send_response(response) - - @IModule.route("iou.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) diff --git a/gns3server/old_modules/iou/adapters/__init__.py b/gns3server/old_modules/iou/adapters/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/gns3server/old_modules/iou/adapters/adapter.py b/gns3server/old_modules/iou/adapters/adapter.py deleted file mode 100644 index 06645e56..00000000 --- a/gns3server/old_modules/iou/adapters/adapter.py +++ /dev/null @@ -1,105 +0,0 @@ -# -*- 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 . - - -class Adapter(object): - - """ - Base class for adapters. - - :param interfaces: number of interfaces supported by this adapter. - """ - - def __init__(self, interfaces=4): - - 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 diff --git a/gns3server/old_modules/iou/adapters/ethernet_adapter.py b/gns3server/old_modules/iou/adapters/ethernet_adapter.py deleted file mode 100644 index bf96362f..00000000 --- a/gns3server/old_modules/iou/adapters/ethernet_adapter.py +++ /dev/null @@ -1,32 +0,0 @@ -# -*- 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 . - -from .adapter import Adapter - - -class EthernetAdapter(Adapter): - - """ - IOU Ethernet adapter. - """ - - def __init__(self): - Adapter.__init__(self, interfaces=4) - - def __str__(self): - - return "IOU Ethernet adapter" diff --git a/gns3server/old_modules/iou/adapters/serial_adapter.py b/gns3server/old_modules/iou/adapters/serial_adapter.py deleted file mode 100644 index ca7d3200..00000000 --- a/gns3server/old_modules/iou/adapters/serial_adapter.py +++ /dev/null @@ -1,32 +0,0 @@ -# -*- 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 . - -from .adapter import Adapter - - -class SerialAdapter(Adapter): - - """ - IOU Serial adapter. - """ - - def __init__(self): - Adapter.__init__(self, interfaces=4) - - def __str__(self): - - return "IOU Serial adapter" diff --git a/gns3server/old_modules/iou/iou_device.py b/gns3server/old_modules/iou/iou_device.py deleted file mode 100644 index ff8ff2c3..00000000 --- a/gns3server/old_modules/iou/iou_device.py +++ /dev/null @@ -1,1069 +0,0 @@ -# -*- 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 . - -""" -IOU device management (creates command line, processes, files etc.) in -order to run an IOU instance. -""" - -import os -import re -import signal -import subprocess -import argparse -import threading -import configparser -import shutil - -from .ioucon import start_ioucon -from .iou_error import IOUError -from .adapters.ethernet_adapter import EthernetAdapter -from .adapters.serial_adapter import SerialAdapter -from .nios.nio_udp import NIO_UDP -from .nios.nio_tap import NIO_TAP -from .nios.nio_generic_ethernet import NIO_GenericEthernet -from ..attic import find_unused_port - -import logging -log = logging.getLogger(__name__) - - -class IOUDevice(object): - - """ - IOU device implementation. - - :param name: name of this IOU device - :param path: path to IOU executable - :param working_dir: path to a working directory - :param iou_id: IOU instance ID - :param console: TCP console port - :param console_host: IP address to bind for console connections - :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, - iou_id=None, - console=None, - console_host="0.0.0.0", - console_start_port_range=4001, - console_end_port_range=4512): - - if not iou_id: - # find an instance identifier if none is provided (0 < id <= 512) - self._id = 0 - for identifier in range(1, 513): - if identifier not in self._instances: - self._id = identifier - self._instances.append(self._id) - break - - if self._id == 0: - raise IOUError("Maximum number of IOU instances reached") - else: - if iou_id in self._instances: - raise IOUError("IOU identifier {} is already used by another IOU device".format(iou_id)) - self._id = iou_id - self._instances.append(self._id) - - self._name = name - self._path = path - self._iourc = "" - self._iouyap = "" - self._console = console - self._working_dir = None - self._command = [] - self._process = None - self._iouyap_process = None - self._iou_stdout_file = "" - self._iouyap_stdout_file = "" - self._ioucon_thead = None - self._ioucon_thread_stop_event = None - self._started = False - self._console_host = console_host - self._console_start_port_range = console_start_port_range - self._console_end_port_range = console_end_port_range - - # IOU settings - self._ethernet_adapters = [EthernetAdapter(), EthernetAdapter()] # one adapter = 4 interfaces - self._serial_adapters = [SerialAdapter(), SerialAdapter()] # one adapter = 4 interfaces - self._slots = self._ethernet_adapters + self._serial_adapters - self._use_default_iou_values = True # for RAM & NVRAM values - self._nvram = 128 # Kilobytes - self._initial_config = "" - self._ram = 256 # Megabytes - self._l1_keepalives = False # used to overcome the always-up Ethernet interfaces (not supported by all IOSes). - - working_dir_path = os.path.join(working_dir, "iou", "device-{}".format(self._id)) - - if iou_id and not os.path.isdir(working_dir_path): - raise IOUError("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._console_host, - ignore_ports=self._allocated_console_ports) - except Exception as e: - raise IOUError(e) - - if self._console in self._allocated_console_ports: - raise IOUError("Console port {} is already in used another IOU device".format(console)) - self._allocated_console_ports.append(self._console) - - log.info("IOU device {name} [id={id}] has been created".format(name=self._name, - id=self._id)) - - def defaults(self): - """ - Returns all the default attribute values for IOU. - - :returns: default values (dictionary) - """ - - iou_defaults = {"name": self._name, - "path": self._path, - "intial_config": self._initial_config, - "use_default_iou_values": self._use_default_iou_values, - "ram": self._ram, - "nvram": self._nvram, - "ethernet_adapters": len(self._ethernet_adapters), - "serial_adapters": len(self._serial_adapters), - "console": self._console, - "l1_keepalives": self._l1_keepalives} - - return iou_defaults - - @property - def id(self): - """ - Returns the unique ID for this IOU device. - - :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 IOU device. - - :returns: name - """ - - return self._name - - @name.setter - def name(self, new_name): - """ - Sets the name of this IOU device. - - :param new_name: name - """ - - if self._initial_config: - # update the initial-config - config_path = os.path.join(self._working_dir, "initial-config.cfg") - if os.path.isfile(config_path): - try: - with open(config_path, "r+", errors="replace") as f: - old_config = f.read() - new_config = old_config.replace(self._name, new_name) - f.seek(0) - f.write(new_config) - except OSError as e: - raise IOUError("Could not amend the configuration {}: {}".format(config_path, e)) - - log.info("IOU {name} [id={id}]: renamed to {new_name}".format(name=self._name, - id=self._id, - new_name=new_name)) - self._name = new_name - - @property - def path(self): - """ - Returns the path to the IOU executable. - - :returns: path to IOU - """ - - return self._path - - @path.setter - def path(self, path): - """ - Sets the path to the IOU executable. - - :param path: path to IOU - """ - - self._path = path - log.info("IOU {name} [id={id}]: path changed to {path}".format(name=self._name, - id=self._id, - path=path)) - - @property - def iourc(self): - """ - Returns the path to the iourc file. - - :returns: path to the iourc file - """ - - return self._iourc - - @iourc.setter - def iourc(self, iourc): - """ - Sets the path to the iourc file. - - :param iourc: path to the iourc file. - """ - - self._iourc = iourc - log.info("IOU {name} [id={id}]: iourc file path set to {path}".format(name=self._name, - id=self._id, - path=self._iourc)) - - @property - def iouyap(self): - """ - Returns the path to iouyap - - :returns: path to iouyap - """ - - return self._iouyap - - @iouyap.setter - def iouyap(self, iouyap): - """ - Sets the path to iouyap. - - :param iouyap: path to iouyap - """ - - self._iouyap = iouyap - log.info("IOU {name} [id={id}]: iouyap path set to {path}".format(name=self._name, - id=self._id, - path=self._iouyap)) - - @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 IOU. - - :param working_dir: path to the working directory - """ - - try: - os.makedirs(working_dir) - except FileExistsError: - pass - except OSError as e: - raise IOUError("Could not create working directory {}: {}".format(working_dir, e)) - - self._working_dir = working_dir - log.info("IOU {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 IOUError("Console port {} is already used by another IOU device".format(console)) - - self._allocated_console_ports.remove(self._console) - self._console = console - self._allocated_console_ports.append(self._console) - log.info("IOU {name} [id={id}]: console port set to {port}".format(name=self._name, - id=self._id, - port=console)) - - def command(self): - """ - Returns the IOU command line. - - :returns: IOU command line (string) - """ - - return " ".join(self._build_command()) - - def delete(self): - """ - Deletes this IOU device. - """ - - 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("IOU device {name} [id={id}] has been deleted".format(name=self._name, - id=self._id)) - - def clean_delete(self): - """ - Deletes this IOU device & all files (nvram, initial-config etc.) - """ - - 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 IOU device {name} [id={id}]: {error}".format(name=self._name, - id=self._id, - error=e)) - return - - log.info("IOU device {name} [id={id}] has been deleted (including associated files)".format(name=self._name, - id=self._id)) - - @property - def started(self): - """ - Returns either this IOU device has been started or not. - - :returns: boolean - """ - - return self._started - - def _update_iouyap_config(self): - """ - Updates the iouyap.ini file. - """ - - iouyap_ini = os.path.join(self._working_dir, "iouyap.ini") - - config = configparser.ConfigParser() - config["default"] = {"netmap": "NETMAP", - "base_port": "49000"} - - bay_id = 0 - for adapter in self._slots: - unit_id = 0 - for unit in adapter.ports.keys(): - nio = adapter.get_nio(unit) - if nio: - connection = None - if isinstance(nio, NIO_UDP): - # UDP tunnel - connection = {"tunnel_udp": "{lport}:{rhost}:{rport}".format(lport=nio.lport, - rhost=nio.rhost, - rport=nio.rport)} - elif isinstance(nio, NIO_TAP): - # TAP interface - connection = {"tap_dev": "{tap_device}".format(tap_device=nio.tap_device)} - - elif isinstance(nio, NIO_GenericEthernet): - # Ethernet interface - connection = {"eth_dev": "{ethernet_device}".format(ethernet_device=nio.ethernet_device)} - - if connection: - interface = "{iouyap_id}:{bay}/{unit}".format(iouyap_id=str(self._id + 512), bay=bay_id, unit=unit_id) - config[interface] = connection - - if nio.capturing: - pcap_data_link_type = nio.pcap_data_link_type.upper() - if pcap_data_link_type == "DLT_PPP_SERIAL": - pcap_protocol = "ppp" - elif pcap_data_link_type == "DLT_C_HDLC": - pcap_protocol = "hdlc" - elif pcap_data_link_type == "DLT_FRELAY": - pcap_protocol = "fr" - else: - pcap_protocol = "ethernet" - capture_info = {"pcap_file": "{pcap_file}".format(pcap_file=nio.pcap_output_file), - "pcap_protocol": pcap_protocol, - "pcap_overwrite": "y"} - config[interface].update(capture_info) - - unit_id += 1 - bay_id += 1 - - try: - with open(iouyap_ini, "w") as config_file: - config.write(config_file) - log.info("IOU {name} [id={id}]: iouyap.ini updated".format(name=self._name, - id=self._id)) - except OSError as e: - raise IOUError("Could not create {}: {}".format(iouyap_ini, e)) - - def _create_netmap_config(self): - """ - Creates the NETMAP file. - """ - - netmap_path = os.path.join(self._working_dir, "NETMAP") - try: - with open(netmap_path, "w") as f: - for bay in range(0, 16): - for unit in range(0, 4): - f.write("{iouyap_id}:{bay}/{unit}{iou_id:>5d}:{bay}/{unit}\n".format(iouyap_id=str(self._id + 512), - bay=bay, - unit=unit, - iou_id=self._id)) - log.info("IOU {name} [id={id}]: NETMAP file created".format(name=self._name, - id=self._id)) - except OSError as e: - raise IOUError("Could not create {}: {}".format(netmap_path, e)) - - def _start_ioucon(self): - """ - Starts ioucon thread (for console connections). - """ - - if not self._ioucon_thead: - telnet_server = "{}:{}".format(self._console_host, self.console) - log.info("starting ioucon for IOU instance {} to accept Telnet connections on {}".format(self._name, telnet_server)) - args = argparse.Namespace(appl_id=str(self._id), debug=False, escape='^^', telnet_limit=0, telnet_server=telnet_server) - self._ioucon_thread_stop_event = threading.Event() - self._ioucon_thead = threading.Thread(target=start_ioucon, args=(args, self._ioucon_thread_stop_event)) - self._ioucon_thead.start() - - def _start_iouyap(self): - """ - Starts iouyap (handles connections to and from this IOU device). - """ - - try: - self._update_iouyap_config() - command = [self._iouyap, "-q", str(self._id + 512)] # iouyap has always IOU ID + 512 - log.info("starting iouyap: {}".format(command)) - self._iouyap_stdout_file = os.path.join(self._working_dir, "iouyap.log") - log.info("logging to {}".format(self._iouyap_stdout_file)) - with open(self._iouyap_stdout_file, "w") as fd: - self._iouyap_process = subprocess.Popen(command, - stdout=fd, - stderr=subprocess.STDOUT, - cwd=self._working_dir) - - log.info("iouyap started PID={}".format(self._iouyap_process.pid)) - except (OSError, subprocess.SubprocessError) as e: - iouyap_stdout = self.read_iouyap_stdout() - log.error("could not start iouyap: {}\n{}".format(e, iouyap_stdout)) - raise IOUError("Could not start iouyap: {}\n{}".format(e, iouyap_stdout)) - - def _library_check(self): - """ - Checks for missing shared library dependencies in the IOU image. - """ - - try: - output = subprocess.check_output(["ldd", self._path]) - except (FileNotFoundError, subprocess.SubprocessError) as e: - log.warn("could not determine the shared library dependencies for {}: {}".format(self._path, e)) - return - - p = re.compile("([\.\w]+)\s=>\s+not found") - missing_libs = p.findall(output.decode("utf-8")) - if missing_libs: - raise IOUError("The following shared library dependencies cannot be found for IOU image {}: {}".format(self._path, - ", ".join(missing_libs))) - - def start(self): - """ - Starts the IOU process. - """ - - if not self.is_running(): - - if not os.path.isfile(self._path) or not os.path.exists(self._path): - if os.path.islink(self._path): - raise IOUError("IOU image '{}' linked to '{}' is not accessible".format(self._path, os.path.realpath(self._path))) - else: - raise IOUError("IOU image '{}' is not accessible".format(self._path)) - - try: - with open(self._path, "rb") as f: - # read the first 7 bytes of the file. - elf_header_start = f.read(7) - except OSError as e: - raise IOUError("Cannot read ELF header for IOU image '{}': {}".format(self._path, e)) - - # IOU images must start with the ELF magic number, be 32-bit, little endian - # and have an ELF version of 1 normal IOS image are big endian! - if elf_header_start != b'\x7fELF\x01\x01\x01': - raise IOUError("'{}' is not a valid IOU image".format(self._path)) - - if not os.access(self._path, os.X_OK): - raise IOUError("IOU image '{}' is not executable".format(self._path)) - - self._library_check() - - if not self._iourc or not os.path.isfile(self._iourc): - raise IOUError("A valid iourc file is necessary to start IOU") - - if not self._iouyap or not os.path.isfile(self._iouyap): - raise IOUError("iouyap is necessary to start IOU") - - self._create_netmap_config() - # created a environment variable pointing to the iourc file. - env = os.environ.copy() - env["IOURC"] = self._iourc - self._command = self._build_command() - try: - log.info("starting IOU: {}".format(self._command)) - self._iou_stdout_file = os.path.join(self._working_dir, "iou.log") - log.info("logging to {}".format(self._iou_stdout_file)) - with open(self._iou_stdout_file, "w") as fd: - self._process = subprocess.Popen(self._command, - stdout=fd, - stderr=subprocess.STDOUT, - cwd=self._working_dir, - env=env) - log.info("IOU instance {} started PID={}".format(self._id, self._process.pid)) - self._started = True - except FileNotFoundError as e: - raise IOUError("could not start IOU: {}: 32-bit binary support is probably not installed".format(e)) - except (OSError, subprocess.SubprocessError) as e: - iou_stdout = self.read_iou_stdout() - log.error("could not start IOU {}: {}\n{}".format(self._path, e, iou_stdout)) - raise IOUError("could not start IOU {}: {}\n{}".format(self._path, e, iou_stdout)) - - # start console support - self._start_ioucon() - # connections support - self._start_iouyap() - - def stop(self): - """ - Stops the IOU process. - """ - - # stop console support - if self._ioucon_thead: - self._ioucon_thread_stop_event.set() - if self._ioucon_thead.is_alive(): - self._ioucon_thead.join(timeout=3.0) # wait for the thread to free the console port - self._ioucon_thead = None - - # stop iouyap - if self.is_iouyap_running(): - log.info("stopping iouyap PID={} for IOU instance {}".format(self._iouyap_process.pid, self._id)) - try: - self._iouyap_process.terminate() - self._iouyap_process.wait(1) - except subprocess.TimeoutExpired: - self._iouyap_process.kill() - if self._iouyap_process.poll() is None: - log.warn("iouyap PID={} for IOU instance {} is still running".format(self._iouyap_process.pid, - self._id)) - self._iouyap_process = None - - # stop the IOU process - if self.is_running(): - log.info("stopping IOU instance {} PID={}".format(self._id, self._process.pid)) - try: - self._process.terminate() - self._process.wait(1) - except subprocess.TimeoutExpired: - self._process.kill() - if self._process.poll() is None: - log.warn("IOU instance {} PID={} is still running".format(self._id, - self._process.pid)) - self._process = None - self._started = False - - def read_iou_stdout(self): - """ - Reads the standard output of the IOU process. - Only use when the process has been stopped or has crashed. - """ - - output = "" - if self._iou_stdout_file: - try: - with open(self._iou_stdout_file, errors="replace") as file: - output = file.read() - except OSError as e: - log.warn("could not read {}: {}".format(self._iou_stdout_file, e)) - return output - - def read_iouyap_stdout(self): - """ - Reads the standard output of the iouyap process. - Only use when the process has been stopped or has crashed. - """ - - output = "" - if self._iouyap_stdout_file: - try: - with open(self._iouyap_stdout_file, errors="replace") as file: - output = file.read() - except OSError as e: - log.warn("could not read {}: {}".format(self._iouyap_stdout_file, e)) - return output - - def is_running(self): - """ - Checks if the IOU process is running - - :returns: True or False - """ - - if self._process and self._process.poll() is None: - return True - return False - - def is_iouyap_running(self): - """ - Checks if the iouyap process is running - - :returns: True or False - """ - - if self._iouyap_process and self._iouyap_process.poll() is None: - return True - return False - - def slot_add_nio_binding(self, slot_id, port_id, nio): - """ - Adds a slot NIO binding. - - :param slot_id: slot ID - :param port_id: port ID - :param nio: NIO instance to add to the slot/port - """ - - try: - adapter = self._slots[slot_id] - except IndexError: - raise IOUError("Slot {slot_id} doesn't exist on IOU {name}".format(name=self._name, - slot_id=slot_id)) - - if not adapter.port_exists(port_id): - raise IOUError("Port {port_id} doesn't exist in adapter {adapter}".format(adapter=adapter, - port_id=port_id)) - - adapter.add_nio(port_id, nio) - log.info("IOU {name} [id={id}]: {nio} added to {slot_id}/{port_id}".format(name=self._name, - id=self._id, - nio=nio, - slot_id=slot_id, - port_id=port_id)) - if self.is_iouyap_running(): - self._update_iouyap_config() - os.kill(self._iouyap_process.pid, signal.SIGHUP) - - def slot_remove_nio_binding(self, slot_id, port_id): - """ - Removes a slot NIO binding. - - :param slot_id: slot ID - :param port_id: port ID - - :returns: NIO instance - """ - - try: - adapter = self._slots[slot_id] - except IndexError: - raise IOUError("Slot {slot_id} doesn't exist on IOU {name}".format(name=self._name, - slot_id=slot_id)) - - if not adapter.port_exists(port_id): - raise IOUError("Port {port_id} doesn't exist in adapter {adapter}".format(adapter=adapter, - port_id=port_id)) - - nio = adapter.get_nio(port_id) - adapter.remove_nio(port_id) - log.info("IOU {name} [id={id}]: {nio} removed from {slot_id}/{port_id}".format(name=self._name, - id=self._id, - nio=nio, - slot_id=slot_id, - port_id=port_id)) - if self.is_iouyap_running(): - self._update_iouyap_config() - os.kill(self._iouyap_process.pid, signal.SIGHUP) - - return nio - - def _enable_l1_keepalives(self, command): - """ - Enables L1 keepalive messages if supported. - - :param command: command line - """ - - env = os.environ.copy() - env["IOURC"] = self._iourc - try: - output = subprocess.check_output([self._path, "-h"], stderr=subprocess.STDOUT, cwd=self._working_dir, env=env) - if re.search("-l\s+Enable Layer 1 keepalive messages", output.decode("utf-8")): - command.extend(["-l"]) - else: - raise IOUError("layer 1 keepalive messages are not supported by {}".format(os.path.basename(self._path))) - except (OSError, subprocess.SubprocessError) as e: - log.warn("could not determine if layer 1 keepalive messages are supported by {}: {}".format(os.path.basename(self._path), e)) - - def _build_command(self): - """ - Command to start the IOU process. - (to be passed to subprocess.Popen()) - - IOU command line: - Usage: [options] - : unix-js-m | unix-is-m | unix-i-m | ... - : instance identifier (0 < id <= 1024) - Options: - -e Number of Ethernet interfaces (default 2) - -s Number of Serial interfaces (default 2) - -n Size of nvram in Kb (default 64KB) - -b IOS debug string - -c Configuration file name - -d Generate debug information - -t Netio message trace - -q Suppress informational messages - -h Display this help - -C Turn off use of host clock - -m Megabytes of router memory (default 256MB) - -L Disable local console, use remote console - -l Enable Layer 1 keepalive messages - -u UDP port base for distributed networks - -R Ignore options from the IOURC file - -U Disable unix: file system location - -W Disable watchdog timer - -N Ignore the NETMAP file - """ - - command = [self._path] - if len(self._ethernet_adapters) != 2: - command.extend(["-e", str(len(self._ethernet_adapters))]) - if len(self._serial_adapters) != 2: - command.extend(["-s", str(len(self._serial_adapters))]) - if not self.use_default_iou_values: - command.extend(["-n", str(self._nvram)]) - command.extend(["-m", str(self._ram)]) - command.extend(["-L"]) # disable local console, use remote console - if self._initial_config: - command.extend(["-c", self._initial_config]) - if self._l1_keepalives: - self._enable_l1_keepalives(command) - command.extend([str(self._id)]) - return command - - @property - def use_default_iou_values(self): - """ - Returns if this device uses the default IOU image values. - - :returns: boolean - """ - - return self._use_default_iou_values - - @use_default_iou_values.setter - def use_default_iou_values(self, state): - """ - Sets if this device uses the default IOU image values. - - :param state: boolean - """ - - self._use_default_iou_values = state - if state: - log.info("IOU {name} [id={id}]: uses the default IOU image values".format(name=self._name, id=self._id)) - else: - log.info("IOU {name} [id={id}]: does not use the default IOU image values".format(name=self._name, id=self._id)) - - @property - def l1_keepalives(self): - """ - Returns either layer 1 keepalive messages option is enabled or disabled. - - :returns: boolean - """ - - return self._l1_keepalives - - @l1_keepalives.setter - def l1_keepalives(self, state): - """ - Enables or disables layer 1 keepalive messages. - - :param state: boolean - """ - - self._l1_keepalives = state - if state: - log.info("IOU {name} [id={id}]: has activated layer 1 keepalive messages".format(name=self._name, id=self._id)) - else: - log.info("IOU {name} [id={id}]: has deactivated layer 1 keepalive messages".format(name=self._name, id=self._id)) - - @property - def ram(self): - """ - Returns the amount of RAM allocated to this IOU instance. - - :returns: amount of RAM in Mbytes (integer) - """ - - return self._ram - - @ram.setter - def ram(self, ram): - """ - Sets amount of RAM allocated to this IOU instance. - - :param ram: amount of RAM in Mbytes (integer) - """ - - if self._ram == ram: - return - - log.info("IOU {name} [id={id}]: RAM updated from {old_ram}MB to {new_ram}MB".format(name=self._name, - id=self._id, - old_ram=self._ram, - new_ram=ram)) - - self._ram = ram - - @property - def nvram(self): - """ - Returns the mount of NVRAM allocated to this IOU instance. - - :returns: amount of NVRAM in Kbytes (integer) - """ - - return self._nvram - - @nvram.setter - def nvram(self, nvram): - """ - Sets amount of NVRAM allocated to this IOU instance. - - :param nvram: amount of NVRAM in Kbytes (integer) - """ - - if self._nvram == nvram: - return - - log.info("IOU {name} [id={id}]: NVRAM updated from {old_nvram}KB to {new_nvram}KB".format(name=self._name, - id=self._id, - old_nvram=self._nvram, - new_nvram=nvram)) - self._nvram = nvram - - @property - def initial_config(self): - """ - Returns the initial-config for this IOU instance. - - :returns: path to initial-config file - """ - - return self._initial_config - - @initial_config.setter - def initial_config(self, initial_config): - """ - Sets the initial-config for this IOU instance. - - :param initial_config: path to initial-config file - """ - - self._initial_config = initial_config - log.info("IOU {name} [id={id}]: initial_config set to {config}".format(name=self._name, - id=self._id, - config=self._initial_config)) - - @property - def ethernet_adapters(self): - """ - Returns the number of Ethernet adapters for this IOU instance. - - :returns: number of adapters - """ - - return len(self._ethernet_adapters) - - @ethernet_adapters.setter - def ethernet_adapters(self, ethernet_adapters): - """ - Sets the number of Ethernet adapters for this IOU instance. - - :param ethernet_adapters: number of adapters - """ - - self._ethernet_adapters.clear() - for _ in range(0, ethernet_adapters): - self._ethernet_adapters.append(EthernetAdapter()) - - log.info("IOU {name} [id={id}]: number of Ethernet adapters changed to {adapters}".format(name=self._name, - id=self._id, - adapters=len(self._ethernet_adapters))) - - self._slots = self._ethernet_adapters + self._serial_adapters - - @property - def serial_adapters(self): - """ - Returns the number of Serial adapters for this IOU instance. - - :returns: number of adapters - """ - - return len(self._serial_adapters) - - @serial_adapters.setter - def serial_adapters(self, serial_adapters): - """ - Sets the number of Serial adapters for this IOU instance. - - :param serial_adapters: number of adapters - """ - - self._serial_adapters.clear() - for _ in range(0, serial_adapters): - self._serial_adapters.append(SerialAdapter()) - - log.info("IOU {name} [id={id}]: number of Serial adapters changed to {adapters}".format(name=self._name, - id=self._id, - adapters=len(self._serial_adapters))) - - self._slots = self._ethernet_adapters + self._serial_adapters - - def start_capture(self, slot_id, port_id, output_file, data_link_type="DLT_EN10MB"): - """ - Starts a packet capture. - - :param slot_id: slot ID - :param port_id: port ID - :param port: allocated port - :param output_file: PCAP destination file for the capture - :param data_link_type: PCAP data link type (DLT_*), default is DLT_EN10MB - """ - - try: - adapter = self._slots[slot_id] - except IndexError: - raise IOUError("Slot {slot_id} doesn't exist on IOU {name}".format(name=self._name, - slot_id=slot_id)) - - if not adapter.port_exists(port_id): - raise IOUError("Port {port_id} doesn't exist in adapter {adapter}".format(adapter=adapter, - port_id=port_id)) - - nio = adapter.get_nio(port_id) - if nio.capturing: - raise IOUError("Packet capture is already activated on {slot_id}/{port_id}".format(slot_id=slot_id, - port_id=port_id)) - - try: - os.makedirs(os.path.dirname(output_file)) - except FileExistsError: - pass - except OSError as e: - raise IOUError("Could not create captures directory {}".format(e)) - - nio.startPacketCapture(output_file, data_link_type) - - log.info("IOU {name} [id={id}]: starting packet capture on {slot_id}/{port_id}".format(name=self._name, - id=self._id, - slot_id=slot_id, - port_id=port_id)) - - if self.is_iouyap_running(): - self._update_iouyap_config() - os.kill(self._iouyap_process.pid, signal.SIGHUP) - - def stop_capture(self, slot_id, port_id): - """ - Stops a packet capture. - - :param slot_id: slot ID - :param port_id: port ID - """ - - try: - adapter = self._slots[slot_id] - except IndexError: - raise IOUError("Slot {slot_id} doesn't exist on IOU {name}".format(name=self._name, - slot_id=slot_id)) - - if not adapter.port_exists(port_id): - raise IOUError("Port {port_id} doesn't exist in adapter {adapter}".format(adapter=adapter, - port_id=port_id)) - - nio = adapter.get_nio(port_id) - nio.stopPacketCapture() - log.info("IOU {name} [id={id}]: stopping packet capture on {slot_id}/{port_id}".format(name=self._name, - id=self._id, - slot_id=slot_id, - port_id=port_id)) - if self.is_iouyap_running(): - self._update_iouyap_config() - os.kill(self._iouyap_process.pid, signal.SIGHUP) diff --git a/gns3server/old_modules/iou/iou_error.py b/gns3server/old_modules/iou/iou_error.py deleted file mode 100644 index 8aac176f..00000000 --- a/gns3server/old_modules/iou/iou_error.py +++ /dev/null @@ -1,39 +0,0 @@ -# -*- 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 . - -""" -Custom exceptions for IOU module. -""" - - -class IOUError(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 diff --git a/gns3server/old_modules/iou/nios/__init__.py b/gns3server/old_modules/iou/nios/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/gns3server/old_modules/iou/nios/nio.py b/gns3server/old_modules/iou/nios/nio.py deleted file mode 100644 index 0c8e610e..00000000 --- a/gns3server/old_modules/iou/nios/nio.py +++ /dev/null @@ -1,80 +0,0 @@ -# -*- 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 . - -""" -Base interface for NIOs. -""" - - -class NIO(object): - - """ - Network Input/Output. - """ - - def __init__(self): - - self._capturing = False - self._pcap_output_file = "" - self._pcap_data_link_type = "" - - def startPacketCapture(self, pcap_output_file, pcap_data_link_type="DLT_EN10MB"): - """ - - :param pcap_output_file: PCAP destination file for the capture - :param pcap_data_link_type: PCAP data link type (DLT_*), default is DLT_EN10MB - """ - - self._capturing = True - self._pcap_output_file = pcap_output_file - self._pcap_data_link_type = pcap_data_link_type - - def stopPacketCapture(self): - - self._capturing = False - self._pcap_output_file = "" - self._pcap_data_link_type = "" - - @property - def capturing(self): - """ - Returns either a capture is configured on this NIO. - - :returns: boolean - """ - - return self._capturing - - @property - def pcap_output_file(self): - """ - Returns the path to the PCAP output file. - - :returns: path to the PCAP output file - """ - - return self._pcap_output_file - - @property - def pcap_data_link_type(self): - """ - Returns the PCAP data link type - - :returns: PCAP data link type (DLT_* value) - """ - - return self._pcap_data_link_type diff --git a/gns3server/old_modules/iou/nios/nio_generic_ethernet.py b/gns3server/old_modules/iou/nios/nio_generic_ethernet.py deleted file mode 100644 index 709e6474..00000000 --- a/gns3server/old_modules/iou/nios/nio_generic_ethernet.py +++ /dev/null @@ -1,50 +0,0 @@ -# -*- 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 . - -""" -Interface for generic Ethernet NIOs (PCAP library). -""" - -from .nio import NIO - - -class NIO_GenericEthernet(NIO): - - """ - Generic Ethernet NIO. - - :param ethernet_device: Ethernet device name (e.g. eth0) - """ - - def __init__(self, ethernet_device): - - NIO.__init__(self) - self._ethernet_device = ethernet_device - - @property - def ethernet_device(self): - """ - Returns the Ethernet device used by this NIO. - - :returns: the Ethernet device name - """ - - return self._ethernet_device - - def __str__(self): - - return "NIO Ethernet" diff --git a/gns3server/old_modules/iou/nios/nio_tap.py b/gns3server/old_modules/iou/nios/nio_tap.py deleted file mode 100644 index f6b1663f..00000000 --- a/gns3server/old_modules/iou/nios/nio_tap.py +++ /dev/null @@ -1,50 +0,0 @@ -# -*- 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 . - -""" -Interface for TAP NIOs (UNIX based OSes only). -""" - -from .nio import NIO - - -class NIO_TAP(NIO): - - """ - TAP NIO. - - :param tap_device: TAP device name (e.g. tap0) - """ - - def __init__(self, tap_device): - - NIO.__init__(self) - self._tap_device = tap_device - - @property - def tap_device(self): - """ - Returns the TAP device used by this NIO. - - :returns: the TAP device name - """ - - return self._tap_device - - def __str__(self): - - return "NIO TAP" diff --git a/gns3server/old_modules/iou/nios/nio_udp.py b/gns3server/old_modules/iou/nios/nio_udp.py deleted file mode 100644 index 3b25f0c4..00000000 --- a/gns3server/old_modules/iou/nios/nio_udp.py +++ /dev/null @@ -1,76 +0,0 @@ -# -*- 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 . - -""" -Interface for UDP NIOs. -""" - -from .nio import NIO - - -class NIO_UDP(NIO): - - """ - 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): - - NIO.__init__(self) - 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" diff --git a/gns3server/old_modules/iou/schemas.py b/gns3server/old_modules/iou/schemas.py deleted file mode 100644 index f1315ec3..00000000 --- a/gns3server/old_modules/iou/schemas.py +++ /dev/null @@ -1,472 +0,0 @@ -# -*- 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 . - - -IOU_CREATE_SCHEMA = { - "$schema": "http://json-schema.org/draft-04/schema#", - "description": "Request validation to create a new IOU instance", - "type": "object", - "properties": { - "name": { - "description": "IOU device name", - "type": "string", - "minLength": 1, - }, - "iou_id": { - "description": "IOU device instance ID", - "type": "integer" - }, - "console": { - "description": "console TCP port", - "minimum": 1, - "maximum": 65535, - "type": "integer" - }, - "path": { - "description": "path to the IOU executable", - "type": "string", - "minLength": 1, - }, - "cloud_path": { - "description": "Path to the image in the cloud object store", - "type": "string", - } - }, - "additionalProperties": False, - "required": ["name", "path"], -} - -IOU_DELETE_SCHEMA = { - "$schema": "http://json-schema.org/draft-04/schema#", - "description": "Request validation to delete an IOU instance", - "type": "object", - "properties": { - "id": { - "description": "IOU device instance ID", - "type": "integer" - }, - }, - "additionalProperties": False, - "required": ["id"] -} - -IOU_UPDATE_SCHEMA = { - "$schema": "http://json-schema.org/draft-04/schema#", - "description": "Request validation to update an IOU instance", - "type": "object", - "properties": { - "id": { - "description": "IOU device instance ID", - "type": "integer" - }, - "name": { - "description": "IOU device name", - "type": "string", - "minLength": 1, - }, - "path": { - "description": "path to the IOU executable", - "type": "string", - "minLength": 1, - }, - "initial_config": { - "description": "path to the IOU initial configuration file", - "type": "string", - "minLength": 1, - }, - "ram": { - "description": "amount of RAM in MB", - "type": "integer" - }, - "nvram": { - "description": "amount of NVRAM in KB", - "type": "integer" - }, - "ethernet_adapters": { - "description": "number of Ethernet adapters", - "type": "integer", - "minimum": 0, - "maximum": 16, - }, - "serial_adapters": { - "description": "number of serial adapters", - "type": "integer", - "minimum": 0, - "maximum": 16, - }, - "console": { - "description": "console TCP port", - "minimum": 1, - "maximum": 65535, - "type": "integer" - }, - "use_default_iou_values": { - "description": "use the default IOU RAM & NVRAM values", - "type": "boolean" - }, - "l1_keepalives": { - "description": "enable or disable layer 1 keepalive messages", - "type": "boolean" - }, - "initial_config_base64": { - "description": "initial configuration base64 encoded", - "type": "string" - }, - }, - "additionalProperties": False, - "required": ["id"] -} - -IOU_START_SCHEMA = { - "$schema": "http://json-schema.org/draft-04/schema#", - "description": "Request validation to start an IOU instance", - "type": "object", - "properties": { - "id": { - "description": "IOU device instance ID", - "type": "integer" - }, - }, - "additionalProperties": False, - "required": ["id"] -} - -IOU_STOP_SCHEMA = { - "$schema": "http://json-schema.org/draft-04/schema#", - "description": "Request validation to stop an IOU instance", - "type": "object", - "properties": { - "id": { - "description": "IOU device instance ID", - "type": "integer" - }, - }, - "additionalProperties": False, - "required": ["id"] -} - -IOU_RELOAD_SCHEMA = { - "$schema": "http://json-schema.org/draft-04/schema#", - "description": "Request validation to reload an IOU instance", - "type": "object", - "properties": { - "id": { - "description": "IOU device instance ID", - "type": "integer" - }, - }, - "additionalProperties": False, - "required": ["id"] -} - -IOU_ALLOCATE_UDP_PORT_SCHEMA = { - "$schema": "http://json-schema.org/draft-04/schema#", - "description": "Request validation to allocate an UDP port for an IOU instance", - "type": "object", - "properties": { - "id": { - "description": "IOU device instance ID", - "type": "integer" - }, - "port_id": { - "description": "Unique port identifier for the IOU instance", - "type": "integer" - }, - }, - "additionalProperties": False, - "required": ["id", "port_id"] -} - -IOU_ADD_NIO_SCHEMA = { - "$schema": "http://json-schema.org/draft-04/schema#", - "description": "Request validation to add a NIO for an IOU instance", - "type": "object", - - "definitions": { - "UDP": { - "description": "UDP Network Input/Output", - "properties": { - "type": { - "enum": ["nio_udp"] - }, - "lport": { - "description": "Local port", - "type": "integer", - "minimum": 1, - "maximum": 65535 - }, - "rhost": { - "description": "Remote host", - "type": "string", - "minLength": 1 - }, - "rport": { - "description": "Remote port", - "type": "integer", - "minimum": 1, - "maximum": 65535 - } - }, - "required": ["type", "lport", "rhost", "rport"], - "additionalProperties": False - }, - "Ethernet": { - "description": "Generic Ethernet Network Input/Output", - "properties": { - "type": { - "enum": ["nio_generic_ethernet"] - }, - "ethernet_device": { - "description": "Ethernet device name e.g. eth0", - "type": "string", - "minLength": 1 - }, - }, - "required": ["type", "ethernet_device"], - "additionalProperties": False - }, - "LinuxEthernet": { - "description": "Linux Ethernet Network Input/Output", - "properties": { - "type": { - "enum": ["nio_linux_ethernet"] - }, - "ethernet_device": { - "description": "Ethernet device name e.g. eth0", - "type": "string", - "minLength": 1 - }, - }, - "required": ["type", "ethernet_device"], - "additionalProperties": False - }, - "TAP": { - "description": "TAP Network Input/Output", - "properties": { - "type": { - "enum": ["nio_tap"] - }, - "tap_device": { - "description": "TAP device name e.g. tap0", - "type": "string", - "minLength": 1 - }, - }, - "required": ["type", "tap_device"], - "additionalProperties": False - }, - "UNIX": { - "description": "UNIX Network Input/Output", - "properties": { - "type": { - "enum": ["nio_unix"] - }, - "local_file": { - "description": "path to the UNIX socket file (local)", - "type": "string", - "minLength": 1 - }, - "remote_file": { - "description": "path to the UNIX socket file (remote)", - "type": "string", - "minLength": 1 - }, - }, - "required": ["type", "local_file", "remote_file"], - "additionalProperties": False - }, - "VDE": { - "description": "VDE Network Input/Output", - "properties": { - "type": { - "enum": ["nio_vde"] - }, - "control_file": { - "description": "path to the VDE control file", - "type": "string", - "minLength": 1 - }, - "local_file": { - "description": "path to the VDE control file", - "type": "string", - "minLength": 1 - }, - }, - "required": ["type", "control_file", "local_file"], - "additionalProperties": False - }, - "NULL": { - "description": "NULL Network Input/Output", - "properties": { - "type": { - "enum": ["nio_null"] - }, - }, - "required": ["type"], - "additionalProperties": False - }, - }, - - "properties": { - "id": { - "description": "IOU device instance ID", - "type": "integer" - }, - "port_id": { - "description": "Unique port identifier for the IOU instance", - "type": "integer" - }, - "slot": { - "description": "Slot number", - "type": "integer", - "minimum": 0, - "maximum": 15 - }, - "port": { - "description": "Port number", - "type": "integer", - "minimum": 0, - "maximum": 3 - }, - "nio": { - "type": "object", - "description": "Network Input/Output", - "oneOf": [ - {"$ref": "#/definitions/UDP"}, - {"$ref": "#/definitions/Ethernet"}, - {"$ref": "#/definitions/LinuxEthernet"}, - {"$ref": "#/definitions/TAP"}, - {"$ref": "#/definitions/UNIX"}, - {"$ref": "#/definitions/VDE"}, - {"$ref": "#/definitions/NULL"}, - ] - }, - }, - "additionalProperties": False, - "required": ["id", "port_id", "slot", "port", "nio"] -} - - -IOU_DELETE_NIO_SCHEMA = { - "$schema": "http://json-schema.org/draft-04/schema#", - "description": "Request validation to delete a NIO for an IOU instance", - "type": "object", - "properties": { - "id": { - "description": "IOU device instance ID", - "type": "integer" - }, - "slot": { - "description": "Slot number", - "type": "integer", - "minimum": 0, - "maximum": 15 - }, - "port": { - "description": "Port number", - "type": "integer", - "minimum": 0, - "maximum": 3 - }, - }, - "additionalProperties": False, - "required": ["id", "slot", "port"] -} - -IOU_START_CAPTURE_SCHEMA = { - "$schema": "http://json-schema.org/draft-04/schema#", - "description": "Request validation to start a packet capture on an IOU instance port", - "type": "object", - "properties": { - "id": { - "description": "IOU device instance ID", - "type": "integer" - }, - "slot": { - "description": "Slot number", - "type": "integer", - "minimum": 0, - "maximum": 15 - }, - "port": { - "description": "Port number", - "type": "integer", - "minimum": 0, - "maximum": 3 - }, - "port_id": { - "description": "Unique port identifier for the IOU instance", - "type": "integer" - }, - "capture_file_name": { - "description": "Capture file name", - "type": "string", - "minLength": 1, - }, - "data_link_type": { - "description": "PCAP data link type", - "type": "string", - "minLength": 1, - }, - }, - "additionalProperties": False, - "required": ["id", "slot", "port", "port_id", "capture_file_name"] -} - -IOU_STOP_CAPTURE_SCHEMA = { - "$schema": "http://json-schema.org/draft-04/schema#", - "description": "Request validation to stop a packet capture on an IOU instance port", - "type": "object", - "properties": { - "id": { - "description": "IOU device instance ID", - "type": "integer" - }, - "slot": { - "description": "Slot number", - "type": "integer", - "minimum": 0, - "maximum": 15 - }, - "port": { - "description": "Port number", - "type": "integer", - "minimum": 0, - "maximum": 3 - }, - "port_id": { - "description": "Unique port identifier for the IOU instance", - "type": "integer" - }, - }, - "additionalProperties": False, - "required": ["id", "slot", "port", "port_id"] -} - -IOU_EXPORT_CONFIG_SCHEMA = { - "$schema": "http://json-schema.org/draft-04/schema#", - "description": "Request validation to export an initial-config from an IOU instance", - "type": "object", - "properties": { - "id": { - "description": "IOU device instance ID", - "type": "integer" - }, - }, - "additionalProperties": False, - "required": ["id"] -} diff --git a/gns3server/old_modules/qemu/__init__.py b/gns3server/old_modules/qemu/__init__.py deleted file mode 100644 index 01b3c72e..00000000 --- a/gns3server/old_modules/qemu/__init__.py +++ /dev/null @@ -1,687 +0,0 @@ -# -*- 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 . - -""" -QEMU server module. -""" - -import sys -import os -import socket -import shutil -import subprocess -import re - -from gns3server.modules import IModule -from gns3server.config import Config -from .qemu_vm import QemuVM -from .qemu_error import QemuError -from .nios.nio_udp import NIO_UDP -from ..attic import find_unused_port - -from .schemas import QEMU_CREATE_SCHEMA -from .schemas import QEMU_DELETE_SCHEMA -from .schemas import QEMU_UPDATE_SCHEMA -from .schemas import QEMU_START_SCHEMA -from .schemas import QEMU_STOP_SCHEMA -from .schemas import QEMU_SUSPEND_SCHEMA -from .schemas import QEMU_RELOAD_SCHEMA -from .schemas import QEMU_ALLOCATE_UDP_PORT_SCHEMA -from .schemas import QEMU_ADD_NIO_SCHEMA -from .schemas import QEMU_DELETE_NIO_SCHEMA - -import logging -log = logging.getLogger(__name__) - - -class Qemu(IModule): - - """ - QEMU 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._qemu_instances = {} - - config = Config.instance() - qemu_config = config.get_section_config(name.upper()) - self._console_start_port_range = qemu_config.get("console_start_port_range", 5001) - self._console_end_port_range = qemu_config.get("console_end_port_range", 5500) - self._monitor_start_port_range = qemu_config.get("monitor_start_port_range", 5501) - self._monitor_end_port_range = qemu_config.get("monitor_end_port_range", 6000) - self._allocated_udp_ports = [] - self._udp_start_port_range = qemu_config.get("udp_start_port_range", 40001) - self._udp_end_port_range = qemu_config.get("udp_end_port_range", 45500) - self._host = qemu_config.get("host", kwargs["host"]) - self._console_host = qemu_config.get("console_host", kwargs["console_host"]) - self._monitor_host = qemu_config.get("monitor_host", "127.0.0.1") - 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 QEMU instances - for qemu_id in self._qemu_instances: - qemu_instance = self._qemu_instances[qemu_id] - qemu_instance.delete() - - IModule.stop(self, signum) # this will stop the I/O loop - - def get_qemu_instance(self, qemu_id): - """ - Returns a QEMU VM instance. - - :param qemu_id: QEMU VM identifier - - :returns: QemuVM instance - """ - - if qemu_id not in self._qemu_instances: - log.debug("QEMU VM ID {} doesn't exist".format(qemu_id), exc_info=1) - self.send_custom_error("QEMU VM ID {} doesn't exist".format(qemu_id)) - return None - return self._qemu_instances[qemu_id] - - @IModule.route("qemu.reset") - def reset(self, request): - """ - Resets the module. - - :param request: JSON request - """ - - # delete all QEMU instances - for qemu_id in self._qemu_instances: - qemu_instance = self._qemu_instances[qemu_id] - qemu_instance.delete() - - # resets the instance IDs - QemuVM.reset() - - self._qemu_instances.clear() - self._allocated_udp_ports.clear() - - self._working_dir = self._projects_dir - log.info("QEMU module has been reset") - - @IModule.route("qemu.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 - - monitor_start_port_range - - monitor_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 qemu_id in self._qemu_instances: - qemu_instance = self._qemu_instances[qemu_id] - qemu_instance.working_dir = os.path.join(self._working_dir, "qemu", "vm-{}".format(qemu_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 "monitor_start_port_range" in request and "monitor_end_port_range" in request: - self._monitor_start_port_range = request["monitor_start_port_range"] - self._monitor_end_port_range = request["monitor_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("qemu.create") - def qemu_create(self, request): - """ - Creates a new QEMU VM instance. - - Mandatory request parameters: - - name (QEMU VM name) - - qemu_path (path to the Qemu binary) - - Optional request parameters: - - console (QEMU VM console port) - - monitor (QEMU VM monitor port) - - Response parameters: - - id (QEMU VM instance identifier) - - name (QEMU VM name) - - default settings - - :param request: JSON request - """ - - # validate the request - if not self.validate_request(request, QEMU_CREATE_SCHEMA): - return - - name = request["name"] - qemu_path = request["qemu_path"] - console = request.get("console") - monitor = request.get("monitor") - qemu_id = request.get("qemu_id") - - try: - qemu_instance = QemuVM(name, - qemu_path, - self._working_dir, - self._host, - qemu_id, - console, - self._console_host, - self._console_start_port_range, - self._console_end_port_range, - monitor, - self._monitor_host, - self._monitor_start_port_range, - self._monitor_end_port_range) - - except QemuError as e: - self.send_custom_error(str(e)) - return - - response = {"name": qemu_instance.name, - "id": qemu_instance.id} - - defaults = qemu_instance.defaults() - response.update(defaults) - self._qemu_instances[qemu_instance.id] = qemu_instance - self.send_response(response) - - @IModule.route("qemu.delete") - def qemu_delete(self, request): - """ - Deletes a QEMU VM instance. - - Mandatory request parameters: - - id (QEMU VM instance identifier) - - Response parameter: - - True on success - - :param request: JSON request - """ - - # validate the request - if not self.validate_request(request, QEMU_DELETE_SCHEMA): - return - - # get the instance - qemu_instance = self.get_qemu_instance(request["id"]) - if not qemu_instance: - return - - try: - qemu_instance.clean_delete() - del self._qemu_instances[request["id"]] - except QemuError as e: - self.send_custom_error(str(e)) - return - - self.send_response(True) - - @IModule.route("qemu.update") - def qemu_update(self, request): - """ - Updates a QEMU VM instance - - Mandatory request parameters: - - id (QEMU 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, QEMU_UPDATE_SCHEMA): - return - - # get the instance - qemu_instance = self.get_qemu_instance(request["id"]) - if not qemu_instance: - return - - # update the QEMU VM settings - response = {} - for name, value in request.items(): - if hasattr(qemu_instance, name) and getattr(qemu_instance, name) != value: - try: - setattr(qemu_instance, name, value) - response[name] = value - except QemuError as e: - self.send_custom_error(str(e)) - return - - self.send_response(response) - - @IModule.route("qemu.start") - def qemu_start(self, request): - """ - Starts a QEMU VM instance. - - Mandatory request parameters: - - id (QEMU VM instance identifier) - - Response parameters: - - True on success - - :param request: JSON request - """ - - # validate the request - if not self.validate_request(request, QEMU_START_SCHEMA): - return - - # get the instance - qemu_instance = self.get_qemu_instance(request["id"]) - if not qemu_instance: - return - - try: - qemu_instance.start() - except QemuError as e: - self.send_custom_error(str(e)) - return - self.send_response(True) - - @IModule.route("qemu.stop") - def qemu_stop(self, request): - """ - Stops a QEMU VM instance. - - Mandatory request parameters: - - id (QEMU VM instance identifier) - - Response parameters: - - True on success - - :param request: JSON request - """ - - # validate the request - if not self.validate_request(request, QEMU_STOP_SCHEMA): - return - - # get the instance - qemu_instance = self.get_qemu_instance(request["id"]) - if not qemu_instance: - return - - try: - qemu_instance.stop() - except QemuError as e: - self.send_custom_error(str(e)) - return - self.send_response(True) - - @IModule.route("qemu.reload") - def qemu_reload(self, request): - """ - Reloads a QEMU VM instance. - - Mandatory request parameters: - - id (QEMU VM identifier) - - Response parameters: - - True on success - - :param request: JSON request - """ - - # validate the request - if not self.validate_request(request, QEMU_RELOAD_SCHEMA): - return - - # get the instance - qemu_instance = self.get_qemu_instance(request["id"]) - if not qemu_instance: - return - - try: - qemu_instance.reload() - except QemuError as e: - self.send_custom_error(str(e)) - return - self.send_response(True) - - @IModule.route("qemu.stop") - def qemu_stop(self, request): - """ - Stops a QEMU VM instance. - - Mandatory request parameters: - - id (QEMU VM instance identifier) - - Response parameters: - - True on success - - :param request: JSON request - """ - - # validate the request - if not self.validate_request(request, QEMU_STOP_SCHEMA): - return - - # get the instance - qemu_instance = self.get_qemu_instance(request["id"]) - if not qemu_instance: - return - - try: - qemu_instance.stop() - except QemuError as e: - self.send_custom_error(str(e)) - return - self.send_response(True) - - @IModule.route("qemu.suspend") - def qemu_suspend(self, request): - """ - Suspends a QEMU VM instance. - - Mandatory request parameters: - - id (QEMU VM instance identifier) - - Response parameters: - - True on success - - :param request: JSON request - """ - - # validate the request - if not self.validate_request(request, QEMU_SUSPEND_SCHEMA): - return - - # get the instance - qemu_instance = self.get_qemu_instance(request["id"]) - if not qemu_instance: - return - - try: - qemu_instance.suspend() - except QemuError as e: - self.send_custom_error(str(e)) - return - self.send_response(True) - - @IModule.route("qemu.allocate_udp_port") - def allocate_udp_port(self, request): - """ - Allocates a UDP port in order to create an UDP NIO. - - Mandatory request parameters: - - id (QEMU 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, QEMU_ALLOCATE_UDP_PORT_SCHEMA): - return - - # get the instance - qemu_instance = self.get_qemu_instance(request["id"]) - if not qemu_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(qemu_instance.name, - qemu_instance.id, - port, - self._host)) - - response = {"lport": port, - "port_id": request["port_id"]} - self.send_response(response) - - @IModule.route("qemu.add_nio") - def add_nio(self, request): - """ - Adds an NIO (Network Input/Output) for a QEMU VM instance. - - Mandatory request parameters: - - id (QEMU 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, QEMU_ADD_NIO_SCHEMA): - return - - # get the instance - qemu_instance = self.get_qemu_instance(request["id"]) - if not qemu_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 QemuError("Could not create an UDP connection to {}:{}: {}".format(rhost, rport, e)) - nio = NIO_UDP(lport, rhost, rport) - if not nio: - raise QemuError("Requested NIO does not exist or is not supported: {}".format(request["nio"]["type"])) - except QemuError as e: - self.send_custom_error(str(e)) - return - - try: - qemu_instance.port_add_nio_binding(port, nio) - except QemuError as e: - self.send_custom_error(str(e)) - return - - self.send_response({"port_id": request["port_id"]}) - - @IModule.route("qemu.delete_nio") - def delete_nio(self, request): - """ - Deletes an NIO (Network Input/Output). - - Mandatory request parameters: - - id (QEMU VM instance identifier) - - port (port identifier) - - Response parameters: - - True on success - - :param request: JSON request - """ - - # validate the request - if not self.validate_request(request, QEMU_DELETE_NIO_SCHEMA): - return - - # get the instance - qemu_instance = self.get_qemu_instance(request["id"]) - if not qemu_instance: - return - - port = request["port"] - try: - nio = qemu_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 QemuError as e: - self.send_custom_error(str(e)) - return - - self.send_response(True) - - def _get_qemu_version(self, qemu_path): - """ - Gets the Qemu version. - - :param qemu_path: path to Qemu - """ - - if sys.platform.startswith("win"): - return "" - try: - output = subprocess.check_output([qemu_path, "-version"]) - match = re.search("version\s+([0-9a-z\-\.]+)", output.decode("utf-8")) - if match: - version = match.group(1) - return version - else: - raise QemuError("Could not determine the Qemu version for {}".format(qemu_path)) - except subprocess.SubprocessError as e: - raise QemuError("Error while looking for the Qemu version: {}".format(e)) - - @IModule.route("qemu.qemu_list") - def qemu_list(self, request): - """ - Gets QEMU binaries list. - - Response parameters: - - List of Qemu binaries - """ - - qemus = [] - paths = [os.getcwd()] + os.environ["PATH"].split(os.pathsep) - # look for Qemu binaries in the current working directory and $PATH - if sys.platform.startswith("win"): - # add specific Windows paths - if hasattr(sys, "frozen"): - # add any qemu dir in the same location as gns3server.exe to the list of paths - exec_dir = os.path.dirname(os.path.abspath(sys.executable)) - for f in os.listdir(exec_dir): - if f.lower().startswith("qemu"): - paths.append(os.path.join(exec_dir, f)) - - if "PROGRAMFILES(X86)" in os.environ and os.path.exists(os.environ["PROGRAMFILES(X86)"]): - paths.append(os.path.join(os.environ["PROGRAMFILES(X86)"], "qemu")) - if "PROGRAMFILES" in os.environ and os.path.exists(os.environ["PROGRAMFILES"]): - paths.append(os.path.join(os.environ["PROGRAMFILES"], "qemu")) - elif sys.platform.startswith("darwin"): - # add specific locations on Mac OS X regardless of what's in $PATH - paths.extend(["/usr/local/bin", "/opt/local/bin"]) - if hasattr(sys, "frozen"): - paths.append(os.path.abspath(os.path.join(os.getcwd(), "../../../qemu/bin/"))) - for path in paths: - try: - for f in os.listdir(path): - if (f.startswith("qemu-system") or f == "qemu" or f == "qemu.exe") and \ - os.access(os.path.join(path, f), os.X_OK) and \ - os.path.isfile(os.path.join(path, f)): - qemu_path = os.path.join(path, f) - version = self._get_qemu_version(qemu_path) - qemus.append({"path": qemu_path, "version": version}) - except OSError: - continue - - response = {"qemus": qemus} - self.send_response(response) - - @IModule.route("qemu.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) diff --git a/gns3server/old_modules/qemu/adapters/__init__.py b/gns3server/old_modules/qemu/adapters/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/gns3server/old_modules/qemu/adapters/adapter.py b/gns3server/old_modules/qemu/adapters/adapter.py deleted file mode 100644 index ade660f9..00000000 --- a/gns3server/old_modules/qemu/adapters/adapter.py +++ /dev/null @@ -1,105 +0,0 @@ -# -*- 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 . - - -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 diff --git a/gns3server/old_modules/qemu/adapters/ethernet_adapter.py b/gns3server/old_modules/qemu/adapters/ethernet_adapter.py deleted file mode 100644 index 2064bb68..00000000 --- a/gns3server/old_modules/qemu/adapters/ethernet_adapter.py +++ /dev/null @@ -1,32 +0,0 @@ -# -*- 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 . - -from .adapter import Adapter - - -class EthernetAdapter(Adapter): - - """ - QEMU Ethernet adapter. - """ - - def __init__(self): - Adapter.__init__(self, interfaces=1) - - def __str__(self): - - return "QEMU Ethernet adapter" diff --git a/gns3server/old_modules/qemu/nios/__init__.py b/gns3server/old_modules/qemu/nios/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/gns3server/old_modules/qemu/nios/nio.py b/gns3server/old_modules/qemu/nios/nio.py deleted file mode 100644 index 3c8a6b9e..00000000 --- a/gns3server/old_modules/qemu/nios/nio.py +++ /dev/null @@ -1,66 +0,0 @@ -# -*- 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 . - -""" -Base interface for NIOs. -""" - - -class NIO(object): - - """ - Network Input/Output. - """ - - def __init__(self): - - self._capturing = False - self._pcap_output_file = "" - - def startPacketCapture(self, pcap_output_file): - """ - - :param pcap_output_file: PCAP destination file for the capture - """ - - self._capturing = True - self._pcap_output_file = pcap_output_file - - def stopPacketCapture(self): - - self._capturing = False - self._pcap_output_file = "" - - @property - def capturing(self): - """ - Returns either a capture is configured on this NIO. - - :returns: boolean - """ - - return self._capturing - - @property - def pcap_output_file(self): - """ - Returns the path to the PCAP output file. - - :returns: path to the PCAP output file - """ - - return self._pcap_output_file diff --git a/gns3server/old_modules/qemu/nios/nio_udp.py b/gns3server/old_modules/qemu/nios/nio_udp.py deleted file mode 100644 index 3b25f0c4..00000000 --- a/gns3server/old_modules/qemu/nios/nio_udp.py +++ /dev/null @@ -1,76 +0,0 @@ -# -*- 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 . - -""" -Interface for UDP NIOs. -""" - -from .nio import NIO - - -class NIO_UDP(NIO): - - """ - 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): - - NIO.__init__(self) - 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" diff --git a/gns3server/old_modules/qemu/qemu_error.py b/gns3server/old_modules/qemu/qemu_error.py deleted file mode 100644 index 55135a34..00000000 --- a/gns3server/old_modules/qemu/qemu_error.py +++ /dev/null @@ -1,39 +0,0 @@ -# -*- 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 . - -""" -Custom exceptions for QEMU module. -""" - - -class QemuError(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 diff --git a/gns3server/old_modules/qemu/qemu_vm.py b/gns3server/old_modules/qemu/qemu_vm.py deleted file mode 100644 index a5ae107d..00000000 --- a/gns3server/old_modules/qemu/qemu_vm.py +++ /dev/null @@ -1,1244 +0,0 @@ -# -*- 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 . - -""" -QEMU VM instance. -""" - -import sys -import os -import shutil -import random -import subprocess -import shlex -import ntpath -import telnetlib -import time -import re - -from gns3server.config import Config -from gns3dms.cloud.rackspace_ctrl import get_provider - -from .qemu_error import QemuError -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 QemuVM(object): - - """ - QEMU VM implementation. - - :param name: name of this QEMU VM - :param qemu_path: path to the QEMU binary - :param working_dir: path to a working directory - :param host: host/address to bind for console and UDP connections - :param qemu_id: QEMU VM instance ID - :param console: TCP console port - :param console_host: IP address to bind for console connections - :param console_start_port_range: TCP console port range start - :param console_end_port_range: TCP console port range end - :param monitor: TCP monitor port - :param monitor_host: IP address to bind for monitor connections - :param monitor_start_port_range: TCP monitor port range start - :param monitor_end_port_range: TCP monitor port range end - """ - - _instances = [] - _allocated_console_ports = [] - _allocated_monitor_ports = [] - - def __init__(self, - name, - qemu_path, - working_dir, - host="127.0.0.1", - qemu_id=None, - console=None, - console_host="0.0.0.0", - console_start_port_range=5001, - console_end_port_range=5500, - monitor=None, - monitor_host="0.0.0.0", - monitor_start_port_range=5501, - monitor_end_port_range=6000): - - if not qemu_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 QemuError("Maximum number of QEMU VM instances reached") - else: - if qemu_id in self._instances: - raise QemuError("QEMU identifier {} is already used by another QEMU VM instance".format(qemu_id)) - self._id = qemu_id - self._instances.append(self._id) - - self._name = name - self._working_dir = None - self._host = host - self._command = [] - self._started = False - self._process = None - self._cpulimit_process = None - self._stdout_file = "" - self._console_host = console_host - self._console_start_port_range = console_start_port_range - self._console_end_port_range = console_end_port_range - self._monitor_host = monitor_host - self._monitor_start_port_range = monitor_start_port_range - self._monitor_end_port_range = monitor_end_port_range - self._cloud_path = None - - # QEMU settings - self._qemu_path = qemu_path - self._hda_disk_image = "" - self._hdb_disk_image = "" - self._options = "" - self._ram = 256 - self._console = console - self._monitor = monitor - self._ethernet_adapters = [] - self._adapter_type = "e1000" - self._initrd = "" - self._kernel_image = "" - self._kernel_command_line = "" - self._legacy_networking = False - self._cpu_throttling = 0 # means no CPU throttling - self._process_priority = "low" - - working_dir_path = os.path.join(working_dir, "qemu", "vm-{}".format(self._id)) - - if qemu_id and not os.path.isdir(working_dir_path): - raise QemuError("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._console_host, - ignore_ports=self._allocated_console_ports) - except Exception as e: - raise QemuError(e) - - if self._console in self._allocated_console_ports: - raise QemuError("Console port {} is already used by another QEMU VM".format(console)) - self._allocated_console_ports.append(self._console) - - if not self._monitor: - # allocate a monitor port - try: - self._monitor = find_unused_port(self._monitor_start_port_range, - self._monitor_end_port_range, - self._monitor_host, - ignore_ports=self._allocated_monitor_ports) - except Exception as e: - raise QemuError(e) - - if self._monitor in self._allocated_monitor_ports: - raise QemuError("Monitor port {} is already used by another QEMU VM".format(monitor)) - self._allocated_monitor_ports.append(self._monitor) - - self.adapters = 1 # creates 1 adapter by default - log.info("QEMU 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 QEMU VM. - - :returns: default values (dictionary) - """ - - qemu_defaults = {"name": self._name, - "qemu_path": self._qemu_path, - "ram": self._ram, - "hda_disk_image": self._hda_disk_image, - "hdb_disk_image": self._hdb_disk_image, - "options": self._options, - "adapters": self.adapters, - "adapter_type": self._adapter_type, - "console": self._console, - "monitor": self._monitor, - "initrd": self._initrd, - "kernel_image": self._kernel_image, - "kernel_command_line": self._kernel_command_line, - "legacy_networking": self._legacy_networking, - "cpu_throttling": self._cpu_throttling, - "process_priority": self._process_priority - } - - return qemu_defaults - - @property - def id(self): - """ - Returns the unique ID for this QEMU VM. - - :returns: id (integer) - """ - - return self._id - - @classmethod - def reset(cls): - """ - Resets allocated instance list. - """ - - cls._instances.clear() - cls._allocated_console_ports.clear() - cls._allocated_monitor_ports.clear() - - @property - def name(self): - """ - Returns the name of this QEMU VM. - - :returns: name - """ - - return self._name - - @name.setter - def name(self, new_name): - """ - Sets the name of this QEMU VM. - - :param new_name: name - """ - - log.info("QEMU 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 QEMU VM. - - :param working_dir: path to the working directory - """ - - try: - os.makedirs(working_dir) - except FileExistsError: - pass - except OSError as e: - raise QemuError("Could not create working directory {}: {}".format(working_dir, e)) - - self._working_dir = working_dir - log.info("QEMU 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 QemuError("Console port {} is already used by another QEMU VM".format(console)) - - self._allocated_console_ports.remove(self._console) - self._console = console - self._allocated_console_ports.append(self._console) - - log.info("QEMU VM {name} [id={id}]: console port set to {port}".format(name=self._name, - id=self._id, - port=console)) - - @property - def monitor(self): - """ - Returns the TCP monitor port. - - :returns: monitor port (integer) - """ - - return self._monitor - - @monitor.setter - def monitor(self, monitor): - """ - Sets the TCP monitor port. - - :param monitor: monitor port (integer) - """ - - if monitor in self._allocated_monitor_ports: - raise QemuError("Monitor port {} is already used by another QEMU VM".format(monitor)) - - self._allocated_monitor_ports.remove(self._monitor) - self._monitor = monitor - self._allocated_monitor_ports.append(self._monitor) - - log.info("QEMU VM {name} [id={id}]: monitor port set to {port}".format(name=self._name, - id=self._id, - port=monitor)) - - def delete(self): - """ - Deletes this QEMU 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) - - if self._monitor and self._monitor in self._allocated_monitor_ports: - self._allocated_monitor_ports.remove(self._monitor) - - log.info("QEMU VM {name} [id={id}] has been deleted".format(name=self._name, - id=self._id)) - - def clean_delete(self): - """ - Deletes this QEMU 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) - - if self._monitor: - self._allocated_monitor_ports.remove(self._monitor) - - try: - shutil.rmtree(self._working_dir) - except OSError as e: - log.error("could not delete QEMU VM {name} [id={id}]: {error}".format(name=self._name, - id=self._id, - error=e)) - return - - log.info("QEMU VM {name} [id={id}] has been deleted (including associated files)".format(name=self._name, - id=self._id)) - - @property - def cloud_path(self): - """ - Returns the cloud path where images can be downloaded from. - - :returns: cloud path - """ - - return self._cloud_path - - @cloud_path.setter - def cloud_path(self, cloud_path): - """ - Sets the cloud path where images can be downloaded from. - - :param cloud_path: - :return: - """ - - self._cloud_path = cloud_path - - @property - def qemu_path(self): - """ - Returns the QEMU binary path for this QEMU VM. - - :returns: QEMU path - """ - - return self._qemu_path - - @qemu_path.setter - def qemu_path(self, qemu_path): - """ - Sets the QEMU binary path this QEMU VM. - - :param qemu_path: QEMU path - """ - - log.info("QEMU VM {name} [id={id}] has set the QEMU path to {qemu_path}".format(name=self._name, - id=self._id, - qemu_path=qemu_path)) - self._qemu_path = qemu_path - - @property - def hda_disk_image(self): - """ - Returns the hda disk image path for this QEMU VM. - - :returns: QEMU hda disk image path - """ - - return self._hda_disk_image - - @hda_disk_image.setter - def hda_disk_image(self, hda_disk_image): - """ - Sets the hda disk image for this QEMU VM. - - :param hda_disk_image: QEMU hda disk image path - """ - - log.info("QEMU VM {name} [id={id}] has set the QEMU hda disk image path to {disk_image}".format(name=self._name, - id=self._id, - disk_image=hda_disk_image)) - self._hda_disk_image = hda_disk_image - - @property - def hdb_disk_image(self): - """ - Returns the hdb disk image path for this QEMU VM. - - :returns: QEMU hdb disk image path - """ - - return self._hdb_disk_image - - @hdb_disk_image.setter - def hdb_disk_image(self, hdb_disk_image): - """ - Sets the hdb disk image for this QEMU VM. - - :param hdb_disk_image: QEMU hdb disk image path - """ - - log.info("QEMU VM {name} [id={id}] has set the QEMU hdb disk image path to {disk_image}".format(name=self._name, - id=self._id, - disk_image=hdb_disk_image)) - self._hdb_disk_image = hdb_disk_image - - @property - def adapters(self): - """ - Returns the number of Ethernet adapters for this QEMU VM instance. - - :returns: number of adapters - """ - - return len(self._ethernet_adapters) - - @adapters.setter - def adapters(self, adapters): - """ - Sets the number of Ethernet adapters for this QEMU VM instance. - - :param adapters: number of adapters - """ - - self._ethernet_adapters.clear() - for adapter_id in range(0, adapters): - self._ethernet_adapters.append(EthernetAdapter()) - - log.info("QEMU VM {name} [id={id}]: number of Ethernet adapters changed to {adapters}".format(name=self._name, - id=self._id, - adapters=adapters)) - - @property - def adapter_type(self): - """ - Returns the adapter type for this QEMU VM instance. - - :returns: adapter type (string) - """ - - return self._adapter_type - - @adapter_type.setter - def adapter_type(self, adapter_type): - """ - Sets the adapter type for this QEMU VM instance. - - :param adapter_type: adapter type (string) - """ - - self._adapter_type = adapter_type - - log.info("QEMU VM {name} [id={id}]: adapter type changed to {adapter_type}".format(name=self._name, - id=self._id, - adapter_type=adapter_type)) - - @property - def legacy_networking(self): - """ - Returns either QEMU legacy networking commands are used. - - :returns: boolean - """ - - return self._legacy_networking - - @legacy_networking.setter - def legacy_networking(self, legacy_networking): - """ - Sets either QEMU legacy networking commands are used. - - :param legacy_networking: boolean - """ - - if legacy_networking: - log.info("QEMU VM {name} [id={id}] has enabled legacy networking".format(name=self._name, id=self._id)) - else: - log.info("QEMU VM {name} [id={id}] has disabled legacy networking".format(name=self._name, id=self._id)) - self._legacy_networking = legacy_networking - - @property - def cpu_throttling(self): - """ - Returns the percentage of CPU allowed. - - :returns: integer - """ - - return self._cpu_throttling - - @cpu_throttling.setter - def cpu_throttling(self, cpu_throttling): - """ - Sets the percentage of CPU allowed. - - :param cpu_throttling: integer - """ - - log.info("QEMU VM {name} [id={id}] has set the percentage of CPU allowed to {cpu}".format(name=self._name, - id=self._id, - cpu=cpu_throttling)) - self._cpu_throttling = cpu_throttling - self._stop_cpulimit() - if cpu_throttling: - self._set_cpu_throttling() - - @property - def process_priority(self): - """ - Returns the process priority. - - :returns: string - """ - - return self._process_priority - - @process_priority.setter - def process_priority(self, process_priority): - """ - Sets the process priority. - - :param process_priority: string - """ - - log.info("QEMU VM {name} [id={id}] has set the process priority to {priority}".format(name=self._name, - id=self._id, - priority=process_priority)) - self._process_priority = process_priority - - @property - def ram(self): - """ - Returns the RAM amount for this QEMU VM. - - :returns: RAM amount in MB - """ - - return self._ram - - @ram.setter - def ram(self, ram): - """ - Sets the amount of RAM for this QEMU VM. - - :param ram: RAM amount in MB - """ - - log.info("QEMU VM {name} [id={id}] has set the RAM to {ram}".format(name=self._name, - id=self._id, - ram=ram)) - self._ram = ram - - @property - def options(self): - """ - Returns the options for this QEMU VM. - - :returns: QEMU options - """ - - return self._options - - @options.setter - def options(self, options): - """ - Sets the options for this QEMU VM. - - :param options: QEMU options - """ - - log.info("QEMU VM {name} [id={id}] has set the QEMU options to {options}".format(name=self._name, - id=self._id, - options=options)) - self._options = options - - @property - def initrd(self): - """ - Returns the initrd path for this QEMU VM. - - :returns: QEMU initrd path - """ - - return self._initrd - - @initrd.setter - def initrd(self, initrd): - """ - Sets the initrd path for this QEMU VM. - - :param initrd: QEMU initrd path - """ - - log.info("QEMU VM {name} [id={id}] has set the QEMU initrd path to {initrd}".format(name=self._name, - id=self._id, - initrd=initrd)) - self._initrd = initrd - - @property - def kernel_image(self): - """ - Returns the kernel image path for this QEMU VM. - - :returns: QEMU kernel image path - """ - - return self._kernel_image - - @kernel_image.setter - def kernel_image(self, kernel_image): - """ - Sets the kernel image path for this QEMU VM. - - :param kernel_image: QEMU kernel image path - """ - - log.info("QEMU VM {name} [id={id}] has set the QEMU kernel image path to {kernel_image}".format(name=self._name, - id=self._id, - kernel_image=kernel_image)) - self._kernel_image = kernel_image - - @property - def kernel_command_line(self): - """ - Returns the kernel command line for this QEMU VM. - - :returns: QEMU kernel command line - """ - - return self._kernel_command_line - - @kernel_command_line.setter - def kernel_command_line(self, kernel_command_line): - """ - Sets the kernel command line for this QEMU VM. - - :param kernel_command_line: QEMU kernel command line - """ - - log.info("QEMU VM {name} [id={id}] has set the QEMU kernel command line to {kernel_command_line}".format(name=self._name, - id=self._id, - kernel_command_line=kernel_command_line)) - self._kernel_command_line = kernel_command_line - - def _set_process_priority(self): - """ - Changes the process priority - """ - - if sys.platform.startswith("win"): - try: - import win32api - import win32con - import win32process - except ImportError: - log.error("pywin32 must be installed to change the priority class for QEMU VM {}".format(self._name)) - else: - log.info("setting QEMU VM {} priority class to BELOW_NORMAL".format(self._name)) - handle = win32api.OpenProcess(win32con.PROCESS_ALL_ACCESS, 0, self._process.pid) - if self._process_priority == "realtime": - priority = win32process.REALTIME_PRIORITY_CLASS - elif self._process_priority == "very high": - priority = win32process.HIGH_PRIORITY_CLASS - elif self._process_priority == "high": - priority = win32process.ABOVE_NORMAL_PRIORITY_CLASS - elif self._process_priority == "low": - priority = win32process.BELOW_NORMAL_PRIORITY_CLASS - elif self._process_priority == "very low": - priority = win32process.IDLE_PRIORITY_CLASS - else: - priority = win32process.NORMAL_PRIORITY_CLASS - win32process.SetPriorityClass(handle, priority) - else: - if self._process_priority == "realtime": - priority = -20 - elif self._process_priority == "very high": - priority = -15 - elif self._process_priority == "high": - priority = -5 - elif self._process_priority == "low": - priority = 5 - elif self._process_priority == "very low": - priority = 19 - else: - priority = 0 - try: - subprocess.call(['renice', '-n', str(priority), '-p', str(self._process.pid)]) - except (OSError, subprocess.SubprocessError) as e: - log.error("could not change process priority for QEMU VM {}: {}".format(self._name, e)) - - def _stop_cpulimit(self): - """ - Stops the cpulimit process. - """ - - if self._cpulimit_process and self._cpulimit_process.poll() is None: - self._cpulimit_process.kill() - try: - self._process.wait(3) - except subprocess.TimeoutExpired: - log.error("could not kill cpulimit process {}".format(self._cpulimit_process.pid)) - - def _set_cpu_throttling(self): - """ - Limits the CPU usage for current QEMU process. - """ - - if not self.is_running(): - return - - try: - if sys.platform.startswith("win") and hasattr(sys, "frozen"): - cpulimit_exec = os.path.join(os.path.dirname(os.path.abspath(sys.executable)), "cpulimit", "cpulimit.exe") - else: - cpulimit_exec = "cpulimit" - subprocess.Popen([cpulimit_exec, "--lazy", "--pid={}".format(self._process.pid), "--limit={}".format(self._cpu_throttling)], cwd=self._working_dir) - log.info("CPU throttled to {}%".format(self._cpu_throttling)) - except FileNotFoundError: - raise QemuError("cpulimit could not be found, please install it or deactivate CPU throttling") - except (OSError, subprocess.SubprocessError) as e: - raise QemuError("Could not throttle CPU: {}".format(e)) - - def start(self): - """ - Starts this QEMU VM. - """ - - if self.is_running(): - - # resume the VM if it is paused - self.resume() - return - - else: - - if not os.path.isfile(self._qemu_path) or not os.path.exists(self._qemu_path): - found = False - paths = [os.getcwd()] + os.environ["PATH"].split(os.pathsep) - # look for the qemu binary in the current working directory and $PATH - for path in paths: - try: - if self._qemu_path in os.listdir(path) and os.access(os.path.join(path, self._qemu_path), os.X_OK): - self._qemu_path = os.path.join(path, self._qemu_path) - found = True - break - except OSError: - continue - - if not found: - raise QemuError("QEMU binary '{}' is not accessible".format(self._qemu_path)) - - if self.cloud_path is not None: - # Download from Cloud Files - if self.hda_disk_image != "": - _, filename = ntpath.split(self.hda_disk_image) - src = '{}/{}'.format(self.cloud_path, filename) - dst = os.path.join(self.working_dir, filename) - if not os.path.isfile(dst): - cloud_settings = Config.instance().cloud_settings() - provider = get_provider(cloud_settings) - log.debug("Downloading file from {} to {}...".format(src, dst)) - provider.download_file(src, dst) - log.debug("Download of {} complete.".format(src)) - self.hda_disk_image = dst - if self.hdb_disk_image != "": - _, filename = ntpath.split(self.hdb_disk_image) - src = '{}/{}'.format(self.cloud_path, filename) - dst = os.path.join(self.working_dir, filename) - if not os.path.isfile(dst): - cloud_settings = Config.instance().cloud_settings() - provider = get_provider(cloud_settings) - log.debug("Downloading file from {} to {}...".format(src, dst)) - provider.download_file(src, dst) - log.debug("Download of {} complete.".format(src)) - self.hdb_disk_image = dst - - if self.initrd != "": - _, filename = ntpath.split(self.initrd) - src = '{}/{}'.format(self.cloud_path, filename) - dst = os.path.join(self.working_dir, filename) - if not os.path.isfile(dst): - cloud_settings = Config.instance().cloud_settings() - provider = get_provider(cloud_settings) - log.debug("Downloading file from {} to {}...".format(src, dst)) - provider.download_file(src, dst) - log.debug("Download of {} complete.".format(src)) - self.initrd = dst - if self.kernel_image != "": - _, filename = ntpath.split(self.kernel_image) - src = '{}/{}'.format(self.cloud_path, filename) - dst = os.path.join(self.working_dir, filename) - if not os.path.isfile(dst): - cloud_settings = Config.instance().cloud_settings() - provider = get_provider(cloud_settings) - log.debug("Downloading file from {} to {}...".format(src, dst)) - provider.download_file(src, dst) - log.debug("Download of {} complete.".format(src)) - self.kernel_image = dst - - self._command = self._build_command() - try: - log.info("starting QEMU: {}".format(self._command)) - self._stdout_file = os.path.join(self._working_dir, "qemu.log") - log.info("logging to {}".format(self._stdout_file)) - with open(self._stdout_file, "w") as fd: - self._process = subprocess.Popen(self._command, - stdout=fd, - stderr=subprocess.STDOUT, - cwd=self._working_dir) - log.info("QEMU VM instance {} started PID={}".format(self._id, self._process.pid)) - self._started = True - except (OSError, subprocess.SubprocessError) as e: - stdout = self.read_stdout() - log.error("could not start QEMU {}: {}\n{}".format(self._qemu_path, e, stdout)) - raise QemuError("could not start QEMU {}: {}\n{}".format(self._qemu_path, e, stdout)) - - self._set_process_priority() - if self._cpu_throttling: - self._set_cpu_throttling() - - def stop(self): - """ - Stops this QEMU VM. - """ - - # stop the QEMU process - if self.is_running(): - log.info("stopping QEMU VM instance {} PID={}".format(self._id, self._process.pid)) - try: - self._process.terminate() - self._process.wait(1) - except subprocess.TimeoutExpired: - self._process.kill() - if self._process.poll() is None: - log.warn("QEMU VM instance {} PID={} is still running".format(self._id, - self._process.pid)) - self._process = None - self._started = False - self._stop_cpulimit() - - def _control_vm(self, command, expected=None, timeout=30): - """ - Executes a command with QEMU monitor when this VM is running. - - :param command: QEMU monitor command (e.g. info status, stop etc.) - :param timeout: how long to wait for QEMU monitor - - :returns: result of the command (Match object or None) - """ - - result = None - if self.is_running() and self._monitor: - log.debug("Execute QEMU monitor command: {}".format(command)) - try: - tn = telnetlib.Telnet(self._monitor_host, self._monitor, timeout=timeout) - except OSError as e: - log.warn("Could not connect to QEMU monitor: {}".format(e)) - return result - try: - tn.write(command.encode('ascii') + b"\n") - time.sleep(0.1) - except OSError as e: - log.warn("Could not write to QEMU monitor: {}".format(e)) - tn.close() - return result - if expected: - try: - ind, match, dat = tn.expect(list=expected, timeout=timeout) - if match: - result = match - except EOFError as e: - log.warn("Could not read from QEMU monitor: {}".format(e)) - tn.close() - return result - - def _get_vm_status(self): - """ - Returns this VM suspend status (running|paused) - - :returns: status (string) - """ - - result = None - - match = self._control_vm("info status", [b"running", b"paused"]) - if match: - result = match.group(0).decode('ascii') - return result - - def suspend(self): - """ - Suspends this QEMU VM. - """ - - vm_status = self._get_vm_status() - if vm_status == "running": - self._control_vm("stop") - log.debug("QEMU VM has been suspended") - else: - log.info("QEMU VM is not running to be suspended, current status is {}".format(vm_status)) - - def reload(self): - """ - Reloads this QEMU VM. - """ - - self._control_vm("system_reset") - log.debug("QEMU VM has been reset") - - def resume(self): - """ - Resumes this QEMU VM. - """ - - vm_status = self._get_vm_status() - if vm_status == "paused": - self._control_vm("cont") - log.debug("QEMU VM has been resumed") - else: - log.info("QEMU VM is not paused to be resumed, current status is {}".format(vm_status)) - - def port_add_nio_binding(self, adapter_id, nio): - """ - Adds a port NIO binding. - - :param adapter_id: adapter ID - :param nio: NIO instance to add to the slot/port - """ - - try: - adapter = self._ethernet_adapters[adapter_id] - except IndexError: - raise QemuError("Adapter {adapter_id} doesn't exist on QEMU VM {name}".format(name=self._name, - adapter_id=adapter_id)) - - if self.is_running(): - # dynamically configure an UDP tunnel on the QEMU VM adapter - if nio and isinstance(nio, NIO_UDP): - if self._legacy_networking: - self._control_vm("host_net_remove {} gns3-{}".format(adapter_id, adapter_id)) - self._control_vm("host_net_add udp vlan={},name=gns3-{},sport={},dport={},daddr={}".format(adapter_id, - adapter_id, - nio.lport, - nio.rport, - nio.rhost)) - else: - # FIXME: does it work? very undocumented feature... - self._control_vm("netdev_del gns3-{}".format(adapter_id)) - self._control_vm("netdev_add socket,id=gns3-{},udp={}:{},localaddr={}:{}".format(adapter_id, - nio.rhost, - nio.rport, - self._host, - nio.lport)) - - adapter.add_nio(0, nio) - log.info("QEMU VM {name} [id={id}]: {nio} added to adapter {adapter_id}".format(name=self._name, - id=self._id, - nio=nio, - adapter_id=adapter_id)) - - def port_remove_nio_binding(self, adapter_id): - """ - Removes a port NIO binding. - - :param adapter_id: adapter ID - - :returns: NIO instance - """ - - try: - adapter = self._ethernet_adapters[adapter_id] - except IndexError: - raise QemuError("Adapter {adapter_id} doesn't exist on QEMU VM {name}".format(name=self._name, - adapter_id=adapter_id)) - - if self.is_running(): - # dynamically disable the QEMU VM adapter - if self._legacy_networking: - self._control_vm("host_net_remove {} gns3-{}".format(adapter_id, adapter_id)) - self._control_vm("host_net_add user vlan={},name=gns3-{}".format(adapter_id, adapter_id)) - else: - # FIXME: does it work? very undocumented feature... - self._control_vm("netdev_del gns3-{}".format(adapter_id)) - self._control_vm("netdev_add user,id=gns3-{}".format(adapter_id)) - - nio = adapter.get_nio(0) - adapter.remove_nio(0) - log.info("QEMU VM {name} [id={id}]: {nio} removed from adapter {adapter_id}".format(name=self._name, - id=self._id, - nio=nio, - adapter_id=adapter_id)) - return nio - - @property - def started(self): - """ - Returns either this QEMU VM has been started or not. - - :returns: boolean - """ - - return self._started - - def read_stdout(self): - """ - Reads the standard output of the QEMU process. - Only use when the process has been stopped or has crashed. - """ - - output = "" - if self._stdout_file: - 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 QEMU process is running - - :returns: True or False - """ - - if self._process and self._process.poll() is None: - return True - return False - - def command(self): - """ - Returns the QEMU command line. - - :returns: QEMU command line (string) - """ - - return " ".join(self._build_command()) - - def _serial_options(self): - - if self._console: - return ["-serial", "telnet:{}:{},server,nowait".format(self._console_host, self._console)] - else: - return [] - - def _monitor_options(self): - - if self._monitor: - return ["-monitor", "telnet:{}:{},server,nowait".format(self._monitor_host, self._monitor)] - else: - return [] - - def _disk_options(self): - - options = [] - qemu_img_path = "" - qemu_path_dir = os.path.dirname(self._qemu_path) - try: - for f in os.listdir(qemu_path_dir): - if f.startswith("qemu-img"): - qemu_img_path = os.path.join(qemu_path_dir, f) - except OSError as e: - raise QemuError("Error while looking for qemu-img in {}: {}".format(qemu_path_dir, e)) - - if not qemu_img_path: - raise QemuError("Could not find qemu-img in {}".format(qemu_path_dir)) - - try: - if self._hda_disk_image: - if not os.path.isfile(self._hda_disk_image) or not os.path.exists(self._hda_disk_image): - if os.path.islink(self._hda_disk_image): - raise QemuError("hda disk image '{}' linked to '{}' is not accessible".format(self._hda_disk_image, os.path.realpath(self._hda_disk_image))) - else: - raise QemuError("hda disk image '{}' is not accessible".format(self._hda_disk_image)) - hda_disk = os.path.join(self._working_dir, "hda_disk.qcow2") - if not os.path.exists(hda_disk): - retcode = subprocess.call([qemu_img_path, "create", "-o", - "backing_file={}".format(self._hda_disk_image), - "-f", "qcow2", hda_disk]) - log.info("{} returned with {}".format(qemu_img_path, retcode)) - else: - # create a "FLASH" with 256MB if no disk image has been specified - hda_disk = os.path.join(self._working_dir, "flash.qcow2") - if not os.path.exists(hda_disk): - retcode = subprocess.call([qemu_img_path, "create", "-f", "qcow2", hda_disk, "128M"]) - log.info("{} returned with {}".format(qemu_img_path, retcode)) - - except (OSError, subprocess.SubprocessError) as e: - raise QemuError("Could not create disk image {}".format(e)) - - options.extend(["-hda", hda_disk]) - if self._hdb_disk_image: - if not os.path.isfile(self._hdb_disk_image) or not os.path.exists(self._hdb_disk_image): - if os.path.islink(self._hdb_disk_image): - raise QemuError("hdb disk image '{}' linked to '{}' is not accessible".format(self._hdb_disk_image, os.path.realpath(self._hdb_disk_image))) - else: - raise QemuError("hdb disk image '{}' is not accessible".format(self._hdb_disk_image)) - hdb_disk = os.path.join(self._working_dir, "hdb_disk.qcow2") - if not os.path.exists(hdb_disk): - try: - retcode = subprocess.call([qemu_img_path, "create", "-o", - "backing_file={}".format(self._hdb_disk_image), - "-f", "qcow2", hdb_disk]) - log.info("{} returned with {}".format(qemu_img_path, retcode)) - except (OSError, subprocess.SubprocessError) as e: - raise QemuError("Could not create disk image {}".format(e)) - options.extend(["-hdb", hdb_disk]) - - return options - - def _linux_boot_options(self): - - options = [] - if self._initrd: - if not os.path.isfile(self._initrd) or not os.path.exists(self._initrd): - if os.path.islink(self._initrd): - raise QemuError("initrd file '{}' linked to '{}' is not accessible".format(self._initrd, os.path.realpath(self._initrd))) - else: - raise QemuError("initrd file '{}' is not accessible".format(self._initrd)) - options.extend(["-initrd", self._initrd]) - if self._kernel_image: - if not os.path.isfile(self._kernel_image) or not os.path.exists(self._kernel_image): - if os.path.islink(self._kernel_image): - raise QemuError("kernel image '{}' linked to '{}' is not accessible".format(self._kernel_image, os.path.realpath(self._kernel_image))) - else: - raise QemuError("kernel image '{}' is not accessible".format(self._kernel_image)) - options.extend(["-kernel", self._kernel_image]) - if self._kernel_command_line: - options.extend(["-append", self._kernel_command_line]) - - return options - - def _network_options(self): - - network_options = [] - adapter_id = 0 - for adapter in self._ethernet_adapters: - # TODO: let users specify a base mac address - mac = "00:00:ab:%02x:%02x:%02d" % (random.randint(0x00, 0xff), random.randint(0x00, 0xff), adapter_id) - if self._legacy_networking: - network_options.extend(["-net", "nic,vlan={},macaddr={},model={}".format(adapter_id, mac, self._adapter_type)]) - else: - network_options.extend(["-device", "{},mac={},netdev=gns3-{}".format(self._adapter_type, mac, adapter_id)]) - - nio = adapter.get_nio(0) - if nio and isinstance(nio, NIO_UDP): - if self._legacy_networking: - network_options.extend(["-net", "udp,vlan={},name=gns3-{},sport={},dport={},daddr={}".format(adapter_id, - adapter_id, - nio.lport, - nio.rport, - nio.rhost)]) - else: - network_options.extend(["-netdev", "socket,id=gns3-{},udp={}:{},localaddr={}:{}".format(adapter_id, - nio.rhost, - nio.rport, - self._host, - nio.lport)]) - else: - if self._legacy_networking: - network_options.extend(["-net", "user,vlan={},name=gns3-{}".format(adapter_id, adapter_id)]) - else: - network_options.extend(["-netdev", "user,id=gns3-{}".format(adapter_id)]) - adapter_id += 1 - - return network_options - - def _build_command(self): - """ - Command to start the QEMU process. - (to be passed to subprocess.Popen()) - """ - - command = [self._qemu_path] - command.extend(["-name", self._name]) - command.extend(["-m", str(self._ram)]) - command.extend(self._disk_options()) - command.extend(self._linux_boot_options()) - command.extend(self._serial_options()) - command.extend(self._monitor_options()) - additional_options = self._options.strip() - if additional_options: - command.extend(shlex.split(additional_options)) - command.extend(self._network_options()) - return command diff --git a/gns3server/old_modules/qemu/schemas.py b/gns3server/old_modules/qemu/schemas.py deleted file mode 100644 index 32b09664..00000000 --- a/gns3server/old_modules/qemu/schemas.py +++ /dev/null @@ -1,423 +0,0 @@ -# -*- 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 . - - -QEMU_CREATE_SCHEMA = { - "$schema": "http://json-schema.org/draft-04/schema#", - "description": "Request validation to create a new QEMU VM instance", - "type": "object", - "properties": { - "name": { - "description": "QEMU VM instance name", - "type": "string", - "minLength": 1, - }, - "qemu_path": { - "description": "Path to QEMU", - "type": "string", - "minLength": 1, - }, - "qemu_id": { - "description": "QEMU VM instance ID", - "type": "integer" - }, - "console": { - "description": "console TCP port", - "minimum": 1, - "maximum": 65535, - "type": "integer" - }, - "monitor": { - "description": "monitor TCP port", - "minimum": 1, - "maximum": 65535, - "type": "integer" - }, - }, - "additionalProperties": False, - "required": ["name", "qemu_path"], -} - -QEMU_DELETE_SCHEMA = { - "$schema": "http://json-schema.org/draft-04/schema#", - "description": "Request validation to delete a QEMU VM instance", - "type": "object", - "properties": { - "id": { - "description": "QEMU VM instance ID", - "type": "integer" - }, - }, - "additionalProperties": False, - "required": ["id"] -} - -QEMU_UPDATE_SCHEMA = { - "$schema": "http://json-schema.org/draft-04/schema#", - "description": "Request validation to update a QEMU VM instance", - "type": "object", - "properties": { - "id": { - "description": "QEMU VM instance ID", - "type": "integer" - }, - "name": { - "description": "QEMU VM instance name", - "type": "string", - "minLength": 1, - }, - "qemu_path": { - "description": "path to QEMU", - "type": "string", - "minLength": 1, - }, - "hda_disk_image": { - "description": "QEMU hda disk image path", - "type": "string", - }, - "hdb_disk_image": { - "description": "QEMU hdb disk image path", - "type": "string", - }, - "ram": { - "description": "amount of RAM in MB", - "type": "integer" - }, - "adapters": { - "description": "number of adapters", - "type": "integer", - "minimum": 0, - "maximum": 32, - }, - "adapter_type": { - "description": "QEMU adapter type", - "type": "string", - "minLength": 1, - }, - "console": { - "description": "console TCP port", - "minimum": 1, - "maximum": 65535, - "type": "integer" - }, - "monitor": { - "description": "monitor TCP port", - "minimum": 1, - "maximum": 65535, - "type": "integer" - }, - "initrd": { - "description": "QEMU initrd path", - "type": "string", - }, - "kernel_image": { - "description": "QEMU kernel image path", - "type": "string", - }, - "kernel_command_line": { - "description": "QEMU kernel command line", - "type": "string", - }, - "cloud_path": { - "description": "Path to the image in the cloud object store", - "type": "string", - }, - "legacy_networking": { - "description": "Use QEMU legagy networking commands (-net syntax)", - "type": "boolean", - }, - "cpu_throttling": { - "description": "Percentage of CPU allowed for QEMU", - "minimum": 0, - "maximum": 800, - "type": "integer", - }, - "process_priority": { - "description": "Process priority for QEMU", - "enum": ["realtime", - "very high", - "high", - "normal", - "low", - "very low"] - }, - "options": { - "description": "Additional QEMU options", - "type": "string", - }, - }, - "additionalProperties": False, - "required": ["id"] -} - -QEMU_START_SCHEMA = { - "$schema": "http://json-schema.org/draft-04/schema#", - "description": "Request validation to start a QEMU VM instance", - "type": "object", - "properties": { - "id": { - "description": "QEMU VM instance ID", - "type": "integer" - }, - }, - "additionalProperties": False, - "required": ["id"] -} - -QEMU_STOP_SCHEMA = { - "$schema": "http://json-schema.org/draft-04/schema#", - "description": "Request validation to stop a QEMU VM instance", - "type": "object", - "properties": { - "id": { - "description": "QEMU VM instance ID", - "type": "integer" - }, - }, - "additionalProperties": False, - "required": ["id"] -} - -QEMU_SUSPEND_SCHEMA = { - "$schema": "http://json-schema.org/draft-04/schema#", - "description": "Request validation to suspend a QEMU VM instance", - "type": "object", - "properties": { - "id": { - "description": "QEMU VM instance ID", - "type": "integer" - }, - }, - "additionalProperties": False, - "required": ["id"] -} - -QEMU_RELOAD_SCHEMA = { - "$schema": "http://json-schema.org/draft-04/schema#", - "description": "Request validation to reload a QEMU VM instance", - "type": "object", - "properties": { - "id": { - "description": "QEMU VM instance ID", - "type": "integer" - }, - }, - "additionalProperties": False, - "required": ["id"] -} - -QEMU_ALLOCATE_UDP_PORT_SCHEMA = { - "$schema": "http://json-schema.org/draft-04/schema#", - "description": "Request validation to allocate an UDP port for a QEMU VM instance", - "type": "object", - "properties": { - "id": { - "description": "QEMU VM instance ID", - "type": "integer" - }, - "port_id": { - "description": "Unique port identifier for the QEMU VM instance", - "type": "integer" - }, - }, - "additionalProperties": False, - "required": ["id", "port_id"] -} - -QEMU_ADD_NIO_SCHEMA = { - "$schema": "http://json-schema.org/draft-04/schema#", - "description": "Request validation to add a NIO for a QEMU VM instance", - "type": "object", - - "definitions": { - "UDP": { - "description": "UDP Network Input/Output", - "properties": { - "type": { - "enum": ["nio_udp"] - }, - "lport": { - "description": "Local port", - "type": "integer", - "minimum": 1, - "maximum": 65535 - }, - "rhost": { - "description": "Remote host", - "type": "string", - "minLength": 1 - }, - "rport": { - "description": "Remote port", - "type": "integer", - "minimum": 1, - "maximum": 65535 - } - }, - "required": ["type", "lport", "rhost", "rport"], - "additionalProperties": False - }, - "Ethernet": { - "description": "Generic Ethernet Network Input/Output", - "properties": { - "type": { - "enum": ["nio_generic_ethernet"] - }, - "ethernet_device": { - "description": "Ethernet device name e.g. eth0", - "type": "string", - "minLength": 1 - }, - }, - "required": ["type", "ethernet_device"], - "additionalProperties": False - }, - "LinuxEthernet": { - "description": "Linux Ethernet Network Input/Output", - "properties": { - "type": { - "enum": ["nio_linux_ethernet"] - }, - "ethernet_device": { - "description": "Ethernet device name e.g. eth0", - "type": "string", - "minLength": 1 - }, - }, - "required": ["type", "ethernet_device"], - "additionalProperties": False - }, - "TAP": { - "description": "TAP Network Input/Output", - "properties": { - "type": { - "enum": ["nio_tap"] - }, - "tap_device": { - "description": "TAP device name e.g. tap0", - "type": "string", - "minLength": 1 - }, - }, - "required": ["type", "tap_device"], - "additionalProperties": False - }, - "UNIX": { - "description": "UNIX Network Input/Output", - "properties": { - "type": { - "enum": ["nio_unix"] - }, - "local_file": { - "description": "path to the UNIX socket file (local)", - "type": "string", - "minLength": 1 - }, - "remote_file": { - "description": "path to the UNIX socket file (remote)", - "type": "string", - "minLength": 1 - }, - }, - "required": ["type", "local_file", "remote_file"], - "additionalProperties": False - }, - "VDE": { - "description": "VDE Network Input/Output", - "properties": { - "type": { - "enum": ["nio_vde"] - }, - "control_file": { - "description": "path to the VDE control file", - "type": "string", - "minLength": 1 - }, - "local_file": { - "description": "path to the VDE control file", - "type": "string", - "minLength": 1 - }, - }, - "required": ["type", "control_file", "local_file"], - "additionalProperties": False - }, - "NULL": { - "description": "NULL Network Input/Output", - "properties": { - "type": { - "enum": ["nio_null"] - }, - }, - "required": ["type"], - "additionalProperties": False - }, - }, - - "properties": { - "id": { - "description": "QEMU VM instance ID", - "type": "integer" - }, - "port_id": { - "description": "Unique port identifier for the QEMU VM instance", - "type": "integer" - }, - "port": { - "description": "Port number", - "type": "integer", - "minimum": 0, - "maximum": 32 - }, - "nio": { - "type": "object", - "description": "Network Input/Output", - "oneOf": [ - {"$ref": "#/definitions/UDP"}, - {"$ref": "#/definitions/Ethernet"}, - {"$ref": "#/definitions/LinuxEthernet"}, - {"$ref": "#/definitions/TAP"}, - {"$ref": "#/definitions/UNIX"}, - {"$ref": "#/definitions/VDE"}, - {"$ref": "#/definitions/NULL"}, - ] - }, - }, - "additionalProperties": False, - "required": ["id", "port_id", "port", "nio"] -} - - -QEMU_DELETE_NIO_SCHEMA = { - "$schema": "http://json-schema.org/draft-04/schema#", - "description": "Request validation to delete a NIO for a QEMU VM instance", - "type": "object", - "properties": { - "id": { - "description": "QEMU VM instance ID", - "type": "integer" - }, - "port": { - "description": "Port number", - "type": "integer", - "minimum": 0, - "maximum": 32 - }, - }, - "additionalProperties": False, - "required": ["id", "port"] -}