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",