mirror of
https://github.com/GNS3/gns3-server.git
synced 2026-11-03 05:51:15 +02:00
Yet another PEP 8 :)
This commit is contained in:
1 parent
7cf409c392
commit
bbee5f90a0
106 files changed
+9
-29
No files matched your search
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""
|
||||
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)
|
||||
Whitespace-only changes.
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
class Adapter(object):
|
||||
|
||||
"""
|
||||
Base class for adapters.
|
||||
|
||||
:param interfaces: number of interfaces supported by this adapter.
|
||||
"""
|
||||
|
||||
def __init__(self, interfaces=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
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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"
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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"
|
||||
File diff suppressed because it is too large.
Load diff
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""
|
||||
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
|
||||
@@ -1,648 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2013, 2014 James E. Carpenter
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
import socket
|
||||
import sys
|
||||
import os
|
||||
import select
|
||||
import fcntl
|
||||
import struct
|
||||
import termios
|
||||
import tty
|
||||
import time
|
||||
import argparse
|
||||
import traceback
|
||||
|
||||
|
||||
import logging
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# Escape characters
|
||||
ESC_CHAR = '^^' # can be overriden from command line
|
||||
ESC_QUIT = 'q'
|
||||
|
||||
# IOU seems to only send *1* byte at a time. If
|
||||
# they ever fix that we'll be ready for it.
|
||||
BUFFER_SIZE = 1024
|
||||
|
||||
# How long to wait before retrying a connection (seconds)
|
||||
RETRY_DELAY = 3
|
||||
|
||||
# How often to test an idle connection (seconds)
|
||||
POLL_TIMEOUT = 3
|
||||
|
||||
|
||||
EXIT_SUCCESS = 0
|
||||
EXIT_FAILURE = 1
|
||||
EXIT_ABORT = 2
|
||||
|
||||
# Mostly from:
|
||||
# https://code.google.com/p/miniboa/source/browse/trunk/miniboa/telnet.py
|
||||
#--[ Telnet Commands ]---------------------------------------------------------
|
||||
SE = 240 # End of sub-negotiation parameters
|
||||
NOP = 241 # No operation
|
||||
DATMK = 242 # Data stream portion of a sync.
|
||||
BREAK = 243 # NVT Character BRK
|
||||
IP = 244 # Interrupt Process
|
||||
AO = 245 # Abort Output
|
||||
AYT = 246 # Are you there
|
||||
EC = 247 # Erase Character
|
||||
EL = 248 # Erase Line
|
||||
GA = 249 # The Go Ahead Signal
|
||||
SB = 250 # Sub-option to follow
|
||||
WILL = 251 # Will; request or confirm option begin
|
||||
WONT = 252 # Wont; deny option request
|
||||
DO = 253 # Do = Request or confirm remote option
|
||||
DONT = 254 # Don't = Demand or confirm option halt
|
||||
IAC = 255 # Interpret as Command
|
||||
SEND = 1 # Sub-process negotiation SEND command
|
||||
IS = 0 # Sub-process negotiation IS command
|
||||
#--[ Telnet Options ]----------------------------------------------------------
|
||||
BINARY = 0 # Transmit Binary
|
||||
ECHO = 1 # Echo characters back to sender
|
||||
RECON = 2 # Reconnection
|
||||
SGA = 3 # Suppress Go-Ahead
|
||||
TMARK = 6 # Timing Mark
|
||||
TTYPE = 24 # Terminal Type
|
||||
NAWS = 31 # Negotiate About Window Size
|
||||
LINEMO = 34 # Line Mode
|
||||
|
||||
|
||||
class FileLock:
|
||||
|
||||
# struct flock { /* from fcntl(2) */
|
||||
# ...
|
||||
# short l_type; /* Type of lock: F_RDLCK,
|
||||
# F_WRLCK, F_UNLCK */
|
||||
# short l_whence; /* How to interpret l_start:
|
||||
# SEEK_SET, SEEK_CUR, SEEK_END */
|
||||
# off_t l_start; /* Starting offset for lock */
|
||||
# off_t l_len; /* Number of bytes to lock */
|
||||
# pid_t l_pid; /* PID of process blocking our lock
|
||||
# (F_GETLK only) */
|
||||
# ...
|
||||
# };
|
||||
_flock = struct.Struct('hhqql')
|
||||
|
||||
def __init__(self, fname=None):
|
||||
self.fd = None
|
||||
self.fname = fname
|
||||
|
||||
def get_lock(self):
|
||||
flk = self._flock.pack(fcntl.F_WRLCK, os.SEEK_SET,
|
||||
0, 0, os.getpid())
|
||||
flk = self._flock.unpack(
|
||||
fcntl.fcntl(self.fd, fcntl.F_GETLK, flk))
|
||||
|
||||
# If it's not locked (or is locked by us) then return None,
|
||||
# otherwise return the PID of the owner.
|
||||
if flk[0] == fcntl.F_UNLCK:
|
||||
return None
|
||||
return flk[4]
|
||||
|
||||
def lock(self):
|
||||
try:
|
||||
self.fd = open('{}.lck'.format(self.fname), 'a')
|
||||
except Exception as e:
|
||||
raise LockError("Couldn't get lock on {}: {}"
|
||||
.format(self.fname, e))
|
||||
|
||||
flk = self._flock.pack(fcntl.F_WRLCK, os.SEEK_SET, 0, 0, 0)
|
||||
try:
|
||||
fcntl.fcntl(self.fd, fcntl.F_SETLK, flk)
|
||||
except BlockingIOError:
|
||||
raise LockError("Already connected. PID {} has lock on {}"
|
||||
.format(self.get_lock(), self.fname))
|
||||
|
||||
# If we got here then we must have the lock. Store the PID.
|
||||
self.fd.truncate(0)
|
||||
self.fd.write('{}\n'.format(os.getpid()))
|
||||
self.fd.flush()
|
||||
|
||||
def unlock(self):
|
||||
if self.fd:
|
||||
# Deleting first prevents a race condition
|
||||
try:
|
||||
os.unlink(self.fd.name)
|
||||
except FileNotFoundError as e:
|
||||
log.debug("{}".format(e))
|
||||
self.fd.close()
|
||||
|
||||
def __enter__(self):
|
||||
self.lock()
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
self.unlock()
|
||||
return False
|
||||
|
||||
|
||||
class Console:
|
||||
|
||||
def fileno(self):
|
||||
raise NotImplementedError("Only routers have fileno()")
|
||||
|
||||
|
||||
class Router:
|
||||
pass
|
||||
|
||||
|
||||
class TTY(Console):
|
||||
|
||||
def read(self, fileno, bufsize):
|
||||
return self.fd.read(bufsize)
|
||||
|
||||
def write(self, buf):
|
||||
return self.fd.write(buf)
|
||||
|
||||
def register(self, epoll):
|
||||
self.epoll = epoll
|
||||
epoll.register(self.fd, select.EPOLLIN | select.EPOLLET)
|
||||
|
||||
def __enter__(self):
|
||||
try:
|
||||
self.fd = open('/dev/tty', 'r+b', buffering=0)
|
||||
except OSError as e:
|
||||
raise TTYError("Couldn't open controlling TTY: {}".format(e))
|
||||
|
||||
# Save original flags
|
||||
self.termios = termios.tcgetattr(self.fd)
|
||||
self.fcntl = fcntl.fcntl(self.fd, fcntl.F_GETFL)
|
||||
|
||||
# Update flags
|
||||
tty.setraw(self.fd, termios.TCSANOW)
|
||||
fcntl.fcntl(self.fd, fcntl.F_SETFL, self.fcntl | os.O_NONBLOCK)
|
||||
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
|
||||
# Restore flags to original settings
|
||||
termios.tcsetattr(self.fd, termios.TCSANOW, self.termios)
|
||||
fcntl.fcntl(self.fd, fcntl.F_SETFL, self.fcntl)
|
||||
|
||||
self.fd.close()
|
||||
|
||||
return False
|
||||
|
||||
|
||||
class TelnetServer(Console):
|
||||
|
||||
def __init__(self, addr, port, stop_event):
|
||||
self.addr = addr
|
||||
self.port = port
|
||||
self.fd_dict = {}
|
||||
self.stop_event = stop_event
|
||||
|
||||
def read(self, fileno, bufsize):
|
||||
# Someone wants to connect?
|
||||
if fileno == self.sock_fd.fileno():
|
||||
self._accept()
|
||||
return None
|
||||
|
||||
self._cur_fileno = fileno
|
||||
|
||||
# Read a maximum of _bufsize_ bytes without blocking. When it
|
||||
# would want to block it means there's no more data. An empty
|
||||
# buffer normally means that we've been disconnected.
|
||||
try:
|
||||
buf = self._read_cur(bufsize, socket.MSG_DONTWAIT)
|
||||
except BlockingIOError:
|
||||
return None
|
||||
except ConnectionResetError:
|
||||
buf = b''
|
||||
if not buf:
|
||||
self._disconnect(fileno)
|
||||
|
||||
# Process and remove any telnet commands from the buffer
|
||||
if IAC in buf:
|
||||
buf = self._IAC_parser(buf)
|
||||
|
||||
return buf
|
||||
|
||||
def write(self, buf):
|
||||
for fd in self.fd_dict.values():
|
||||
fd.send(buf)
|
||||
|
||||
def register(self, epoll):
|
||||
self.epoll = epoll
|
||||
epoll.register(self.sock_fd, select.EPOLLIN)
|
||||
|
||||
def _read_block(self, bufsize):
|
||||
buf = self._read_cur(bufsize, socket.MSG_WAITALL)
|
||||
# If we don't get everything we were looking for then the
|
||||
# client probably disconnected.
|
||||
if len(buf) < bufsize:
|
||||
self._disconnect(self._cur_fileno)
|
||||
return buf
|
||||
|
||||
def _read_cur(self, bufsize, flags):
|
||||
return self.fd_dict[self._cur_fileno].recv(bufsize, flags)
|
||||
|
||||
def _write_cur(self, buf):
|
||||
return self.fd_dict[self._cur_fileno].send(buf)
|
||||
|
||||
def _IAC_parser(self, buf):
|
||||
skip_to = 0
|
||||
while not self.stop_event.is_set():
|
||||
# Locate an IAC to process
|
||||
iac_loc = buf.find(IAC, skip_to)
|
||||
if iac_loc < 0:
|
||||
break
|
||||
|
||||
# Get the TELNET command
|
||||
iac_cmd = bytearray([IAC])
|
||||
try:
|
||||
iac_cmd.append(buf[iac_loc + 1])
|
||||
except IndexError:
|
||||
buf.extend(self._read_block(1))
|
||||
iac_cmd.append(buf[iac_loc + 1])
|
||||
|
||||
# Is this just a 2-byte TELNET command?
|
||||
if iac_cmd[1] not in [WILL, WONT, DO, DONT]:
|
||||
if iac_cmd[1] == AYT:
|
||||
log.debug("Telnet server received Are-You-There (AYT)")
|
||||
self._write_cur(
|
||||
b'\r\nYour Are-You-There received. I am here.\r\n'
|
||||
)
|
||||
elif iac_cmd[1] == IAC:
|
||||
# It's data, not an IAC
|
||||
iac_cmd.pop()
|
||||
# This prevents the 0xff from being
|
||||
# interputed as yet another IAC
|
||||
skip_to = iac_loc + 1
|
||||
log.debug("Received IAC IAC")
|
||||
elif iac_cmd[1] == NOP:
|
||||
pass
|
||||
else:
|
||||
log.debug("Unhandled telnet command: "
|
||||
"{0:#x} {1:#x}".format(*iac_cmd))
|
||||
|
||||
# This must be a 3-byte TELNET command
|
||||
else:
|
||||
try:
|
||||
iac_cmd.append(buf[iac_loc + 2])
|
||||
except IndexError:
|
||||
buf.extend(self._read_block(1))
|
||||
iac_cmd.append(buf[iac_loc + 2])
|
||||
# We do ECHO, SGA, and BINARY. Period.
|
||||
if iac_cmd[1] == DO and iac_cmd[2] not in [ECHO, SGA, BINARY]:
|
||||
self._write_cur(bytes([IAC, WONT, iac_cmd[2]]))
|
||||
log.debug("Telnet WON'T {:#x}".format(iac_cmd[2]))
|
||||
else:
|
||||
log.debug("Unhandled telnet command: "
|
||||
"{0:#x} {1:#x} {2:#x}".format(*iac_cmd))
|
||||
|
||||
# Remove the entire TELNET command from the buffer
|
||||
buf = buf.replace(iac_cmd, b'', 1)
|
||||
|
||||
# Return the new copy of the buffer, minus telnet commands
|
||||
return buf
|
||||
|
||||
def _accept(self):
|
||||
fd, addr = self.sock_fd.accept()
|
||||
self.fd_dict[fd.fileno()] = fd
|
||||
self.epoll.register(fd, select.EPOLLIN | select.EPOLLET)
|
||||
|
||||
log.info("Telnet connection from {}:{}".format(addr[0], addr[1]))
|
||||
|
||||
# This is a one-way negotiation. This is very basic so there
|
||||
# shouldn't be any problems with any decent client.
|
||||
fd.send(bytes([IAC, WILL, ECHO,
|
||||
IAC, WILL, SGA,
|
||||
IAC, WILL, BINARY,
|
||||
IAC, DO, BINARY]))
|
||||
|
||||
if args.telnet_limit and len(self.fd_dict) > args.telnet_limit:
|
||||
fd.send(b'\r\nToo many connections\r\n')
|
||||
self._disconnect(fd.fileno())
|
||||
log.warn("Client disconnected because of too many connections. "
|
||||
"(limit currently {})".format(args.telnet_limit))
|
||||
|
||||
def _disconnect(self, fileno):
|
||||
fd = self.fd_dict.pop(fileno)
|
||||
log.info("Telnet client disconnected")
|
||||
try:
|
||||
fd.shutdown(socket.SHUT_RDWR)
|
||||
except OSError as e:
|
||||
log.warn("shutdown: {}".format(e))
|
||||
fd.close()
|
||||
|
||||
def __enter__(self):
|
||||
# Open a socket and start listening
|
||||
sock_fd = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
sock_fd.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
try:
|
||||
sock_fd.bind((self.addr, self.port))
|
||||
except OSError:
|
||||
raise TelnetServerError("Cannot bind to {}:{}"
|
||||
.format(self.addr, self.port))
|
||||
|
||||
sock_fd.listen(socket.SOMAXCONN)
|
||||
self.sock_fd = sock_fd
|
||||
log.info("Telnet server ready for connections on {}:{}".format(self.addr, self.port))
|
||||
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
for fileno in list(self.fd_dict.keys()):
|
||||
self._disconnect(fileno)
|
||||
self.sock_fd.close()
|
||||
return False
|
||||
|
||||
|
||||
class IOU(Router):
|
||||
|
||||
def __init__(self, ttyC, ttyS, stop_event):
|
||||
self.ttyC = ttyC
|
||||
self.ttyS = ttyS
|
||||
self.stop_event = stop_event
|
||||
|
||||
def read(self, bufsize):
|
||||
try:
|
||||
buf = self.fd.recv(bufsize)
|
||||
except BlockingIOError:
|
||||
return None
|
||||
return buf
|
||||
|
||||
def write(self, buf):
|
||||
try:
|
||||
self.fd.send(buf)
|
||||
except BlockingIOError:
|
||||
return
|
||||
|
||||
def _open(self):
|
||||
self.fd = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
|
||||
self.fd.setblocking(False)
|
||||
|
||||
def _bind(self):
|
||||
try:
|
||||
os.unlink(self.ttyC)
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
except Exception as e:
|
||||
raise NetioError("Couldn't unlink socket {}: {}".format(self.ttyC, e))
|
||||
|
||||
try:
|
||||
self.fd.bind(self.ttyC)
|
||||
except Exception as e:
|
||||
raise NetioError("Couldn't create socket {}: {}".format(self.ttyC, e))
|
||||
|
||||
def _connect(self):
|
||||
# Keep trying until we connect or die trying
|
||||
while not self.stop_event.is_set():
|
||||
try:
|
||||
self.fd.connect(self.ttyS)
|
||||
except FileNotFoundError:
|
||||
log.debug("Waiting to connect to {}".format(self.ttyS))
|
||||
time.sleep(RETRY_DELAY)
|
||||
except Exception as e:
|
||||
raise NetioError("Couldn't connect to socket {}: {}".format(self.ttyS, e))
|
||||
else:
|
||||
break
|
||||
|
||||
def register(self, epoll):
|
||||
self.epoll = epoll
|
||||
epoll.register(self.fd, select.EPOLLIN | select.EPOLLET)
|
||||
|
||||
def fileno(self):
|
||||
return self.fd.fileno()
|
||||
|
||||
def __enter__(self):
|
||||
self._open()
|
||||
self._bind()
|
||||
self._connect()
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
os.unlink(self.ttyC)
|
||||
self.fd.close()
|
||||
return False
|
||||
|
||||
|
||||
class IOUConError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class LockError(IOUConError):
|
||||
pass
|
||||
|
||||
|
||||
class NetioError(IOUConError):
|
||||
pass
|
||||
|
||||
|
||||
class TTYError(IOUConError):
|
||||
pass
|
||||
|
||||
|
||||
class TelnetServerError(IOUConError):
|
||||
pass
|
||||
|
||||
|
||||
class ConfigError(IOUConError):
|
||||
pass
|
||||
|
||||
|
||||
def mkdir_netio(netio_dir):
|
||||
try:
|
||||
os.mkdir(netio_dir)
|
||||
except FileExistsError:
|
||||
pass
|
||||
except Exception as e:
|
||||
raise NetioError("Couldn't create directory {}: {}".format(netio_dir, e))
|
||||
|
||||
|
||||
def send_recv_loop(console, router, esc_char, stop_event):
|
||||
|
||||
epoll = select.epoll()
|
||||
router.register(epoll)
|
||||
console.register(epoll)
|
||||
|
||||
router_fileno = router.fileno()
|
||||
esc_quit = bytes(ESC_QUIT.upper(), 'ascii')
|
||||
esc_state = False
|
||||
|
||||
while not stop_event.is_set():
|
||||
event_list = epoll.poll(timeout=POLL_TIMEOUT)
|
||||
|
||||
# When/if the poll times out we send an empty datagram. If IOU
|
||||
# has gone away then this will toss a ConnectionRefusedError
|
||||
# exception.
|
||||
if not event_list:
|
||||
router.write(b'')
|
||||
continue
|
||||
|
||||
for fileno, event in event_list:
|
||||
buf = bytearray()
|
||||
|
||||
# IOU --> tty(s)
|
||||
if fileno == router_fileno:
|
||||
while not stop_event.is_set():
|
||||
data = router.read(BUFFER_SIZE)
|
||||
if not data:
|
||||
break
|
||||
buf.extend(data)
|
||||
console.write(buf)
|
||||
|
||||
# tty --> IOU
|
||||
else:
|
||||
while not stop_event.is_set():
|
||||
data = console.read(fileno, BUFFER_SIZE)
|
||||
if not data:
|
||||
break
|
||||
buf.extend(data)
|
||||
|
||||
# If we just received the escape character then
|
||||
# enter the escape state.
|
||||
#
|
||||
# If we are in the escape state then check for a
|
||||
# quit command. Or if it's the escape character then
|
||||
# send the escape character. Else, send the escape
|
||||
# character we ate earlier and whatever character we
|
||||
# just got. Exit escape state.
|
||||
#
|
||||
# If we're not in the escape state and this isn't an
|
||||
# escape character then just send it to IOU.
|
||||
if esc_state:
|
||||
if buf.upper() == esc_quit:
|
||||
sys.exit(EXIT_SUCCESS)
|
||||
elif buf == esc_char:
|
||||
router.write(esc_char)
|
||||
else:
|
||||
router.write(esc_char)
|
||||
router.write(buf)
|
||||
esc_state = False
|
||||
elif buf == esc_char:
|
||||
esc_state = True
|
||||
else:
|
||||
router.write(buf)
|
||||
|
||||
|
||||
def get_args():
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Connect to an IOU console port.')
|
||||
parser.add_argument('-d', '--debug', action='store_true',
|
||||
help='display some debugging information')
|
||||
parser.add_argument('-e', '--escape',
|
||||
help='set escape character (default: %(default)s)',
|
||||
default=ESC_CHAR, metavar='CHAR')
|
||||
parser.add_argument('-t', '--telnet-server',
|
||||
help='start telnet server listening on ADDR:PORT',
|
||||
metavar='ADDR:PORT', default=False)
|
||||
parser.add_argument('-l', '--telnet-limit',
|
||||
help='maximum number of simultaneous '
|
||||
'telnet connections (default: %(default)s)',
|
||||
metavar='LIMIT', type=int, default=1)
|
||||
parser.add_argument('appl_id', help='IOU instance identifier')
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def get_escape_character(escape):
|
||||
|
||||
# Figure out the escape character to use.
|
||||
# Can be any ASCII character or a spelled out control
|
||||
# character, like "^e". The string "none" disables it.
|
||||
if escape.lower() == 'none':
|
||||
esc_char = b''
|
||||
elif len(escape) == 2 and escape[0] == '^':
|
||||
c = ord(escape[1].upper()) - 0x40
|
||||
if not 0 <= c <= 0x1f: # control code range
|
||||
raise ConfigError("Invalid control code")
|
||||
esc_char = bytes([c])
|
||||
elif len(escape) == 1:
|
||||
try:
|
||||
esc_char = bytes(escape, 'ascii')
|
||||
except ValueError as e:
|
||||
raise ConfigError("Invalid escape character") from e
|
||||
else:
|
||||
raise ConfigError("Invalid length for escape character")
|
||||
|
||||
return esc_char
|
||||
|
||||
|
||||
def start_ioucon(cmdline_args, stop_event):
|
||||
|
||||
global args
|
||||
args = cmdline_args
|
||||
|
||||
if args.debug:
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
else:
|
||||
# default logging level
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
# Create paths for the Unix domain sockets
|
||||
netio = '/tmp/netio{}'.format(os.getuid())
|
||||
ttyC = '{}/ttyC{}'.format(netio, args.appl_id)
|
||||
ttyS = '{}/ttyS{}'.format(netio, args.appl_id)
|
||||
|
||||
try:
|
||||
mkdir_netio(netio)
|
||||
with FileLock(ttyC):
|
||||
esc_char = get_escape_character(args.escape)
|
||||
|
||||
if args.telnet_server:
|
||||
addr, _, port = args.telnet_server.partition(':')
|
||||
nport = 0
|
||||
try:
|
||||
nport = int(port)
|
||||
except ValueError:
|
||||
pass
|
||||
if addr == '' or nport == 0:
|
||||
raise ConfigError('format for --telnet-server must be '
|
||||
'ADDR:PORT (like 127.0.0.1:20000)')
|
||||
|
||||
while not stop_event.is_set():
|
||||
try:
|
||||
if args.telnet_server:
|
||||
with TelnetServer(addr, nport, stop_event) as console:
|
||||
with IOU(ttyC, ttyS, stop_event) as router:
|
||||
send_recv_loop(console, router, b'', stop_event)
|
||||
else:
|
||||
with IOU(ttyC, ttyS, stop_event) as router, TTY() as console:
|
||||
send_recv_loop(console, router, esc_char, stop_event)
|
||||
except ConnectionRefusedError:
|
||||
pass
|
||||
except KeyboardInterrupt:
|
||||
sys.exit(EXIT_ABORT)
|
||||
finally:
|
||||
# Put us at the beginning of a line
|
||||
if not args.telnet_server:
|
||||
print()
|
||||
|
||||
except IOUConError as e:
|
||||
if args.debug:
|
||||
traceback.print_exc(file=sys.stderr)
|
||||
else:
|
||||
log.error("ioucon: {}".format(e))
|
||||
sys.exit(EXIT_FAILURE)
|
||||
|
||||
log.info("exiting...")
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
import threading
|
||||
stop_event = threading.Event()
|
||||
args = get_args()
|
||||
start_ioucon(args, stop_event)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Whitespace-only changes.
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""
|
||||
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
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""
|
||||
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"
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""
|
||||
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"
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""
|
||||
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"
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
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"]
|
||||
}
|
||||
Reference in new issue
Block a user