diff --git a/gns3server/compute/builtin/nodes/cloud.py b/gns3server/compute/builtin/nodes/cloud.py index ff6b1592..00db80e9 100644 --- a/gns3server/compute/builtin/nodes/cloud.py +++ b/gns3server/compute/builtin/nodes/cloud.py @@ -19,6 +19,9 @@ import asyncio from ...node_error import NodeError from ...base_node import BaseNode +from ...nios.nio_udp import NIOUDP + +from gns3server.utils.interfaces import interfaces import logging log = logging.getLogger(__name__) @@ -35,17 +38,52 @@ class Cloud(BaseNode): :param manager: Parent VM Manager """ - def __init__(self, name, node_id, project, manager): + def __init__(self, name, node_id, project, manager, ports=None): super().__init__(name, node_id, project, manager) + self._nios = {} + self._ports = [] + if ports: + self._ports = ports def __json__(self): + host_interfaces = [] + network_interfaces = interfaces() + for interface in network_interfaces: + interface_type = "ethernet" + if interface["name"].startswith("tap"): + # found no way to reliably detect a TAP interface + interface_type = "tap" + host_interfaces.append({"name": interface["name"], + "type": interface_type}) + return {"name": self.name, "node_id": self.id, - "project_id": self.project.id} + "project_id": self.project.id, + "ports": self._ports, + "interfaces": host_interfaces} + + @property + def ports(self): + """ + Ports on this cloud. + + :returns: ports info + """ + + return self._ports + + @ports.setter + def ports(self, ports): + """ + Set the ports on this cloud. + + :param ports: ports info + """ + + self._ports = ports - @asyncio.coroutine def create(self): """ Creates this cloud. @@ -54,13 +92,17 @@ class Cloud(BaseNode): super().create() log.info('Cloud "{name}" [{id}] has been created'.format(name=self._name, id=self._id)) - @asyncio.coroutine def delete(self): """ Deletes this cloud. """ - raise NotImplementedError() + for nio in self._nios.values(): + if nio and isinstance(nio, NIOUDP): + self.manager.port_manager.release_udp_port(nio.lport, self._project) + + super().delete() + log.info('Cloud "{name}" [{id}] has been deleted'.format(name=self._name, id=self._id)) @asyncio.coroutine def add_nio(self, nio, port_number): @@ -71,7 +113,19 @@ class Cloud(BaseNode): :param port_number: port to allocate for the NIO """ - raise NotImplementedError() + if port_number in self._nios: + raise NodeError("Port {} isn't free".format(port_number)) + + log.info('Cloud "{name}" [{id}]: NIO {nio} bound to port {port}'.format(name=self._name, + id=self._id, + nio=nio, + port=port_number)) + self._nios[port_number] = nio + for port_settings in self._ports: + if port_settings["port_number"] == port_number: + #yield from self.set_port_settings(port_number, port_settings) + break + @asyncio.coroutine def remove_nio(self, port_number): @@ -83,7 +137,20 @@ class Cloud(BaseNode): :returns: the NIO that was bound to the allocated port """ - raise NotImplementedError() + if port_number not in self._nios: + raise NodeError("Port {} is not allocated".format(port_number)) + + nio = self._nios[port_number] + if isinstance(nio, NIOUDP): + self.manager.port_manager.release_udp_port(nio.lport, self._project) + + log.info('Cloud "{name}" [{id}]: NIO {nio} removed from port {port}'.format(name=self._name, + id=self._id, + nio=nio, + port=port_number)) + + del self._nios[port_number] + return nio @asyncio.coroutine def start_capture(self, port_number, output_file, data_link_type="DLT_EN10MB"): diff --git a/gns3server/compute/vmware/vmware_vm.py b/gns3server/compute/vmware/vmware_vm.py index dc2feb60..3a947670 100644 --- a/gns3server/compute/vmware/vmware_vm.py +++ b/gns3server/compute/vmware/vmware_vm.py @@ -71,7 +71,7 @@ class VMwareVM(BaseNode): self._adapters = 0 self._ethernet_adapters = {} self._adapter_type = "e1000" - self._use_ubridge = True + self._use_ubridge = True # TODO: clean this (old ubridge code) self._use_any_adapter = False if not os.path.exists(vmx_path): @@ -89,7 +89,6 @@ class VMwareVM(BaseNode): "enable_remote_console": self.enable_remote_console, "adapters": self._adapters, "adapter_type": self.adapter_type, - "use_ubridge": self.use_ubridge, "use_any_adapter": self.use_any_adapter, "status": self.status, "node_directory": self.working_dir} @@ -740,30 +739,6 @@ class VMwareVM(BaseNode): id=self.id, adapter_type=adapter_type)) - @property - def use_ubridge(self): - """ - Returns either GNS3 can use uBridge for network connections. - - :returns: boolean - """ - - return self._use_ubridge - - @use_ubridge.setter - def use_ubridge(self, use_ubridge): - """ - Allows GNS3 to use uBridge for network connections. - - :param use_ubridge: boolean - """ - - if use_ubridge: - log.info("VMware VM '{name}' [{id}] will use uBridge for network connections".format(name=self.name, id=self.id)) - else: - log.info("VMware VM '{name}' [{id}] will not use uBridge for network connections".format(name=self.name, id=self.id)) - self._use_ubridge = use_ubridge - @property def use_any_adapter(self): """ diff --git a/gns3server/handlers/api/compute/cloud_handler.py b/gns3server/handlers/api/compute/cloud_handler.py index 552045d5..607f2720 100644 --- a/gns3server/handlers/api/compute/cloud_handler.py +++ b/gns3server/handlers/api/compute/cloud_handler.py @@ -51,7 +51,8 @@ class CloudHandler: node = yield from builtin_manager.create_node(request.json.pop("name"), request.match_info["project_id"], request.json.get("node_id"), - node_type="cloud") + node_type="cloud", + ports=request.json.get("ports")) response.set_status(201) response.json(node) @@ -93,6 +94,9 @@ class CloudHandler: builtin_manager = Builtin.instance() node = builtin_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"]) + for name, value in request.json.items(): + if hasattr(node, name) and getattr(node, name) != value: + setattr(node, name, value) node.updated() response.json(node) diff --git a/gns3server/schemas/cloud.py b/gns3server/schemas/cloud.py index 82199e8b..fc0dfe41 100644 --- a/gns3server/schemas/cloud.py +++ b/gns3server/schemas/cloud.py @@ -16,13 +16,14 @@ # along with this program. If not, see . + CLOUD_CREATE_SCHEMA = { "$schema": "http://json-schema.org/draft-04/schema#", "description": "Request validation to create a new cloud instance", "type": "object", "definitions": { - "EthernetHubPort": { - "description": "Ethernet port", + "EthernetInterfacePort": { + "description": "Ethernet interface port", "properties": { "name": { "description": "Port name", @@ -34,8 +35,96 @@ CLOUD_CREATE_SCHEMA = { "type": "integer", "minimum": 1 }, + "type": { + "description": "Port type", + "enum": ["ethernet"] + }, + "interface": { + "description": "Ethernet interface name e.g. eth0", + "type": "string", + "minLength": 1 + }, }, - "required": ["name", "port_number"], + "required": ["name", "port_number", "type", "interface"], + "additionalProperties": False + }, + "TAPInterfacePort": { + "description": "TAP interface port", + "properties": { + "name": { + "description": "Port name", + "type": "string", + "minLength": 1, + }, + "port_number": { + "description": "Port number", + "type": "integer", + "minimum": 1 + }, + "type": { + "description": "Port type", + "enum": ["tap"] + }, + "interface": { + "description": "TAP interface name e.g. tap0", + "type": "string", + "minLength": 1 + }, + }, + "required": ["name", "port_number", "type", "interface"], + "additionalProperties": False + }, + "UDPTunnelPort": { + "description": "UDP tunnel port", + "properties": { + "name": { + "description": "Port name", + "type": "string", + "minLength": 1, + }, + "port_number": { + "description": "Port number", + "type": "integer", + "minimum": 1 + }, + "type": { + "description": "Port type", + "enum": ["udp"] + }, + "lport": { + "description": "Local UDP tunnel port", + "type": "integer", + "minimum": 1, + "maximum": 65535 + }, + "rhost": { + "description": "Remote UDP tunnel host", + "type": "string", + "minLength": 1 + }, + "rport": { + "description": "Remote UDP tunnel port", + "type": "integer", + "minimum": 1, + "maximum": 65535 + } + }, + "required": ["name", "port_number", "type", "lport", "rhost", "rport"], + "additionalProperties": False + }, + "HostInterfaces": { + "description": "Interfaces on this host", + "properties": { + "name": { + "description": "Interface name", + "type": "string", + "minLength": 1, + }, + "type": { + "enum": ["Ethernet", "TAP"] + }, + }, + "required": ["name", "type"], "additionalProperties": False }, }, @@ -54,15 +143,26 @@ CLOUD_CREATE_SCHEMA = { "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}$"} ] }, - # "ports": { - # "type": "array", - # "items": [ - # {"type": "object", - # "oneOf": [ - # {"$ref": "#/definitions/EthernetHubPort"} - # ]}, - # ] - # }, + "ports": { + "type": "array", + "items": [ + {"type": "object", + "oneOf": [ + {"$ref": "#/definitions/EthernetInterfacePort"}, + {"$ref": "#/definitions/TAPInterfacePort"}, + {"$ref": "#/definitions/UDPTunnelPort"} + ]}, + ] + }, + "interfaces": { + "type": "array", + "items": [ + {"type": "object", + "oneOf": [ + {"$ref": "#/definitions/HostInterfaces"} + ]}, + ] + }, }, "additionalProperties": False, "required": ["name"] @@ -73,8 +173,8 @@ CLOUD_OBJECT_SCHEMA = { "description": "Cloud instance", "type": "object", "definitions": { - "EthernetHubPort": { - "description": "Ethernet port", + "EthernetInterfacePort": { + "description": "Ethernet interface port", "properties": { "name": { "description": "Port name", @@ -86,8 +186,96 @@ CLOUD_OBJECT_SCHEMA = { "type": "integer", "minimum": 1 }, + "type": { + "description": "Port type", + "enum": ["ethernet"] + }, + "interface": { + "description": "Ethernet interface name e.g. eth0", + "type": "string", + "minLength": 1 + }, }, - "required": ["name", "port_number"], + "required": ["name", "port_number", "type", "interface"], + "additionalProperties": False + }, + "TAPInterfacePort": { + "description": "TAP interface port", + "properties": { + "name": { + "description": "Port name", + "type": "string", + "minLength": 1, + }, + "port_number": { + "description": "Port number", + "type": "integer", + "minimum": 1 + }, + "type": { + "description": "Port type", + "enum": ["tap"] + }, + "interface": { + "description": "TAP interface name e.g. tap0", + "type": "string", + "minLength": 1 + }, + }, + "required": ["name", "port_number", "type", "interface"], + "additionalProperties": False + }, + "UDPTunnelPort": { + "description": "UDP tunnel port", + "properties": { + "name": { + "description": "Port name", + "type": "string", + "minLength": 1, + }, + "port_number": { + "description": "Port number", + "type": "integer", + "minimum": 1 + }, + "type": { + "description": "Port type", + "enum": ["udp"] + }, + "lport": { + "description": "Local UDP tunnel port", + "type": "integer", + "minimum": 1, + "maximum": 65535 + }, + "rhost": { + "description": "Remote UDP tunnel host", + "type": "string", + "minLength": 1 + }, + "rport": { + "description": "Remote UDP tunnel port", + "type": "integer", + "minimum": 1, + "maximum": 65535 + } + }, + "required": ["name", "port_number", "type", "lport", "rhost", "rport"], + "additionalProperties": False + }, + "HostInterfaces": { + "description": "Interfaces on this host", + "properties": { + "name": { + "description": "Interface name", + "type": "string", + "minLength": 1, + }, + "type": { + "enum": ["ethernet", "tap"] + }, + }, + "required": ["name", "type"], "additionalProperties": False }, }, @@ -111,22 +299,33 @@ CLOUD_OBJECT_SCHEMA = { "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}$" }, - # "ports": { - # "type": "array", - # "items": [ - # {"type": "object", - # "oneOf": [ - # {"$ref": "#/definitions/EthernetHubPort"} - # ]}, - # ] - # }, + "ports": { + "type": "array", + "items": [ + {"type": "object", + "oneOf": [ + {"$ref": "#/definitions/EthernetInterfacePort"}, + {"$ref": "#/definitions/TAPInterfacePort"}, + {"$ref": "#/definitions/UDPTunnelPort"} + ]}, + ] + }, + "interfaces": { + "type": "array", + "items": [ + {"type": "object", + "oneOf": [ + {"$ref": "#/definitions/HostInterfaces"} + ]}, + ] + }, "status": { "description": "Node status", "enum": ["started", "stopped", "suspended"] }, }, "additionalProperties": False, - "required": ["name", "node_id", "project_id"] #, "ports"] + "required": ["name", "node_id", "project_id", "ports"] } CLOUD_UPDATE_SCHEMA = CLOUD_OBJECT_SCHEMA diff --git a/gns3server/schemas/vmware.py b/gns3server/schemas/vmware.py index 1fdbde5c..4bbcc6d5 100644 --- a/gns3server/schemas/vmware.py +++ b/gns3server/schemas/vmware.py @@ -71,10 +71,6 @@ VMWARE_CREATE_SCHEMA = { "type": "string", "minLength": 1, }, - "use_ubridge": { - "description": "Use uBridge for network connections", - "type": "boolean", - }, "use_any_adapter": { "description": "Allow GNS3 to use any VMware adapter", "type": "boolean", @@ -128,10 +124,6 @@ VMWARE_UPDATE_SCHEMA = { "type": "string", "minLength": 1, }, - "use_ubridge": { - "description": "Use uBridge for network connections", - "type": "boolean", - }, "use_any_adapter": { "description": "Allow GNS3 to use any VMware adapter", "type": "boolean", @@ -201,10 +193,6 @@ VMWARE_OBJECT_SCHEMA = { "type": "string", "minLength": 1, }, - "use_ubridge": { - "description": "Use uBridge for network connections", - "type": "boolean", - }, "use_any_adapter": { "description": "Allow GNS3 to use any VMware adapter", "type": "boolean", diff --git a/gns3server/utils/asyncio/pool.py b/gns3server/utils/asyncio/pool.py index b3ea0ea9..cbfa3898 100644 --- a/gns3server/utils/asyncio/pool.py +++ b/gns3server/utils/asyncio/pool.py @@ -21,9 +21,9 @@ import asyncio class Pool(): """ - Limit concurrency for running parallel task + Limit concurrency for running parallel tasks """ - def __init__(self, concurrency=2): + def __init__(self, concurrency=5): self._tasks = [] self._concurrency = concurrency @@ -41,7 +41,6 @@ class Pool(): task, args, kwargs = self._tasks.pop(0) pending.add(task(*args, **kwargs)) (done, pending) = yield from asyncio.wait(pending, return_when=asyncio.FIRST_COMPLETED) - print(done) def main():