From 7a523990a622b95a9e06729ac0ed255d063ea0e5 Mon Sep 17 00:00:00 2001 From: grossmj Date: Sun, 15 May 2016 11:23:14 -0600 Subject: [PATCH] Base for generic switch nodes. Ref #492. --- gns3server/compute/__init__.py | 4 +- gns3server/compute/builtin/__init__.py | 36 +++ .../compute/builtin/builtin_node_factory.py | 39 +++ gns3server/compute/builtin/nodes/__init__.py | 0 .../compute/builtin/nodes/ethernet_hub.py | 177 +++++++++++ gns3server/compute/dynamips/__init__.py | 5 +- ...{dynamips_vm.py => dynamips_vm_factory.py} | 2 +- gns3server/controller/node.py | 5 +- gns3server/handlers/api/compute/__init__.py | 2 +- .../api/compute/ethernet_hub_handler.py | 226 +++++++++++++++ gns3server/schemas/ethernet_hub.py | 274 ++++++++++++++++++ gns3server/schemas/node.py | 2 +- 12 files changed, 763 insertions(+), 9 deletions(-) create mode 100644 gns3server/compute/builtin/__init__.py create mode 100644 gns3server/compute/builtin/builtin_node_factory.py create mode 100644 gns3server/compute/builtin/nodes/__init__.py create mode 100644 gns3server/compute/builtin/nodes/ethernet_hub.py rename gns3server/compute/dynamips/{dynamips_vm.py => dynamips_vm_factory.py} (98%) create mode 100644 gns3server/handlers/api/compute/ethernet_hub_handler.py create mode 100644 gns3server/schemas/ethernet_hub.py diff --git a/gns3server/compute/__init__.py b/gns3server/compute/__init__.py index a8955e4d..5d454a86 100644 --- a/gns3server/compute/__init__.py +++ b/gns3server/compute/__init__.py @@ -17,13 +17,15 @@ import sys import os + +from .builtin import Builtin from .vpcs import VPCS from .virtualbox import VirtualBox from .dynamips import Dynamips from .qemu import Qemu from .vmware import VMware -MODULES = [VPCS, VirtualBox, Dynamips, Qemu, VMware] +MODULES = [Builtin, VPCS, VirtualBox, Dynamips, Qemu, VMware] if sys.platform.startswith("linux") or hasattr(sys, "_called_from_test") or os.environ.get("PYTEST_BUILD_DOCUMENTATION") == "1": diff --git a/gns3server/compute/builtin/__init__.py b/gns3server/compute/builtin/__init__.py new file mode 100644 index 00000000..8123b201 --- /dev/null +++ b/gns3server/compute/builtin/__init__.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2016 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 . + +""" +Builtin nodes server module. +""" + + +from ..base_manager import BaseManager +from .builtin_node_factory import BuiltinNodeFactory + +import logging +log = logging.getLogger(__name__) + + +class Builtin(BaseManager): + + _NODE_CLASS = BuiltinNodeFactory + + def __init__(self): + + super().__init__() diff --git a/gns3server/compute/builtin/builtin_node_factory.py b/gns3server/compute/builtin/builtin_node_factory.py new file mode 100644 index 00000000..9f9e2285 --- /dev/null +++ b/gns3server/compute/builtin/builtin_node_factory.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2016 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 ..node_error import NodeError +from .nodes.ethernet_hub import EthernetHub + +import logging +log = logging.getLogger(__name__) + +BUILTIN_NODES = {'ethernet_hub': EthernetHub} + + +class BuiltinNodeFactory: + + """ + Factory to create an builtin object based on the node type. + """ + + def __new__(cls, name, node_id, project, manager, node_type, **kwargs): + + if node_type not in BUILTIN_NODES: + raise NodeError("Unknown node type: {}".format(node_type)) + + return BUILTIN_NODES[node_type](name, node_id, project, manager, **kwargs) diff --git a/gns3server/compute/builtin/nodes/__init__.py b/gns3server/compute/builtin/nodes/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/gns3server/compute/builtin/nodes/ethernet_hub.py b/gns3server/compute/builtin/nodes/ethernet_hub.py new file mode 100644 index 00000000..70e434ea --- /dev/null +++ b/gns3server/compute/builtin/nodes/ethernet_hub.py @@ -0,0 +1,177 @@ +# -*- 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 . + +""" +Hub object that uses the Bridge interface to create a hub with ports. +""" + +import asyncio + +from ...node_error import NodeError +from ...base_node import BaseNode + +import logging +log = logging.getLogger(__name__) + + +class EthernetHub(BaseNode): + + """ + Ethernet hub + + :param name: name for this hub + :param node_id: Node identifier + :param project: Project instance + :param manager: Parent VM Manager + """ + + def __init__(self, name, node_id, project, manager): + + super().__init__(name, node_id, project, manager) + self._mappings = {} + + def __json__(self): + + return {"name": self.name, + "project_id": self.project.id, + "node_id": self.id} + + # @asyncio.coroutine + # def create(self): + # + # yield from Bridge.create(self) + # log.info('Ethernet hub "{name}" [{id}] has been created'.format(name=self._name, id=self._id)) + # + # @property + # def mappings(self): + # """ + # Returns port mappings + # + # :returns: mappings list + # """ + # + # return self._mappings + # + # @asyncio.coroutine + # def delete(self): + # """ + # Deletes this hub. + # """ + # + # for nio in self._nios: + # if nio and isinstance(nio, NIOUDP): + # self.manager.port_manager.release_udp_port(nio.lport, self._project) + # + # try: + # yield from Bridge.delete(self) + # log.info('Ethernet hub "{name}" [{id}] has been deleted'.format(name=self._name, id=self._id)) + # except DynamipsError: + # log.debug("Could not properly delete Ethernet hub {}".format(self._name)) + # if self._hypervisor and not self._hypervisor.devices: + # yield from self.hypervisor.stop() + # + # @asyncio.coroutine + # def add_nio(self, nio, port_number): + # """ + # Adds a NIO as new port on this hub. + # + # :param nio: NIO instance to add + # :param port_number: port to allocate for the NIO + # """ + # + # if port_number in self._mappings: + # raise DynamipsError("Port {} isn't free".format(port_number)) + # + # yield from Bridge.add_nio(self, nio) + # + # log.info('Ethernet hub "{name}" [{id}]: NIO {nio} bound to port {port}'.format(name=self._name, + # id=self._id, + # nio=nio, + # port=port_number)) + # self._mappings[port_number] = nio + # + # @asyncio.coroutine + # def remove_nio(self, port_number): + # """ + # Removes the specified NIO as member of this hub. + # + # :param port_number: allocated port number + # + # :returns: the NIO that was bound to the allocated port + # """ + # + # if port_number not in self._mappings: + # raise DynamipsError("Port {} is not allocated".format(port_number)) + # + # nio = self._mappings[port_number] + # if isinstance(nio, NIOUDP): + # self.manager.port_manager.release_udp_port(nio.lport, self._project) + # yield from Bridge.remove_nio(self, nio) + # + # log.info('Ethernet switch "{name}" [{id}]: NIO {nio} removed from port {port}'.format(name=self._name, + # id=self._id, + # nio=nio, + # port=port_number)) + # + # del self._mappings[port_number] + # return nio + # + # @asyncio.coroutine + # def start_capture(self, port_number, output_file, data_link_type="DLT_EN10MB"): + # """ + # Starts a packet capture. + # + # :param port_number: allocated port number + # :param output_file: PCAP destination file for the capture + # :param data_link_type: PCAP data link type (DLT_*), default is DLT_EN10MB + # """ + # + # if port_number not in self._mappings: + # raise DynamipsError("Port {} is not allocated".format(port_number)) + # + # nio = self._mappings[port_number] + # + # data_link_type = data_link_type.lower() + # if data_link_type.startswith("dlt_"): + # data_link_type = data_link_type[4:] + # + # if nio.input_filter[0] is not None and nio.output_filter[0] is not None: + # raise DynamipsError("Port {} has already a filter applied".format(port_number)) + # + # yield from nio.bind_filter("both", "capture") + # yield from nio.setup_filter("both", '{} "{}"'.format(data_link_type, output_file)) + # + # log.info('Ethernet hub "{name}" [{id}]: starting packet capture on port {port}'.format(name=self._name, + # id=self._id, + # port=port_number)) + # + # @asyncio.coroutine + # def stop_capture(self, port_number): + # """ + # Stops a packet capture. + # + # :param port_number: allocated port number + # """ + # + # if port_number not in self._mappings: + # raise DynamipsError("Port {} is not allocated".format(port_number)) + # + # nio = self._mappings[port_number] + # yield from nio.unbind_filter("both") + # log.info('Ethernet hub "{name}" [{id}]: stopping packet capture on port {port}'.format(name=self._name, + # id=self._id, + # port=port_number)) diff --git a/gns3server/compute/dynamips/__init__.py b/gns3server/compute/dynamips/__init__.py index 069553d2..69c85fc4 100644 --- a/gns3server/compute/dynamips/__init__.py +++ b/gns3server/compute/dynamips/__init__.py @@ -42,7 +42,7 @@ from ..port_manager import PortManager from .dynamips_error import DynamipsError from .hypervisor import Hypervisor from .nodes.router import Router -from .dynamips_vm import DynamipsVM +from .dynamips_vm_factory import DynamipsVMFactory from .dynamips_device import DynamipsDevice # NIOs @@ -102,7 +102,7 @@ WIC_MATRIX = {"WIC-1ENET": WIC_1ENET, class Dynamips(BaseManager): - _NODE_CLASS = DynamipsVM + _NODE_CLASS = DynamipsVMFactory _DEVICE_CLASS = DynamipsDevice _ghost_ios_lock = None @@ -121,7 +121,6 @@ class Dynamips(BaseManager): :returns: a free dynamips id """ self._dynamips_ids.setdefault(project_id, set()) - dynamips_id = 0 for dynamips_id in range(1, 4097): if dynamips_id not in self._dynamips_ids[project_id]: self._dynamips_ids[project_id].add(dynamips_id) diff --git a/gns3server/compute/dynamips/dynamips_vm.py b/gns3server/compute/dynamips/dynamips_vm_factory.py similarity index 98% rename from gns3server/compute/dynamips/dynamips_vm.py rename to gns3server/compute/dynamips/dynamips_vm_factory.py index c7faf711..f0c7cf1b 100644 --- a/gns3server/compute/dynamips/dynamips_vm.py +++ b/gns3server/compute/dynamips/dynamips_vm_factory.py @@ -37,7 +37,7 @@ PLATFORMS = {'c1700': C1700, 'c7200': C7200} -class DynamipsVM: +class DynamipsVMFactory: """ Factory to create an Router object based on the correct platform. diff --git a/gns3server/controller/node.py b/gns3server/controller/node.py index 98d296c2..b9920e3e 100644 --- a/gns3server/controller/node.py +++ b/gns3server/controller/node.py @@ -152,8 +152,9 @@ class Node: """ data = copy.copy(self._properties) data["name"] = self._name - data["console"] = self._console - data["console_type"] = self._console_type + if self._console: + data["console"] = self._console + data["console_type"] = self._console_type # None properties should be send. Because it can mean the emulator doesn't support it for key in list(data.keys()): diff --git a/gns3server/handlers/api/compute/__init__.py b/gns3server/handlers/api/compute/__init__.py index ccd984e4..64614935 100644 --- a/gns3server/handlers/api/compute/__init__.py +++ b/gns3server/handlers/api/compute/__init__.py @@ -29,7 +29,7 @@ from .vmware_handler import VMwareHandler from .config_handler import ConfigHandler from .version_handler import VersionHandler from .notification_handler import NotificationHandler - +from .ethernet_hub_handler import EthernetHubHandler if sys.platform.startswith("linux") or hasattr(sys, "_called_from_test") or os.environ.get("PYTEST_BUILD_DOCUMENTATION") == "1": # IOU runs only on Linux but testsuite work on UNIX platform diff --git a/gns3server/handlers/api/compute/ethernet_hub_handler.py b/gns3server/handlers/api/compute/ethernet_hub_handler.py new file mode 100644 index 00000000..5caa23c0 --- /dev/null +++ b/gns3server/handlers/api/compute/ethernet_hub_handler.py @@ -0,0 +1,226 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2015 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 . + +import os + +from gns3server.web.route import Route +from gns3server.schemas.node import NODE_CAPTURE_SCHEMA +from gns3server.compute.builtin import Builtin + +from gns3server.schemas.ethernet_hub import ( + ETHERNET_HUB_CREATE_SCHEMA, + ETHERNET_HUB_UPDATE_SCHEMA, + ETHERNET_HUB_OBJECT_SCHEMA, + ETHERNET_HUB_NIO_SCHEMA +) + + +class EthernetHubHandler: + + """ + API entry points for Ethernet hub. + """ + + @Route.post( + r"/projects/{project_id}/ethernet_hub/nodes", + parameters={ + "project_id": "Project UUID" + }, + status_codes={ + 201: "Instance created", + 400: "Invalid request", + 409: "Conflict" + }, + description="Create a new Ethernet hub instance", + input=ETHERNET_HUB_CREATE_SCHEMA, + output=ETHERNET_HUB_OBJECT_SCHEMA) + def create(request, response): + + builtin_manager = Builtin.instance() + node = yield from builtin_manager.create_node(request.json.pop("name"), + request.match_info["project_id"], + request.json.get("node_id"), + node_type="ethernet_hub") + + response.set_status(201) + response.json(node) + + @Route.get( + r"/projects/{project_id}/ethernet_hub/nodes/{node_id}", + parameters={ + "project_id": "Project UUID", + "node_id": "Node UUID" + }, + status_codes={ + 200: "Success", + 400: "Invalid request", + 404: "Instance doesn't exist" + }, + description="Get an Ethernet hub instance", + output=ETHERNET_HUB_OBJECT_SCHEMA) + def show(request, response): + + builtin_manager = Builtin.instance() + node = builtin_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"]) + response.json(node) + + @Route.put( + r"/projects/{project_id}/ethernet_hub/nodes/{node_id}", + parameters={ + "project_id": "Project UUID", + "node_id": "Node UUID" + }, + status_codes={ + 200: "Instance updated", + 400: "Invalid request", + 404: "Instance doesn't exist", + 409: "Conflict" + }, + description="Update an Ethernet hub instance", + input=ETHERNET_HUB_UPDATE_SCHEMA, + output=ETHERNET_HUB_OBJECT_SCHEMA) + def update(request, response): + + builtin_manager = Builtin.instance() + node = builtin_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"]) + + # if "name" in request.json: + # yield from device.set_name(request.json["name"]) + # + # if "ports" in request.json: + # for port in request.json["ports"]: + # yield from device.set_port_settings(port["port"], port) + + response.json(node) + + @Route.delete( + r"/projects/{project_id}/ethernet_hub/nodes/{node_id}", + parameters={ + "project_id": "Project UUID", + "node_id": "Node UUID" + }, + status_codes={ + 204: "Instance deleted", + 400: "Invalid request", + 404: "Instance doesn't exist" + }, + description="Delete an Ethernet hub instance") + def delete(request, response): + + builtin_manager = Builtin.instance() + yield from builtin_manager.delete_node(request.match_info["node_id"]) + response.set_status(204) + + # FIXME: introduce adapter number (always 0)? + @Route.post( + r"/projects/{project_id}/ethernet_hub/nodes/{node_id}/ports/{port_number:\d+}/nio", + parameters={ + "project_id": "Project UUID", + "node_id": "Node UUID", + "port_number": "Port on the hub" + }, + status_codes={ + 201: "NIO created", + 400: "Invalid request", + 404: "Instance doesn't exist" + }, + description="Add a NIO to an Ethernet hub instance", + input=ETHERNET_HUB_NIO_SCHEMA) + def create_nio(request, response): + + builtin_manager = Builtin.instance() + node = builtin_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"]) + nio = yield from builtin_manager.create_nio(node, request.json["nio"]) + port_number = int(request.match_info["port_number"]) + # port_settings = request.json.get("port_settings") + # + # if asyncio.iscoroutinefunction(node.add_nio): + # yield from node.add_nio(nio, port_number) + # else: + # node.add_nio(nio, port_number) + # + # if port_settings: + # yield from node.set_port_settings(port_number, port_settings) + + response.set_status(201) + response.json(nio) + + @Route.delete( + r"/projects/{project_id}/ethernet_hub/nodes/{node_id}/ports/{port_number:\d+}/nio", + parameters={ + "project_id": "Project UUID", + "node_id": "Node UUID", + "port_number": "Port on the hub" + }, + status_codes={ + 204: "NIO deleted", + 400: "Invalid request", + 404: "Instance doesn't exist" + }, + description="Remove a NIO from an Ethernet hub instance") + def delete_nio(request, response): + + builtin_manager = Builtin.instance() + node = builtin_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"]) + port_number = int(request.match_info["port_number"]) + nio = yield from node.remove_nio(port_number) + yield from nio.delete() + response.set_status(204) + + @Route.post( + r"/projects/{project_id}/ethernet_hub/nodes/{node_id}/ports/{port_number:\d+}/start_capture", + parameters={ + "project_id": "Project UUID", + "node_id": "Node UUID", + "port_number": "Port on the hub" + }, + status_codes={ + 200: "Capture started", + 400: "Invalid request", + 404: "Instance doesn't exist" + }, + description="Start a packet capture on an Ethernet hub instance", + input=NODE_CAPTURE_SCHEMA) + def start_capture(request, response): + + builtin_manager = Builtin.instance() + node = builtin_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"]) + port_number = int(request.match_info["port_number"]) + pcap_file_path = os.path.join(node.project.capture_working_directory(), request.json["capture_file_name"]) + yield from node.start_capture(port_number, pcap_file_path, request.json["data_link_type"]) + response.json({"pcap_file_path": pcap_file_path}) + + @Route.post( + r"/projects/{project_id}/ethernet_hub/nodes/{node_id}/ports/{port_number:\d+}/stop_capture", + parameters={ + "project_id": "Project UUID", + "node_id": "Node UUID", + "port_number": "Port on the hub" + }, + status_codes={ + 204: "Capture stopped", + 400: "Invalid request", + 404: "Instance doesn't exist" + }, + description="Stop a packet capture on an Ethernet hub instance") + def stop_capture(request, response): + + builtin_manager = Builtin.instance() + node = builtin_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"]) + port_number = int(request.match_info["port_number"]) + yield from node.stop_capture(port_number) + response.set_status(204) diff --git a/gns3server/schemas/ethernet_hub.py b/gns3server/schemas/ethernet_hub.py new file mode 100644 index 00000000..ee8c93f3 --- /dev/null +++ b/gns3server/schemas/ethernet_hub.py @@ -0,0 +1,274 @@ +# -*- 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 . + + +ETHERNET_HUB_CREATE_SCHEMA = { + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "Request validation to create a new Ethernet hub instance", + "type": "object", + "properties": { + "name": { + "description": "Ethernet hub name", + "type": "string", + "minLength": 1, + }, + "node_id": { + "description": "Node UUID", + "oneOf": [ + {"type": "string", + "minLength": 36, + "maxLength": 36, + "pattern": "^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$"} + ] + } + }, + "additionalProperties": False, + "required": ["name"] +} + +ETHERNET_HUB_UPDATE_SCHEMA = { + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "Ethernet hub instance", + "type": "object", + "properties": { + "name": { + "description": "Ethernet hub name", + "type": "string", + "minLength": 1, + }, + "ports": { + "description": "Number of ports", + "type": "integer" + } + }, + "additionalProperties": False, +} + +ETHERNET_HUB_OBJECT_SCHEMA = { + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "Ethernet hub instance", + "type": "object", + "properties": { + "node_id": { + "description": "Node UUID", + "type": "string", + "minLength": 36, + "maxLength": 36, + "pattern": "^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$" + }, + "project_id": { + "description": "Project UUID", + "type": "string", + "minLength": 36, + "maxLength": 36, + "pattern": "^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$" + }, + "name": { + "description": "Ethernet hub name", + "type": "string", + "minLength": 1, + }, + "ports": { + "description": "Number of ports", + "type": "integer" + } + }, + "additionalProperties": False, + "required": ["name", "node_id", "project_id"] +} + +ETHERNET_HUB_NIO_SCHEMA = { + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "Request validation to add a NIO for an Ethernet hub 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 + }, + "NAT": { + "description": "NAT Network Input/Output", + "properties": { + "type": { + "enum": ["nio_nat"] + }, + }, + "required": ["type"], + "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": { + "nio": { + "type": "object", + "oneOf": [ + {"$ref": "#/definitions/UDP"}, + {"$ref": "#/definitions/Ethernet"}, + {"$ref": "#/definitions/LinuxEthernet"}, + {"$ref": "#/definitions/NAT"}, + {"$ref": "#/definitions/TAP"}, + {"$ref": "#/definitions/UNIX"}, + {"$ref": "#/definitions/VDE"}, + {"$ref": "#/definitions/NULL"}, + ] + }, + "port_settings": { + # only Ethernet switches have port settings + "type": "object", + "description": "Ethernet switch", + "properties": { + "type": { + "description": "Port type", + "enum": ["access", "dot1q", "qinq"], + }, + "vlan": {"description": "VLAN number", + "type": "integer", + "minimum": 1 + }, + "ethertype": { + "description": "QinQ Ethertype", + "enum": ["", "0x8100", "0x88A8", "0x9100", "0x9200"], + }, + }, + "required": ["type", "vlan"], + "additionalProperties": False + }, + "mappings": { + # only Frame-Relay and ATM switches have mappings + "type": "object", + } + }, + "additionalProperties": False, + "required": ["nio"] +} diff --git a/gns3server/schemas/node.py b/gns3server/schemas/node.py index e0fbf627..bd1f3fdf 100644 --- a/gns3server/schemas/node.py +++ b/gns3server/schemas/node.py @@ -88,7 +88,7 @@ NODE_OBJECT_SCHEMA = { }, "node_type": { "description": "Type of node", - "enum": ["docker", "dynamips", "vpcs", "virtualbox", "vmware", "iou", "qemu"] + "enum": ["ethernet_hub", "docker", "dynamips", "vpcs", "virtualbox", "vmware", "iou", "qemu"] }, "node_directory": { "description": "Working directory of the node. Read only",