From a5ac7c54815b0d8782213d0d06515e3f0fab2544 Mon Sep 17 00:00:00 2001 From: Jeremy Date: Fri, 13 Feb 2015 15:11:14 -0700 Subject: [PATCH] Dynamips NIO connections. --- gns3server/handlers/dynamips_handler.py | 52 +++ gns3server/handlers/virtualbox_handler.py | 32 +- .../modules/adapters/ethernet_adapter.py | 2 +- gns3server/modules/adapters/serial_adapter.py | 2 +- gns3server/modules/dynamips/__init__.py | 44 +-- gns3server/modules/dynamips/nios/nio.py | 2 +- gns3server/modules/dynamips/nios/nio_fifo.py | 13 +- .../dynamips/nios/nio_generic_ethernet.py | 14 +- .../dynamips/nios/nio_linux_ethernet.py | 14 +- gns3server/modules/dynamips/nios/nio_mcast.py | 15 +- gns3server/modules/dynamips/nios/nio_null.py | 13 +- gns3server/modules/dynamips/nios/nio_tap.py | 14 +- gns3server/modules/dynamips/nios/nio_udp.py | 16 +- .../modules/dynamips/nios/nio_udp_auto.py | 17 +- gns3server/modules/dynamips/nios/nio_unix.py | 15 +- gns3server/modules/dynamips/nios/nio_vde.py | 35 +- gns3server/modules/dynamips/nodes/router.py | 338 +++++++++--------- gns3server/schemas/dynamips.py | 159 ++++++++ 18 files changed, 521 insertions(+), 276 deletions(-) diff --git a/gns3server/handlers/dynamips_handler.py b/gns3server/handlers/dynamips_handler.py index e3ed5d95..993bbd44 100644 --- a/gns3server/handlers/dynamips_handler.py +++ b/gns3server/handlers/dynamips_handler.py @@ -20,6 +20,7 @@ import asyncio from ..web.route import Route from ..schemas.dynamips import VM_CREATE_SCHEMA from ..schemas.dynamips import VM_UPDATE_SCHEMA +from ..schemas.dynamips import VM_NIO_SCHEMA from ..schemas.dynamips import VM_OBJECT_SCHEMA from ..modules.dynamips import Dynamips from ..modules.project_manager import ProjectManager @@ -238,3 +239,54 @@ class DynamipsHandler: vm = dynamips_manager.get_vm(request.match_info["vm_id"], project_id=request.match_info["project_id"]) yield from vm.reload() response.set_status(204) + + @Route.post( + r"/projects/{project_id}/dynamips/vms/{vm_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio", + parameters={ + "project_id": "UUID for the project", + "vm_id": "UUID for the instance", + "adapter_number": "Adapter where the nio should be added", + "port_number": "Port on the adapter" + }, + status_codes={ + 201: "NIO created", + 400: "Invalid request", + 404: "Instance doesn't exist" + }, + description="Add a NIO to a Dynamips VM instance", + input=VM_NIO_SCHEMA, + output=VM_NIO_SCHEMA) + def create_nio(request, response): + + dynamips_manager = Dynamips.instance() + vm = dynamips_manager.get_vm(request.match_info["vm_id"], project_id=request.match_info["project_id"]) + nio = yield from dynamips_manager.create_nio(vm, request.json) + slot_number = int(request.match_info["adapter_number"]) + port_number = int(request.match_info["port_number"]) + yield from vm.slot_add_nio_binding(slot_number, port_number, nio) + response.set_status(201) + response.json(nio) + + @classmethod + @Route.delete( + r"/projects/{project_id}/dynamips/vms/{vm_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio", + parameters={ + "project_id": "UUID for the project", + "vm_id": "UUID for the instance", + "adapter_number": "Adapter from where the nio should be removed", + "port_number": "Port on the adapter" + }, + status_codes={ + 204: "NIO deleted", + 400: "Invalid request", + 404: "Instance doesn't exist" + }, + description="Remove a NIO from a Dynamips VM instance") + def delete_nio(request, response): + + dynamips_manager = Dynamips.instance() + vm = dynamips_manager.get_vm(request.match_info["vm_id"], project_id=request.match_info["project_id"]) + slot_number = int(request.match_info["adapter_number"]) + port_number = int(request.match_info["port_number"]) + yield from vm.slot_remove_nio_binding(slot_number, port_number) + response.set_status(204) diff --git a/gns3server/handlers/virtualbox_handler.py b/gns3server/handlers/virtualbox_handler.py index 16361c1d..ae8d296e 100644 --- a/gns3server/handlers/virtualbox_handler.py +++ b/gns3server/handlers/virtualbox_handler.py @@ -252,12 +252,12 @@ class VirtualBoxHandler: response.set_status(204) @Route.post( - r"/projects/{project_id}/virtualbox/vms/{vm_id}/adapters/{adapter_id:\d+}/ports/{port_id:\d+}/nio", + r"/projects/{project_id}/virtualbox/vms/{vm_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio", parameters={ "project_id": "UUID for the project", "vm_id": "UUID for the instance", - "adapter_id": "Adapter where the nio should be added", - "port_id": "Port in the adapter (always 0 for virtualbox)" + "adapter_number": "Adapter where the nio should be added", + "port_number": "Port on the adapter (always 0)" }, status_codes={ 201: "NIO created", @@ -272,18 +272,18 @@ class VirtualBoxHandler: vbox_manager = VirtualBox.instance() vm = vbox_manager.get_vm(request.match_info["vm_id"], project_id=request.match_info["project_id"]) nio = vbox_manager.create_nio(vbox_manager.vboxmanage_path, request.json) - yield from vm.adapter_add_nio_binding(int(request.match_info["adapter_id"]), nio) + yield from vm.adapter_add_nio_binding(int(request.match_info["adapter_number"]), nio) response.set_status(201) response.json(nio) @classmethod @Route.delete( - r"/projects/{project_id}/virtualbox/vms/{vm_id}/adapters/{adapter_id:\d+}/ports/{port_id:\d+}/nio", + r"/projects/{project_id}/virtualbox/vms/{vm_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio", parameters={ "project_id": "UUID for the project", "vm_id": "UUID for the instance", - "adapter_id": "Adapter from where the nio should be removed", - "port_id": "Port in the adapter (always 0 for virtualbox)" + "adapter_number": "Adapter from where the nio should be removed", + "port_number": "Port on the adapter (always)" }, status_codes={ 204: "NIO deleted", @@ -295,15 +295,16 @@ class VirtualBoxHandler: vbox_manager = VirtualBox.instance() vm = vbox_manager.get_vm(request.match_info["vm_id"], project_id=request.match_info["project_id"]) - yield from vm.adapter_remove_nio_binding(int(request.match_info["adapter_id"])) + yield from vm.adapter_remove_nio_binding(int(request.match_info["adapter_number"])) response.set_status(204) @Route.post( - r"/projects/{project_id}/virtualbox/vms/{vm_id}/adapters/{adapter_id:\d+}/start_capture", + r"/projects/{project_id}/virtualbox/vms/{vm_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/start_capture", parameters={ "project_id": "UUID for the project", "vm_id": "UUID for the instance", - "adapter_id": "Adapter to start a packet capture" + "adapter_number": "Adapter to start a packet capture", + "port_number": "Port on the adapter (always 0)" }, status_codes={ 200: "Capture started", @@ -316,17 +317,18 @@ class VirtualBoxHandler: vbox_manager = VirtualBox.instance() vm = vbox_manager.get_vm(request.match_info["vm_id"], project_id=request.match_info["project_id"]) - adapter_id = int(request.match_info["adapter_id"]) + adapter_number = int(request.match_info["adapter_number"]) pcap_file_path = os.path.join(vm.project.capture_working_directory(), request.json["capture_file_name"]) - vm.start_capture(adapter_id, pcap_file_path) + vm.start_capture(adapter_number, pcap_file_path) response.json({"pcap_file_path": pcap_file_path}) @Route.post( - r"/projects/{project_id}/virtualbox/vms/{vm_id}/adapters/{adapter_id:\d+}/stop_capture", + r"/projects/{project_id}/virtualbox/vms/{vm_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/stop_capture", parameters={ "project_id": "UUID for the project", "vm_id": "UUID for the instance", - "adapter_id": "Adapter to stop a packet capture" + "adapter_number": "Adapter to stop a packet capture", + "port_number": "Port on the adapter (always 0)" }, status_codes={ 204: "Capture stopped", @@ -338,5 +340,5 @@ class VirtualBoxHandler: vbox_manager = VirtualBox.instance() vm = vbox_manager.get_vm(request.match_info["vm_id"], project_id=request.match_info["project_id"]) - vm.stop_capture(int(request.match_info["adapter_id"])) + vm.stop_capture(int(request.match_info["adapter_number"])) response.set_status(204) diff --git a/gns3server/modules/adapters/ethernet_adapter.py b/gns3server/modules/adapters/ethernet_adapter.py index 6a4981a0..d5b5373f 100644 --- a/gns3server/modules/adapters/ethernet_adapter.py +++ b/gns3server/modules/adapters/ethernet_adapter.py @@ -21,7 +21,7 @@ from .adapter import Adapter class EthernetAdapter(Adapter): """ - VPCS Ethernet adapter. + Ethernet adapter. """ def __init__(self, interfaces=1): diff --git a/gns3server/modules/adapters/serial_adapter.py b/gns3server/modules/adapters/serial_adapter.py index 8986f095..1ac39ce1 100644 --- a/gns3server/modules/adapters/serial_adapter.py +++ b/gns3server/modules/adapters/serial_adapter.py @@ -21,7 +21,7 @@ from .adapter import Adapter class SerialAdapter(Adapter): """ - VPCS Ethernet adapter. + Ethernet adapter. """ def __init__(self, interfaces=1): diff --git a/gns3server/modules/dynamips/__init__.py b/gns3server/modules/dynamips/__init__.py index 47dd96a5..07af76e4 100644 --- a/gns3server/modules/dynamips/__init__.py +++ b/gns3server/modules/dynamips/__init__.py @@ -170,35 +170,7 @@ class Dynamips(BaseManager): return hypervisor - def create_nio(self, executable, nio_settings): - """ - Creates a new NIO. - - :param nio_settings: information to create the NIO - - :returns: a NIO object - """ - - nio = None - if nio_settings["type"] == "nio_udp": - lport = nio_settings["lport"] - rhost = nio_settings["rhost"] - rport = nio_settings["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 aiohttp.web.HTTPInternalServerError(text="Could not create an UDP connection to {}:{}: {}".format(rhost, rport, e)) - nio = NIOUDP(lport, rhost, rport) - elif nio_settings["type"] == "nio_tap": - tap_device = nio_settings["tap_device"] - if not self._has_privileged_access(executable): - raise aiohttp.web.HTTPForbidden(text="{} has no privileged access to {}.".format(executable, tap_device)) - nio = NIOTAP(tap_device) - assert nio is not None - return nio - + @asyncio.coroutine def create_nio(self, node, nio_settings): """ Creates a new NIO. @@ -221,12 +193,12 @@ class Dynamips(BaseManager): except OSError as e: raise DynamipsError("Could not create an UDP connection to {}:{}: {}".format(rhost, rport, e)) # check if we have an allocated NIO UDP auto - nio = node.hypervisor.get_nio_udp_auto(lport) - if not nio: - # otherwise create an NIO UDP - nio = NIOUDP(node.hypervisor, lport, rhost, rport) - else: - nio.connect(rhost, rport) + #nio = node.hypervisor.get_nio_udp_auto(lport) + #if not nio: + # otherwise create an NIO UDP + nio = NIOUDP(node.hypervisor, lport, rhost, rport) + #else: + # nio.connect(rhost, rport) elif nio_settings["type"] == "nio_generic_ethernet": ethernet_device = nio_settings["ethernet_device"] if sys.platform.startswith("win"): @@ -259,6 +231,8 @@ class Dynamips(BaseManager): nio = NIOVDE(node.hypervisor, control_file, local_file) elif nio_settings["type"] == "nio_null": nio = NIONull(node.hypervisor) + + yield from nio.create() return nio # def set_ghost_ios(self, router): diff --git a/gns3server/modules/dynamips/nios/nio.py b/gns3server/modules/dynamips/nios/nio.py index f829e4ed..2f978f19 100644 --- a/gns3server/modules/dynamips/nios/nio.py +++ b/gns3server/modules/dynamips/nios/nio.py @@ -38,7 +38,7 @@ class NIO: def __init__(self, name, hypervisor): self._hypervisor = hypervisor - self._name = None + self._name = name self._bandwidth = None # no bandwidth constraint by default self._input_filter = None # no input filter applied by default self._output_filter = None # no output filter applied by default diff --git a/gns3server/modules/dynamips/nios/nio_fifo.py b/gns3server/modules/dynamips/nios/nio_fifo.py index 55b91b8d..fd10e40f 100644 --- a/gns3server/modules/dynamips/nios/nio_fifo.py +++ b/gns3server/modules/dynamips/nios/nio_fifo.py @@ -38,12 +38,11 @@ class NIOFIFO(NIO): def __init__(self, hypervisor): - NIO.__init__(self, hypervisor) - - # create an unique ID - self._id = NIOFIFO._instance_count + # create an unique ID and name + nio_id = NIOFIFO._instance_count NIOFIFO._instance_count += 1 - self._name = 'nio_fifo' + str(self._id) + name = 'nio_fifo' + str(nio_id) + NIO.__init__(name, self, hypervisor) @classmethod def reset(cls): @@ -71,3 +70,7 @@ class NIOFIFO(NIO): nio=nio)) log.info("NIO FIFO {name} crossconnected with {nio_name}.".format(name=self._name, nio_name=nio.name)) + + def __json__(self): + + return {"type": "nio_fifo"} diff --git a/gns3server/modules/dynamips/nios/nio_generic_ethernet.py b/gns3server/modules/dynamips/nios/nio_generic_ethernet.py index af631654..9690237a 100644 --- a/gns3server/modules/dynamips/nios/nio_generic_ethernet.py +++ b/gns3server/modules/dynamips/nios/nio_generic_ethernet.py @@ -39,13 +39,12 @@ class NIOGenericEthernet(NIO): def __init__(self, hypervisor, ethernet_device): - NIO.__init__(self, hypervisor) - - # create an unique ID - self._id = NIOGenericEthernet._instance_count + # create an unique ID and name + nio_id = NIOGenericEthernet._instance_count NIOGenericEthernet._instance_count += 1 - self._name = 'nio_gen_eth' + str(self._id) + name = 'nio_gen_eth' + str(nio_id) self._ethernet_device = ethernet_device + NIO.__init__(self, name, hypervisor) @classmethod def reset(cls): @@ -73,3 +72,8 @@ class NIOGenericEthernet(NIO): """ return self._ethernet_device + + def __json__(self): + + return {"type": "nio_generic_ethernet", + "ethernet_device": self._ethernet_device} diff --git a/gns3server/modules/dynamips/nios/nio_linux_ethernet.py b/gns3server/modules/dynamips/nios/nio_linux_ethernet.py index 1d3b280f..f121dbb6 100644 --- a/gns3server/modules/dynamips/nios/nio_linux_ethernet.py +++ b/gns3server/modules/dynamips/nios/nio_linux_ethernet.py @@ -39,13 +39,12 @@ class NIOLinuxEthernet(NIO): def __init__(self, hypervisor, ethernet_device): - NIO.__init__(self, hypervisor) - - # create an unique ID - self._id = NIOLinuxEthernet._instance_count + # create an unique ID and name + nio_id = NIOLinuxEthernet._instance_count NIOLinuxEthernet._instance_count += 1 - self._name = 'nio_linux_eth' + str(self._id) + name = 'nio_linux_eth' + str(nio_id) self._ethernet_device = ethernet_device + NIO.__init__(self, name, hypervisor) @classmethod def reset(cls): @@ -73,3 +72,8 @@ class NIOLinuxEthernet(NIO): """ return self._ethernet_device + + def __json__(self): + + return {"type": "nio_linux_ethernet", + "ethernet_device": self._ethernet_device} diff --git a/gns3server/modules/dynamips/nios/nio_mcast.py b/gns3server/modules/dynamips/nios/nio_mcast.py index ed6ea896..95a9f2ae 100644 --- a/gns3server/modules/dynamips/nios/nio_mcast.py +++ b/gns3server/modules/dynamips/nios/nio_mcast.py @@ -40,15 +40,14 @@ class NIOMcast(NIO): def __init__(self, hypervisor, group, port): - NIO.__init__(self, hypervisor) - - # create an unique ID - self._id = NIOMcast._instance_count + # create an unique ID and name + nio_id = NIOMcast._instance_count NIOMcast._instance_count += 1 - self._name = 'nio_mcast' + str(self._id) + name = 'nio_mcast' + str(nio_id) self._group = group self._port = port self._ttl = 1 # default TTL + NIO.__init__(self, name, hypervisor) @classmethod def reset(cls): @@ -109,3 +108,9 @@ class NIOMcast(NIO): yield from self._hypervisor.send("nio set_mcast_ttl {name} {ttl}".format(name=self._name, ttl=ttl)) self._ttl = ttl + + def __json__(self): + + return {"type": "nio_mcast", + "mgroup": self._mgroup, + "mport": self._mport} diff --git a/gns3server/modules/dynamips/nios/nio_null.py b/gns3server/modules/dynamips/nios/nio_null.py index b2c0e65f..e36fb0e5 100644 --- a/gns3server/modules/dynamips/nios/nio_null.py +++ b/gns3server/modules/dynamips/nios/nio_null.py @@ -38,12 +38,11 @@ class NIONull(NIO): def __init__(self, hypervisor): - NIO.__init__(self, hypervisor) - - # create an unique ID - self._id = NIONull._instance_count + # create an unique ID and name + nio_id = NIONull._instance_count NIONull._instance_count += 1 - self._name = 'nio_null' + str(self._id) + name = 'nio_null' + str(nio_id) + NIO.__init__(self, name, hypervisor) @classmethod def reset(cls): @@ -58,3 +57,7 @@ class NIONull(NIO): yield from self._hypervisor.send("nio create_null {}".format(self._name)) log.info("NIO NULL {name} created.".format(name=self._name)) + + def __json__(self): + + return {"type": "nio_null"} diff --git a/gns3server/modules/dynamips/nios/nio_tap.py b/gns3server/modules/dynamips/nios/nio_tap.py index e077161e..0e3b5683 100644 --- a/gns3server/modules/dynamips/nios/nio_tap.py +++ b/gns3server/modules/dynamips/nios/nio_tap.py @@ -39,13 +39,12 @@ class NIOTAP(NIO): def __init__(self, hypervisor, tap_device): - NIO.__init__(self, hypervisor) - - # create an unique ID - self._id = NIOTAP._instance_count + # create an unique ID and name + nio_id = NIOTAP._instance_count NIOTAP._instance_count += 1 - self._name = 'nio_tap' + str(self._id) + name = 'nio_tap' + str(nio_id) self._tap_device = tap_device + NIO.__init__(self, name, hypervisor) @classmethod def reset(cls): @@ -70,3 +69,8 @@ class NIOTAP(NIO): """ return self._tap_device + + def __json__(self): + + return {"type": "nio_tap", + "tap_device": self._tap_device} diff --git a/gns3server/modules/dynamips/nios/nio_udp.py b/gns3server/modules/dynamips/nios/nio_udp.py index f1b0ca18..c7016e6a 100644 --- a/gns3server/modules/dynamips/nios/nio_udp.py +++ b/gns3server/modules/dynamips/nios/nio_udp.py @@ -41,15 +41,14 @@ class NIOUDP(NIO): def __init__(self, hypervisor, lport, rhost, rport): - NIO.__init__(self, hypervisor) - - # create an unique ID - self._id = NIOUDP._instance_count + # create an unique ID and name + nio_id = NIOUDP._instance_count NIOUDP._instance_count += 1 - self._name = 'nio_udp' + str(self._id) + name = 'nio_udp' + str(nio_id) self._lport = lport self._rhost = rhost self._rport = rport + NIO.__init__(self, name, hypervisor) @classmethod def reset(cls): @@ -101,3 +100,10 @@ class NIOUDP(NIO): """ return self._rport + + def __json__(self): + + return {"type": "nio_udp", + "lport": self._lport, + "rport": self._rport, + "rhost": self._rhost} diff --git a/gns3server/modules/dynamips/nios/nio_udp_auto.py b/gns3server/modules/dynamips/nios/nio_udp_auto.py index 40eb6768..a7757199 100644 --- a/gns3server/modules/dynamips/nios/nio_udp_auto.py +++ b/gns3server/modules/dynamips/nios/nio_udp_auto.py @@ -41,17 +41,15 @@ class NIOUDPAuto(NIO): def __init__(self, hypervisor, laddr, lport_start, lport_end): - NIO.__init__(self, hypervisor) - - # create an unique ID - self._id = NIOUDPAuto._instance_count + # create an unique ID and name + nio_id = NIOUDPAuto._instance_count NIOUDPAuto._instance_count += 1 - self._name = 'nio_udp_auto' + str(self._id) - + name = 'nio_udp_auto' + str(nio_id) self._laddr = laddr self._lport = None self._raddr = None self._rport = None + NIO.__init__(self, name, hypervisor) @classmethod def reset(cls): @@ -133,3 +131,10 @@ class NIOUDPAuto(NIO): log.info("NIO UDP AUTO {name} connected to {raddr}:{rport}".format(name=self._name, raddr=raddr, rport=rport)) + + def __json__(self): + + return {"type": "nio_udp_auto", + "lport": self._lport, + "rport": self._rport, + "raddr": self._raddr} diff --git a/gns3server/modules/dynamips/nios/nio_unix.py b/gns3server/modules/dynamips/nios/nio_unix.py index d37c83ad..dddfaf82 100644 --- a/gns3server/modules/dynamips/nios/nio_unix.py +++ b/gns3server/modules/dynamips/nios/nio_unix.py @@ -40,14 +40,13 @@ class NIOUNIX(NIO): def __init__(self, hypervisor, local_file, remote_file): - NIO.__init__(self, hypervisor) - - # create an unique ID - self._id = NIOUNIX._instance_count + # create an unique ID and name + nio_id = NIOUNIX._instance_count NIOUNIX._instance_count += 1 - self._name = 'nio_unix' + str(self._id) + name = 'nio_unix' + str(nio_id) self._local_file = local_file self._remote_file = remote_file + NIO.__init__(self, name, hypervisor) @classmethod def reset(cls): @@ -87,3 +86,9 @@ class NIOUNIX(NIO): """ return self._remote_file + + def __json__(self): + + return {"type": "nio_unix", + "local_file": self._local_file, + "remote_file": self._remote_file} diff --git a/gns3server/modules/dynamips/nios/nio_vde.py b/gns3server/modules/dynamips/nios/nio_vde.py index 04b9fc07..c62ce2ca 100644 --- a/gns3server/modules/dynamips/nios/nio_vde.py +++ b/gns3server/modules/dynamips/nios/nio_vde.py @@ -19,6 +19,7 @@ Interface for VDE (Virtual Distributed Ethernet) NIOs (Unix based OSes only). """ +import asyncio from .nio import NIO import logging @@ -39,22 +40,13 @@ class NIOVDE(NIO): def __init__(self, hypervisor, control_file, local_file): - NIO.__init__(self, hypervisor) - - # create an unique ID - self._id = NIOVDE._instance_count + # create an unique ID and name + nio_id = NIOVDE._instance_count NIOVDE._instance_count += 1 - self._name = 'nio_vde' + str(self._id) + name = 'nio_vde' + str(nio_id) self._control_file = control_file self._local_file = local_file - - self._hypervisor.send("nio create_vde {name} {control} {local}".format(name=self._name, - control=control_file, - local=local_file)) - - log.info("NIO VDE {name} created with control={control}, local={local}".format(name=self._name, - control=control_file, - local=local_file)) + NIO.__init__(self, name, hypervisor) @classmethod def reset(cls): @@ -64,6 +56,17 @@ class NIOVDE(NIO): cls._instance_count = 0 + @asyncio.coroutine + def create(self): + + self._hypervisor.send("nio create_vde {name} {control} {local}".format(name=self._name, + control=self._control_file, + local=self._local_file)) + + log.info("NIO VDE {name} created with control={control}, local={local}".format(name=self._name, + control=self._control_file, + local=self._local_file)) + @property def control_file(self): """ @@ -83,3 +86,9 @@ class NIOVDE(NIO): """ return self._local_file + + def __json__(self): + + return {"type": "nio_vde", + "local_file": self._local_file, + "control_file": self._control_file} diff --git a/gns3server/modules/dynamips/nodes/router.py b/gns3server/modules/dynamips/nodes/router.py index bfff9d27..055cab13 100644 --- a/gns3server/modules/dynamips/nodes/router.py +++ b/gns3server/modules/dynamips/nodes/router.py @@ -145,16 +145,16 @@ class Router(BaseVM): "system_id": self._system_id} # FIXME: add default slots/wics - # slot_id = 0 + # slot_number = 0 # for slot in self._slots: # if slot: # slot = str(slot) - # router_defaults["slot" + str(slot_id)] = slot - # slot_id += 1 + # router_defaults["slot" + str(slot_number)] = slot + # slot_number += 1 # if self._slots[0] and self._slots[0].wics: - # for wic_slot_id in range(0, len(self._slots[0].wics)): - # router_defaults["wic" + str(wic_slot_id)] = None + # for wic_slot_number in range(0, len(self._slots[0].wics)): + # router_defaults["wic" + str(wic_slot_number)] = None return router_info @@ -991,23 +991,23 @@ class Router(BaseVM): return slot_bindings @asyncio.coroutine - def slot_add_binding(self, slot_id, adapter): + def slot_add_binding(self, slot_number, adapter): """ Adds a slot binding (a module into a slot). - :param slot_id: slot ID + :param slot_number: slot number :param adapter: device to add in the corresponding slot """ try: - slot = self._slots[slot_id] + slot = self._slots[slot_number] except IndexError: - raise DynamipsError('Slot {slot_id} does not exist on router "{name}"'.format(name=self._name, slot_id=slot_id)) + raise DynamipsError('Slot {slot_number} does not exist on router "{name}"'.format(name=self._name, slot_number=slot_number)) if slot is not None: current_adapter = slot - raise DynamipsError('Slot {slot_id} is already occupied by adapter {adapter} on router "{name}"'.format(name=self._name, - slot_id=slot_id, + raise DynamipsError('Slot {slot_number} is already occupied by adapter {adapter} on router "{name}"'.format(name=self._name, + slot_number=slot_number, adapter=current_adapter)) is_running = yield from self.is_running() @@ -1019,42 +1019,44 @@ class Router(BaseVM): raise DynamipsError('Adapter {adapter} cannot be added while router "{name}" is running'.format(adapter=adapter, name=self._name)) - yield from self._hypervisor.send('vm slot_add_binding "{name}" {slot_id} 0 {adapter}'.format(name=self._name, - slot_id=slot_id, - adapter=adapter)) + yield from self._hypervisor.send('vm slot_add_binding "{name}" {slot_number} 0 {adapter}'.format(name=self._name, + slot_number=slot_number, + adapter=adapter)) - log.info('Router "{name}" [{id}]: adapter {adapter} inserted into slot {slot_id}'.format(name=self._name, - id=self._id, - adapter=adapter, - slot_id=slot_id)) + log.info('Router "{name}" [{id}]: adapter {adapter} inserted into slot {slot_number}'.format(name=self._name, + id=self._id, + adapter=adapter, + slot_number=slot_number)) - self._slots[slot_id] = adapter + self._slots[slot_number] = adapter # Generate an OIR event if the router is running if is_running: - yield from self._hypervisor.send('vm slot_oir_start "{name}" {slot_id} 0'.format(name=self._name, slot_id=slot_id)) + yield from self._hypervisor.send('vm slot_oir_start "{name}" {slot_number} 0'.format(name=self._name, + slot_number=slot_number)) - log.info('Router "{name}" [{id}]: OIR start event sent to slot {slot_id}'.format(name=self._name, - id=self._id, - slot_id=slot_id)) + log.info('Router "{name}" [{id}]: OIR start event sent to slot {slot_number}'.format(name=self._name, + id=self._id, + slot_number=slot_number)) @asyncio.coroutine - def slot_remove_binding(self, slot_id): + def slot_remove_binding(self, slot_number): """ Removes a slot binding (a module from a slot). - :param slot_id: slot ID + :param slot_number: slot number """ try: - adapter = self._slots[slot_id] + adapter = self._slots[slot_number] except IndexError: - raise DynamipsError('Slot {slot_id} does not exist on router "{name}"'.format(name=self._name, slot_id=slot_id)) + raise DynamipsError('Slot {slot_number} does not exist on router "{name}"'.format(name=self._name, + slot_number=slot_number)) if adapter is None: - raise DynamipsError('No adapter in slot {slot_id} on router "{name}"'.format(name=self._name, - slot_id=slot_id)) + raise DynamipsError('No adapter in slot {slot_number} on router "{name}"'.format(name=self._name, + slot_number=slot_number)) is_running = yield from self.is_running() @@ -1068,239 +1070,245 @@ class Router(BaseVM): # Generate an OIR event if the router is running if is_running: - yield from self._hypervisor.send('vm slot_oir_stop "{name}" {slot_id} 0'.format(name=self._name, slot_id=slot_id)) + yield from self._hypervisor.send('vm slot_oir_stop "{name}" {slot_number} 0'.format(name=self._name, + slot_number=slot_number)) - log.info('Router "{name}" [{id}]: OIR stop event sent to slot {slot_id}'.format(name=self._name, + log.info('Router "{name}" [{id}]: OIR stop event sent to slot {slot_number}'.format(name=self._name, id=self._id, - slot_id=slot_id)) + slot_number=slot_number)) - yield from self._hypervisor.send('vm slot_remove_binding "{name}" {slot_id} 0'.format(name=self._name, slot_id=slot_id)) + yield from self._hypervisor.send('vm slot_remove_binding "{name}" {slot_number} 0'.format(name=self._name, + slot_number=slot_number)) - log.info('Router "{name}" [{id}]: adapter {adapter} removed from slot {slot_id}'.format(name=self._name, + log.info('Router "{name}" [{id}]: adapter {adapter} removed from slot {slot_number}'.format(name=self._name, id=self._id, adapter=adapter, - slot_id=slot_id)) - self._slots[slot_id] = None + slot_number=slot_number)) + self._slots[slot_number] = None @asyncio.coroutine - def install_wic(self, wic_slot_id, wic): + def install_wic(self, wic_slot_number, wic): """ Installs a WIC adapter into this router. - :param wic_slot_id: WIC slot ID + :param wic_slot_number: WIC slot number :param wic: WIC to be installed """ # WICs are always installed on adapters in slot 0 - slot_id = 0 + slot_number = 0 # Do not check if slot has an adapter because adapters with WICs interfaces # must be inserted by default in the router and cannot be removed. - adapter = self._slots[slot_id] + adapter = self._slots[slot_number] - if wic_slot_id > len(adapter.wics) - 1: - raise DynamipsError("WIC slot {wic_slot_id} doesn't exist".format(wic_slot_id=wic_slot_id)) + if wic_slot_number > len(adapter.wics) - 1: + raise DynamipsError("WIC slot {wic_slot_number} doesn't exist".format(wic_slot_number=wic_slot_number)) - if not adapter.wic_slot_available(wic_slot_id): - raise DynamipsError("WIC slot {wic_slot_id} is already occupied by another WIC".format(wic_slot_id=wic_slot_id)) + if not adapter.wic_slot_available(wic_slot_number): + raise DynamipsError("WIC slot {wic_slot_number} is already occupied by another WIC".format(wic_slot_number=wic_slot_number)) # Dynamips WICs slot IDs start on a multiple of 16 # WIC1 = 16, WIC2 = 32 and WIC3 = 48 - internal_wic_slot_id = 16 * (wic_slot_id + 1) - yield from self._hypervisor.send('vm slot_add_binding "{name}" {slot_id} {wic_slot_id} {wic}'.format(name=self._name, - slot_id=slot_id, - wic_slot_id=internal_wic_slot_id, + internal_wic_slot_number = 16 * (wic_slot_number + 1) + yield from self._hypervisor.send('vm slot_add_binding "{name}" {slot_number} {wic_slot_number} {wic}'.format(name=self._name, + slot_number=slot_number, + wic_slot_number=internal_wic_slot_number, wic=wic)) - log.info('Router "{name}" [{id}]: {wic} inserted into WIC slot {wic_slot_id}'.format(name=self._name, + log.info('Router "{name}" [{id}]: {wic} inserted into WIC slot {wic_slot_number}'.format(name=self._name, id=self._id, wic=wic, - wic_slot_id=wic_slot_id)) + wic_slot_number=wic_slot_number)) - adapter.install_wic(wic_slot_id, wic) + adapter.install_wic(wic_slot_number, wic) @asyncio.coroutine - def uninstall_wic(self, wic_slot_id): + def uninstall_wic(self, wic_slot_number): """ Uninstalls a WIC adapter from this router. - :param wic_slot_id: WIC slot ID + :param wic_slot_number: WIC slot number """ # WICs are always installed on adapters in slot 0 - slot_id = 0 + slot_number = 0 # Do not check if slot has an adapter because adapters with WICs interfaces # must be inserted by default in the router and cannot be removed. - adapter = self._slots[slot_id] + adapter = self._slots[slot_number] - if wic_slot_id > len(adapter.wics) - 1: - raise DynamipsError("WIC slot {wic_slot_id} doesn't exist".format(wic_slot_id=wic_slot_id)) + if wic_slot_number > len(adapter.wics) - 1: + raise DynamipsError("WIC slot {wic_slot_number} doesn't exist".format(wic_slot_number=wic_slot_number)) - if adapter.wic_slot_available(wic_slot_id): - raise DynamipsError("No WIC is installed in WIC slot {wic_slot_id}".format(wic_slot_id=wic_slot_id)) + if adapter.wic_slot_available(wic_slot_number): + raise DynamipsError("No WIC is installed in WIC slot {wic_slot_number}".format(wic_slot_number=wic_slot_number)) # Dynamips WICs slot IDs start on a multiple of 16 # WIC1 = 16, WIC2 = 32 and WIC3 = 48 - internal_wic_slot_id = 16 * (wic_slot_id + 1) - yield from self._hypervisor.send('vm slot_remove_binding "{name}" {slot_id} {wic_slot_id}'.format(name=self._name, - slot_id=slot_id, - wic_slot_id=internal_wic_slot_id)) + internal_wic_slot_number = 16 * (wic_slot_number + 1) + yield from self._hypervisor.send('vm slot_remove_binding "{name}" {slot_number} {wic_slot_number}'.format(name=self._name, + slot_number=slot_number, + wic_slot_number=internal_wic_slot_number)) - log.info('Router "{name}" [{id}]: {wic} removed from WIC slot {wic_slot_id}'.format(name=self._name, - id=self._id, - wic=adapter.wics[wic_slot_id], - wic_slot_id=wic_slot_id)) - adapter.uninstall_wic(wic_slot_id) + log.info('Router "{name}" [{id}]: {wic} removed from WIC slot {wic_slot_number}'.format(name=self._name, + id=self._id, + wic=adapter.wics[wic_slot_number], + wic_slot_number=wic_slot_number)) + adapter.uninstall_wic(wic_slot_number) @asyncio.coroutine - def get_slot_nio_bindings(self, slot_id): + def get_slot_nio_bindings(self, slot_number): """ Returns slot NIO bindings. - :param slot_id: slot ID + :param slot_number: slot number :returns: list of NIO bindings """ - nio_bindings = yield from self._hypervisor.send('vm slot_nio_bindings "{name}" {slot_id}'.format(name=self._name, - slot_id=slot_id)) + nio_bindings = yield from self._hypervisor.send('vm slot_nio_bindings "{name}" {slot_number}'.format(name=self._name, + slot_number=slot_number)) return nio_bindings @asyncio.coroutine - def slot_add_nio_binding(self, slot_id, port_id, nio): + def slot_add_nio_binding(self, slot_number, port_number, nio): """ Adds a slot NIO binding. - :param slot_id: slot ID - :param port_id: port ID + :param slot_number: slot number + :param port_number: port number :param nio: NIO instance to add to the slot/port """ try: - adapter = self._slots[slot_id] + adapter = self._slots[slot_number] except IndexError: - raise DynamipsError('Slot {slot_id} does not exist on router "{name}"'.format(name=self._name, - slot_id=slot_id)) - if not adapter.port_exists(port_id): - raise DynamipsError("Port {port_id} does not exist in adapter {adapter}".format(adapter=adapter, - port_id=port_id)) - - yield from self._hypervisor.send('vm slot_add_nio_binding "{name}" {slot_id} {port_id} {nio}'.format(name=self._name, - slot_id=slot_id, - port_id=port_id, - nio=nio)) - - log.info('Router "{name}" [{id}]: NIO {nio_name} bound to port {slot_id}/{port_id}'.format(name=self._name, - id=self._id, - nio_name=nio.name, - slot_id=slot_id, - port_id=port_id)) - - yield from self.slot_enable_nio(slot_id, port_id) - adapter.add_nio(port_id, nio) + raise DynamipsError('Slot {slot_number} does not exist on router "{name}"'.format(name=self._name, + slot_number=slot_number)) + if not adapter.port_exists(port_number): + raise DynamipsError("Port {port_number} does not exist in adapter {adapter}".format(adapter=adapter, + port_number=port_number)) + + yield from self._hypervisor.send('vm slot_add_nio_binding "{name}" {slot_number} {port_number} {nio}'.format(name=self._name, + slot_number=slot_number, + port_number=port_number, + nio=nio)) + + log.info('Router "{name}" [{id}]: NIO {nio_name} bound to port {slot_number}/{port_number}'.format(name=self._name, + id=self._id, + nio_name=nio.name, + slot_number=slot_number, + port_number=port_number)) + + yield from self.slot_enable_nio(slot_number, port_number) + adapter.add_nio(port_number, nio) @asyncio.coroutine - def slot_remove_nio_binding(self, slot_id, port_id): + def slot_remove_nio_binding(self, slot_number, port_number): """ Removes a slot NIO binding. - :param slot_id: slot ID - :param port_id: port ID + :param slot_number: slot number + :param port_number: port number :returns: removed NIO instance """ try: - adapter = self._slots[slot_id] + adapter = self._slots[slot_number] except IndexError: - raise DynamipsError('Slot {slot_id} does not exist on router "{name}"'.format(name=self._name, slot_id=slot_id)) - if not adapter.port_exists(port_id): - raise DynamipsError("Port {port_id} does not exist in adapter {adapter}".format(adapter=adapter, port_id=port_id)) - - yield from self.slot_disable_nio(slot_id, port_id) - yield from self._hypervisor.send('vm slot_remove_nio_binding "{name}" {slot_id} {port_id}'.format(name=self._name, - slot_id=slot_id, - port_id=port_id)) - - nio = adapter.get_nio(port_id) - adapter.remove_nio(port_id) - - log.info('Router "{name}" [{id}]: NIO {nio_name} removed from port {slot_id}/{port_id}'.format(name=self._name, - id=self._id, - nio_name=nio.name, - slot_id=slot_id, - port_id=port_id)) + raise DynamipsError('Slot {slot_number} does not exist on router "{name}"'.format(name=self._name, + slot_number=slot_number)) + if not adapter.port_exists(port_number): + raise DynamipsError("Port {port_number} does not exist in adapter {adapter}".format(adapter=adapter, + port_number=port_number)) + + yield from self.slot_disable_nio(slot_number, port_number) + yield from self._hypervisor.send('vm slot_remove_nio_binding "{name}" {slot_number} {port_number}'.format(name=self._name, + slot_number=slot_number, + port_number=port_number)) + + nio = adapter.get_nio(port_number) + adapter.remove_nio(port_number) + + log.info('Router "{name}" [{id}]: NIO {nio_name} removed from port {slot_number}/{port_number}'.format(name=self._name, + id=self._id, + nio_name=nio.name, + slot_number=slot_number, + port_number=port_number)) return nio @asyncio.coroutine - def slot_enable_nio(self, slot_id, port_id): + def slot_enable_nio(self, slot_number, port_number): """ Enables a slot NIO binding. - :param slot_id: slot ID - :param port_id: port ID + :param slot_number: slot number + :param port_number: port number """ is_running = yield from self.is_running() if is_running: # running router - yield from self._hypervisor.send('vm slot_enable_nio "{name}" {slot_id} {port_id}'.format(name=self._name, - slot_id=slot_id, - port_id=port_id)) + yield from self._hypervisor.send('vm slot_enable_nio "{name}" {slot_number} {port_number}'.format(name=self._name, + slot_number=slot_number, + port_number=port_number)) - log.info('Router "{name}" [{id}]: NIO enabled on port {slot_id}/{port_id}'.format(name=self._name, - id=self._id, - slot_id=slot_id, - port_id=port_id)) + log.info('Router "{name}" [{id}]: NIO enabled on port {slot_number}/{port_number}'.format(name=self._name, + id=self._id, + slot_number=slot_number, + port_number=port_number)) @asyncio.coroutine - def slot_disable_nio(self, slot_id, port_id): + def slot_disable_nio(self, slot_number, port_number): """ Disables a slot NIO binding. - :param slot_id: slot ID - :param port_id: port ID + :param slot_number: slot number + :param port_number: port number """ is_running = yield from self.is_running() if is_running: # running router - yield from self._hypervisor.send('vm slot_disable_nio "{name}" {slot_id} {port_id}'.format(name=self._name, - slot_id=slot_id, - port_id=port_id)) + yield from self._hypervisor.send('vm slot_disable_nio "{name}" {slot_number} {port_number}'.format(name=self._name, + slot_number=slot_number, + port_number=port_number)) - log.info('Router "{name}" [{id}]: NIO disabled on port {slot_id}/{port_id}'.format(name=self._name, - id=self._id, - slot_id=slot_id, - port_id=port_id)) + log.info('Router "{name}" [{id}]: NIO disabled on port {slot_number}/{port_number}'.format(name=self._name, + id=self._id, + slot_number=slot_number, + port_number=port_number)) @asyncio.coroutine - def start_capture(self, slot_id, port_id, output_file, data_link_type="DLT_EN10MB"): + def start_capture(self, slot_number, port_number, output_file, data_link_type="DLT_EN10MB"): """ Starts a packet capture. - :param slot_id: slot ID - :param port_id: port ID + :param slot_number: slot number + :param port_number: port number :param output_file: PCAP destination file for the capture :param data_link_type: PCAP data link type (DLT_*), default is DLT_EN10MB """ try: - adapter = self._slots[slot_id] + adapter = self._slots[slot_number] except IndexError: - raise DynamipsError('Slot {slot_id} does not exist on router "{name}"'.format(name=self._name, slot_id=slot_id)) - if not adapter.port_exists(port_id): - raise DynamipsError("Port {port_id} does not exist in adapter {adapter}".format(adapter=adapter, port_id=port_id)) + raise DynamipsError('Slot {slot_number} does not exist on router "{name}"'.format(name=self._name, + slot_number=slot_number)) + if not adapter.port_exists(port_number): + raise DynamipsError("Port {port_number} does not exist in adapter {adapter}".format(adapter=adapter, + port_number=port_number)) data_link_type = data_link_type.lower() if data_link_type.startswith("dlt_"): data_link_type = data_link_type[4:] - nio = adapter.get_nio(port_id) + nio = adapter.get_nio(port_number) if nio.input_filter[0] is not None and nio.output_filter[0] is not None: - raise DynamipsError("Port {port_id} has already a filter applied on {adapter}".format(adapter=adapter, - port_id=port_id)) + raise DynamipsError("Port {port_number} has already a filter applied on {adapter}".format(adapter=adapter, + port_number=port_number)) # FIXME: capture # try: @@ -1311,36 +1319,38 @@ class Router(BaseVM): yield from nio.bind_filter("both", "capture") yield from nio.setup_filter("both", '{} "{}"'.format(data_link_type, output_file)) - log.info('Router "{name}" [{id}]: starting packet capture on port {slot_id}/{port_id}'.format(name=self._name, - id=self._id, - nio_name=nio.name, - slot_id=slot_id, - port_id=port_id)) + log.info('Router "{name}" [{id}]: starting packet capture on port {slot_number}/{port_number}'.format(name=self._name, + id=self._id, + nio_name=nio.name, + slot_number=slot_number, + port_number=port_number)) @asyncio.coroutine - def stop_capture(self, slot_id, port_id): + def stop_capture(self, slot_number, port_number): """ Stops a packet capture. - :param slot_id: slot ID - :param port_id: port ID + :param slot_number: slot number + :param port_number: port number """ try: - adapter = self._slots[slot_id] + adapter = self._slots[slot_number] except IndexError: - raise DynamipsError('Slot {slot_id} does not exist on router "{name}"'.format(name=self._name, slot_id=slot_id)) - if not adapter.port_exists(port_id): - raise DynamipsError("Port {port_id} does not exist in adapter {adapter}".format(adapter=adapter, port_id=port_id)) + raise DynamipsError('Slot {slot_number} does not exist on router "{name}"'.format(name=self._name, + slot_number=slot_number)) + if not adapter.port_exists(port_number): + raise DynamipsError("Port {port_number} does not exist in adapter {adapter}".format(adapter=adapter, + port_number=port_number)) - nio = adapter.get_nio(port_id) + nio = adapter.get_nio(port_number) yield from nio.unbind_filter("both") - log.info('Router "{name}" [{id}]: stopping packet capture on port {slot_id}/{port_id}'.format(name=self._name, - id=self._id, - nio_name=nio.name, - slot_id=slot_id, - port_id=port_id)) + log.info('Router "{name}" [{id}]: stopping packet capture on port {slot_number}/{port_number}'.format(name=self._name, + id=self._id, + nio_name=nio.name, + slot_number=slot_number, + port_number=port_number)) def _create_slots(self, numslots): """ diff --git a/gns3server/schemas/dynamips.py b/gns3server/schemas/dynamips.py index d9fe845d..984b8ac6 100644 --- a/gns3server/schemas/dynamips.py +++ b/gns3server/schemas/dynamips.py @@ -46,6 +46,12 @@ VM_CREATE_SCHEMA = { "minLength": 1, "pattern": "^c[0-9]{4}$" }, + "chassis": { + "description": "router chassis model", + "type": "string", + "minLength": 1, + "pattern": "^[0-9]{4}(XM)?$" + }, "image": { "description": "path to the IOS image", "type": "string", @@ -265,6 +271,12 @@ VM_UPDATE_SCHEMA = { "minLength": 1, "pattern": "^c[0-9]{4}$" }, + "chassis": { + "description": "router chassis model", + "type": "string", + "minLength": 1, + "pattern": "^[0-9]{4}(XM)?$" + }, "image": { "description": "path to the IOS image", "type": "string", @@ -467,6 +479,147 @@ VM_UPDATE_SCHEMA = { "additionalProperties": False, } +VM_NIO_SCHEMA = { + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "Request validation to add a NIO for a Dynamips VM instance", + "type": "object", + "definitions": { + "UDP": { + "description": "UDP Network Input/Output", + "properties": { + "type": { + "enum": ["nio_udp"] + }, + "lport": { + "description": "Local port", + "type": "integer", + "minimum": 1, + "maximum": 65535 + }, + "rhost": { + "description": "Remote host", + "type": "string", + "minLength": 1 + }, + "rport": { + "description": "Remote port", + "type": "integer", + "minimum": 1, + "maximum": 65535 + } + }, + "required": ["type", "lport", "rhost", "rport"], + "additionalProperties": False + }, + "Ethernet": { + "description": "Generic Ethernet Network Input/Output", + "properties": { + "type": { + "enum": ["nio_generic_ethernet"] + }, + "ethernet_device": { + "description": "Ethernet device name e.g. eth0", + "type": "string", + "minLength": 1 + }, + }, + "required": ["type", "ethernet_device"], + "additionalProperties": False + }, + "LinuxEthernet": { + "description": "Linux Ethernet Network Input/Output", + "properties": { + "type": { + "enum": ["nio_linux_ethernet"] + }, + "ethernet_device": { + "description": "Ethernet device name e.g. eth0", + "type": "string", + "minLength": 1 + }, + }, + "required": ["type", "ethernet_device"], + "additionalProperties": False + }, + "TAP": { + "description": "TAP Network Input/Output", + "properties": { + "type": { + "enum": ["nio_tap"] + }, + "tap_device": { + "description": "TAP device name e.g. tap0", + "type": "string", + "minLength": 1 + }, + }, + "required": ["type", "tap_device"], + "additionalProperties": False + }, + "UNIX": { + "description": "UNIX Network Input/Output", + "properties": { + "type": { + "enum": ["nio_unix"] + }, + "local_file": { + "description": "path to the UNIX socket file (local)", + "type": "string", + "minLength": 1 + }, + "remote_file": { + "description": "path to the UNIX socket file (remote)", + "type": "string", + "minLength": 1 + }, + }, + "required": ["type", "local_file", "remote_file"], + "additionalProperties": False + }, + "VDE": { + "description": "VDE Network Input/Output", + "properties": { + "type": { + "enum": ["nio_vde"] + }, + "control_file": { + "description": "path to the VDE control file", + "type": "string", + "minLength": 1 + }, + "local_file": { + "description": "path to the VDE control file", + "type": "string", + "minLength": 1 + }, + }, + "required": ["type", "control_file", "local_file"], + "additionalProperties": False + }, + "NULL": { + "description": "NULL Network Input/Output", + "properties": { + "type": { + "enum": ["nio_null"] + }, + }, + "required": ["type"], + "additionalProperties": False + }, + }, + "oneOf": [ + {"$ref": "#/definitions/UDP"}, + {"$ref": "#/definitions/Ethernet"}, + {"$ref": "#/definitions/LinuxEthernet"}, + {"$ref": "#/definitions/TAP"}, + {"$ref": "#/definitions/UNIX"}, + {"$ref": "#/definitions/VDE"}, + {"$ref": "#/definitions/NULL"}, + ], + "additionalProperties": True, + "required": ["type"] +} + VM_OBJECT_SCHEMA = { "$schema": "http://json-schema.org/draft-04/schema#", "description": "Dynamips VM instance", @@ -501,6 +654,12 @@ VM_OBJECT_SCHEMA = { "minLength": 1, "pattern": "^c[0-9]{4}$" }, + "chassis": { + "description": "router chassis model", + "type": "string", + "minLength": 1, + "pattern": "^[0-9]{4}(XM)?$" + }, "image": { "description": "path to the IOS image", "type": "string",