From 54faaf5f6f25c74978065e0e619ff68f834c0b22 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Tue, 18 Jul 2017 14:59:47 +0200 Subject: [PATCH] Packet filters support for VirtualBox Fix #1107 --- .../compute/virtualbox/virtualbox_vm.py | 39 ++++++++++++++++--- gns3server/controller/link.py | 1 + gns3server/controller/project.py | 4 +- .../handlers/api/compute/qemu_handler.py | 2 +- .../api/compute/virtualbox_handler.py | 27 +++++++++++++ tests/handlers/api/compute/test_virtualbox.py | 29 +++++++++++--- 6 files changed, 88 insertions(+), 14 deletions(-) diff --git a/gns3server/compute/virtualbox/virtualbox_vm.py b/gns3server/compute/virtualbox/virtualbox_vm.py index 5b114fa5..79dc0472 100644 --- a/gns3server/compute/virtualbox/virtualbox_vm.py +++ b/gns3server/compute/virtualbox/virtualbox_vm.py @@ -94,6 +94,10 @@ class VirtualBoxVM(BaseNode): json["node_directory"] = None return json + @property + def ethernet_adapters(self): + return self._ethernet_adapters + @asyncio.coroutine def _get_system_properties(self): @@ -973,15 +977,38 @@ class VirtualBoxVM(BaseNode): self._local_udp_tunnels[adapter_number][1], nio) except KeyError: - raise VirtualBoxError("Adapter {adapter_number} doesn't exist on VirtualBox VM '{name}'".format(name=self.name, - adapter_number=adapter_number)) + raise VirtualBoxError("Adapter {adapter_number} doesn't exist on VirtualBox VM '{name}'".format( + name=self.name, + adapter_number=adapter_number)) yield from self._control_vm("setlinkstate{} on".format(adapter_number + 1)) adapter.add_nio(0, nio) - log.info("VirtualBox VM '{name}' [{id}]: {nio} added to adapter {adapter_number}".format(name=self.name, - id=self.id, - nio=nio, - adapter_number=adapter_number)) + log.info("VirtualBox VM '{name}' [{id}]: {nio} added to adapter {adapter_number}".format( + name=self.name, + id=self.id, + 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.is_running(): + try: + yield from self.update_ubridge_udp_connection( + "VBOX-{}-{}".format(self._id, adapter_number), + self._local_udp_tunnels[adapter_number][1], + nio) + except IndexError: + raise VirtualBoxError('Adapter {adapter_number} does not exist on VirtualBox 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..fbf6e7e0 100644 --- a/gns3server/controller/link.py +++ b/gns3server/controller/link.py @@ -371,6 +371,7 @@ class Link: 'iou', 'cloud', 'nat', + 'virtualbox', 'docker'): return node["node"] return None diff --git a/gns3server/controller/project.py b/gns3server/controller/project.py index 3f396770..3e625c13 100644 --- a/gns3server/controller/project.py +++ b/gns3server/controller/project.py @@ -746,11 +746,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/qemu_handler.py b/gns3server/handlers/api/compute/qemu_handler.py index 95d78e92..69c7032a 100644 --- a/gns3server/handlers/api/compute/qemu_handler.py +++ b/gns3server/handlers/api/compute/qemu_handler.py @@ -294,7 +294,7 @@ class QEMUHandler: 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["port_number"]), nio) + yield from vm.adapter_update_nio_binding(int(request.match_info["adapter_number"]), nio) response.set_status(201) response.json(request.json) diff --git a/gns3server/handlers/api/compute/virtualbox_handler.py b/gns3server/handlers/api/compute/virtualbox_handler.py index 5e74592d..0d35ba6b 100644 --- a/gns3server/handlers/api/compute/virtualbox_handler.py +++ b/gns3server/handlers/api/compute/virtualbox_handler.py @@ -290,6 +290,33 @@ class VirtualBoxHandler: response.set_status(201) response.json(nio) + @Route.put( + r"/projects/{project_id}/virtualbox/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): + + virtualbox_manager = VirtualBox.instance() + vm = virtualbox_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}/virtualbox/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio", parameters={ diff --git a/tests/handlers/api/compute/test_virtualbox.py b/tests/handlers/api/compute/test_virtualbox.py index 8dcd0c77..49fbb462 100644 --- a/tests/handlers/api/compute/test_virtualbox.py +++ b/tests/handlers/api/compute/test_virtualbox.py @@ -26,9 +26,10 @@ def vm(http_compute, project, monkeypatch): vboxmanage_path = "/fake/VboxManage" with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.create", return_value=True) as mock: - response = http_compute.post("/projects/{project_id}/virtualbox/nodes".format(project_id=project.id), {"name": "VMTEST", - "vmname": "VMTEST", - "linked_clone": False}) + response = http_compute.post("/projects/{project_id}/virtualbox/nodes".format( + project_id=project.id), {"name": "VMTEST", + "vmname": "VMTEST", + "linked_clone": False}) assert mock.called assert response.status == 201 @@ -40,8 +41,8 @@ def test_vbox_create(http_compute, project): with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.create", return_value=True): response = http_compute.post("/projects/{project_id}/virtualbox/nodes".format(project_id=project.id), {"name": "VM1", - "vmname": "VM1", - "linked_clone": False}, + "vmname": "VM1", + "linked_clone": False}, example=True) assert response.status == 201 assert response.json["name"] == "VM1" @@ -111,6 +112,24 @@ def test_vbox_nio_create_udp(http_compute, vm): assert response.json["type"] == "nio_udp" +def test_virtualbox_nio_update_udp(http_compute, vm): + with asyncio_patch('gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.ethernet_adapters'): + with asyncio_patch('gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.adapter_remove_nio_binding') as mock: + response = http_compute.put("/projects/{project_id}/virtualbox/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}/virtualbox/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio" + assert response.json["type"] == "nio_udp" + + def test_vbox_delete_nio(http_compute, vm): with asyncio_patch('gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.adapter_remove_nio_binding') as mock: