diff --git a/gns3server/compute/qemu/qemu_vm.py b/gns3server/compute/qemu/qemu_vm.py index 117a340e..d782cbe7 100644 --- a/gns3server/compute/qemu/qemu_vm.py +++ b/gns3server/compute/qemu/qemu_vm.py @@ -454,6 +454,13 @@ class QemuVM(BaseNode): id=self._id, boot_priority=self._boot_priority)) + @property + def ethernet_adapters(self): + """ + Return the list of ethernet adapters of the node + """ + return self._ethernet_adapters + @property def adapters(self): """ @@ -1145,6 +1152,25 @@ class QemuVM(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.is_running(): + try: + yield from self.update_ubridge_udp_connection( + "QEMU-{}-{}".format(self._id, adapter_number), + self._local_udp_tunnels[adapter_number][1], + nio) + except IndexError: + raise QemuError('Adapter {adapter_number} does not exist on QEMU 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 2e1e2956..357c3867 100644 --- a/gns3server/controller/link.py +++ b/gns3server/controller/link.py @@ -365,7 +365,7 @@ class Link: :returns: None if no node support filtering else the node """ for node in self._nodes: - if node["node"].node_type in ('vpcs', 'dynamips'): + if node["node"].node_type in ('vpcs', 'dynamips', 'qemu'): return node["node"] return None diff --git a/gns3server/handlers/api/compute/qemu_handler.py b/gns3server/handlers/api/compute/qemu_handler.py index 6563c7de..95d78e92 100644 --- a/gns3server/handlers/api/compute/qemu_handler.py +++ b/gns3server/handlers/api/compute/qemu_handler.py @@ -271,6 +271,33 @@ class QEMUHandler: response.set_status(201) response.json(nio) + @Route.put( + r"/projects/{project_id}/qemu/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 Qemu instance") + def update_nio(request, response): + + qemu_manager = Qemu.instance() + vm = qemu_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["port_number"]), nio) + response.set_status(201) + response.json(request.json) + @Route.delete( r"/projects/{project_id}/qemu/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio", parameters={ @@ -316,7 +343,6 @@ class QEMUHandler: yield from vm.start_capture(adapter_number, pcap_file_path) response.json({"pcap_file_path": pcap_file_path}) - @Route.post( r"/projects/{project_id}/qemu/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/stop_capture", parameters={ diff --git a/tests/controller/test_link.py b/tests/controller/test_link.py index 42015b08..f90cd058 100644 --- a/tests/controller/test_link.py +++ b/tests/controller/test_link.py @@ -382,14 +382,14 @@ def test_update_filters(async_run, project, compute): def test_available_filters(async_run, project, compute): - node1 = Node(project, compute, "node1", node_type="qemu") + node1 = Node(project, compute, "node1", node_type="ethernet_switch") node1._ports = [EthernetPort("E0", 0, 0, 4)] link = Link(project) link.create = AsyncioMagicMock() assert link.available_filters() == [] - # Qemu is not supported should return 0 filters + # Ethernet switch is not supported should return 0 filters async_run(link.add_node(node1, 0, 4)) assert link.available_filters() == [] diff --git a/tests/handlers/api/compute/test_qemu.py b/tests/handlers/api/compute/test_qemu.py index 2a4348e8..229284a2 100644 --- a/tests/handlers/api/compute/test_qemu.py +++ b/tests/handlers/api/compute/test_qemu.py @@ -171,22 +171,41 @@ def test_qemu_nio_create_udp(http_compute, vm): with asyncio_patch("gns3server.compute.qemu.qemu_vm.QemuVM.add_ubridge_udp_connection"): http_compute.put("/projects/{project_id}/qemu/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"]), {"adapters": 2}) response = http_compute.post("/projects/{project_id}/qemu/nodes/{node_id}/adapters/1/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"}, - example=True) + "lport": 4242, + "rport": 4343, + "rhost": "127.0.0.1"}, + example=True) assert response.status == 201 assert response.route == "/projects/{project_id}/qemu/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio" assert response.json["type"] == "nio_udp" +def test_qemu_nio_update_udp(http_compute, vm): + http_compute.put("/projects/{project_id}/qemu/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"]), {"adapters": 2}) + http_compute.post("/projects/{project_id}/qemu/nodes/{node_id}/adapters/1/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"}) + response = http_compute.put("/projects/{project_id}/qemu/nodes/{node_id}/adapters/1/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}/qemu/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio" + assert response.json["type"] == "nio_udp" + + def test_qemu_delete_nio(http_compute, vm): with asyncio_patch("gns3server.compute.qemu.qemu_vm.QemuVM._ubridge_send"): http_compute.put("/projects/{project_id}/qemu/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"]), {"adapters": 2}) http_compute.post("/projects/{project_id}/qemu/nodes/{node_id}/adapters/1/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"}) + "lport": 4242, + "rport": 4343, + "rhost": "127.0.0.1"}) response = http_compute.delete("/projects/{project_id}/qemu/nodes/{node_id}/adapters/1/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), example=True) assert response.status == 204 assert response.route == "/projects/{project_id}/qemu/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio"