diff --git a/gns3server/compute/vmware/vmware_vm.py b/gns3server/compute/vmware/vmware_vm.py index a1ed1fdb..7d253512 100644 --- a/gns3server/compute/vmware/vmware_vm.py +++ b/gns3server/compute/vmware/vmware_vm.py @@ -68,6 +68,10 @@ class VMwareVM(BaseNode): if not os.path.exists(vmx_path): raise VMwareError('VMware VM "{name}" [{id}]: could not find VMX file "{vmx_path}"'.format(name=name, id=node_id, vmx_path=vmx_path)) + @property + def ethernet_adapters(self): + return self._ethernet_adapters + def __json__(self): json = {"name": self.name, @@ -290,7 +294,7 @@ class VMwareVM(BaseNode): if allocate_vmnet: try: vmnet = self.manager.allocate_vmnet() - except: + except BaseException: # clear everything up in case of error (e.g. no enough vmnets) self._vmnets.clear() raise @@ -306,6 +310,15 @@ class VMwareVM(BaseNode): log.debug("disabling remaining adapter {}".format(adapter_number)) self._vmx_pairs["ethernet{}.startconnected".format(adapter_number)] = "FALSE" + def _get_vnet(self, adapter_number): + """ + Return the vnet will use in ubridge + """ + vnet = "ethernet{}.vnet".format(adapter_number) + if vnet not in self._vmx_pairs: + raise VMwareError("vnet {} not in VMX file".format(vnet)) + return vnet + @asyncio.coroutine def _add_ubridge_connection(self, nio, adapter_number): """ @@ -316,9 +329,7 @@ class VMwareVM(BaseNode): """ block_host_traffic = self.manager.config.get_section_config("VMware").getboolean("block_host_traffic", False) - vnet = "ethernet{}.vnet".format(adapter_number) - if vnet not in self._vmx_pairs: - raise VMwareError("vnet {} not in VMX file".format(vnet)) + vnet = self._get_vnet(adapter_number) yield from self._ubridge_send("bridge create {name}".format(name=vnet)) vmnet_interface = os.path.basename(self._vmx_pairs[vnet]) @@ -346,6 +357,21 @@ class VMwareVM(BaseNode): # source_mac = interface["mac_address"] # if source_mac: # yield from self._ubridge_send('bridge set_pcap_filter {name} "not ether src {mac}"'.format(name=vnet, mac=source_mac)) + yield from self._ubridge_apply_filters(vnet, nio.filters) + + @asyncio.coroutine + def _update_ubridge_connection(self, adapter_number, nio): + """ + Update a connection in uBridge. + + :param nio: NIO instance + :param adapter_number: adapter number + """ + try: + bridge_name = self._get_vnet(adapter_number) + except VMwareError: + return # vnet not yet available + yield from self._ubridge_apply_filters(bridge_name, nio.filters) @asyncio.coroutine def _delete_ubridge_connection(self, adapter_number): @@ -735,6 +761,24 @@ class VMwareVM(BaseNode): nio=nio, adapter_number=adapter_number)) + @asyncio.coroutine + def adapter_update_nio_binding(self, adapter_number, nio): + """ + Update a port NIO binding. + + :param adapter_number: adapter number + :param nio: NIO instance to add to the adapter + """ + + if self._ubridge_hypervisor: + try: + yield from self._update_ubridge_connection(adapter_number, nio) + except IndexError: + raise VMwareError('Adapter {adapter_number} does not exist on VMware VM "{name}"'.format( + name=self._name, + adapter_number=adapter_number + )) + @asyncio.coroutine def adapter_remove_nio_binding(self, adapter_number): """ diff --git a/gns3server/controller/link.py b/gns3server/controller/link.py index 502c2959..afa46b4f 100644 --- a/gns3server/controller/link.py +++ b/gns3server/controller/link.py @@ -366,6 +366,7 @@ class Link: """ for node in self._nodes: if node["node"].node_type in ('vpcs', + 'vmware', 'dynamips', 'qemu', 'iou', diff --git a/gns3server/controller/project.py b/gns3server/controller/project.py index 3f396770..cd1d4647 100644 --- a/gns3server/controller/project.py +++ b/gns3server/controller/project.py @@ -19,7 +19,6 @@ import re import os import json import uuid -import copy import shutil import asyncio import aiohttp @@ -746,11 +745,11 @@ class Project: yield from self.add_node(compute, name, node_id, dump=False, **node) for link_data in topology.get("links", []): link = yield from self.add_link(link_id=link_data["link_id"]) + if "filters" in link_data: + yield from link.update_filters(link_data["filters"]) for node_link in link_data["nodes"]: node = self.get_node(node_link["node_id"]) yield from link.add_node(node, node_link["adapter_number"], node_link["port_number"], label=node_link.get("label"), dump=False) - if "filters" in link_data: - yield from link.update_filters(link_data["filters"]) for drawing_data in topology.get("drawings", []): yield from self.add_drawing(dump=False, **drawing_data) diff --git a/gns3server/handlers/api/compute/vmware_handler.py b/gns3server/handlers/api/compute/vmware_handler.py index 97d49736..06d8a545 100644 --- a/gns3server/handlers/api/compute/vmware_handler.py +++ b/gns3server/handlers/api/compute/vmware_handler.py @@ -259,6 +259,33 @@ class VMwareHandler: response.set_status(201) response.json(nio) + @Route.put( + r"/projects/{project_id}/vmware/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio", + parameters={ + "project_id": "Project UUID", + "node_id": "Node UUID", + "adapter_number": "Network adapter where the nio is located", + "port_number": "Port from where the nio should be updated" + }, + status_codes={ + 201: "NIO updated", + 400: "Invalid request", + 404: "Instance doesn't exist" + }, + input=NIO_SCHEMA, + output=NIO_SCHEMA, + description="Update a NIO from a Virtualbox instance") + def update_nio(request, response): + + vmware_manager = VMware.instance() + vm = vmware_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"]) + nio = vm.ethernet_adapters[int(request.match_info["adapter_number"])] + if "filters" in request.json and nio: + nio.filters = request.json["filters"] + yield from vm.adapter_update_nio_binding(int(request.match_info["adapter_number"]), nio) + response.set_status(201) + response.json(request.json) + @Route.delete( r"/projects/{project_id}/vmware/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio", parameters={ diff --git a/tests/handlers/api/compute/test_vmware.py b/tests/handlers/api/compute/test_vmware.py index fc52090d..8462ea4b 100644 --- a/tests/handlers/api/compute/test_vmware.py +++ b/tests/handlers/api/compute/test_vmware.py @@ -120,6 +120,25 @@ def test_vmware_nio_create_udp(http_compute, vm): assert response.json["type"] == "nio_udp" +def test_vmware_nio_update_udp(http_compute, vm): + with asyncio_patch('gns3server.compute.vmware.vmware_vm.VMwareVM._ubridge_send'): + with asyncio_patch('gns3server.compute.vmware.vmware_vm.VMwareVM.ethernet_adapters'): + with patch('gns3server.compute.vmware.vmware_vm.VMwareVM._get_vnet') as mock: + response = http_compute.put("/projects/{project_id}/vmware/nodes/{node_id}/adapters/0/ports/0/nio".format( + project_id=vm["project_id"], + node_id=vm["node_id"]), + { + "type": "nio_udp", + "lport": 4242, + "rport": 4343, + "rhost": "127.0.0.1", + "filters": {}}, + example=True) + assert response.status == 201, response.body.decode() + assert response.route == "/projects/{project_id}/vmware/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio" + assert response.json["type"] == "nio_udp" + + def test_vmware_delete_nio(http_compute, vm): with asyncio_patch('gns3server.compute.vmware.vmware_vm.VMwareVM.adapter_remove_nio_binding') as mock: