From 536c708c16b49cbdf075a5a618600968a6da5cf6 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Mon, 17 Jul 2017 14:22:05 +0200 Subject: [PATCH] Filter implementation for cloud & Nat Fix #1111 --- gns3server/compute/builtin/nodes/cloud.py | 18 +++++++++++++ gns3server/compute/nios/nio_udp.py | 1 + gns3server/controller/link.py | 7 ++++- .../handlers/api/compute/cloud_handler.py | 27 +++++++++++++++++++ .../handlers/api/compute/nat_handler.py | 27 +++++++++++++++++++ tests/compute/builtin/nodes/test_cloud.py | 4 ++- .../compute/dynamips/test_ethernet_switch.py | 4 +-- tests/compute/test_base_node.py | 9 +++---- tests/handlers/api/compute/test_cloud.py | 18 +++++++++++++ tests/handlers/api/compute/test_nat.py | 18 +++++++++++++ 10 files changed, 124 insertions(+), 9 deletions(-) diff --git a/gns3server/compute/builtin/nodes/cloud.py b/gns3server/compute/builtin/nodes/cloud.py index 630905df..eeddbc08 100644 --- a/gns3server/compute/builtin/nodes/cloud.py +++ b/gns3server/compute/builtin/nodes/cloud.py @@ -64,6 +64,10 @@ class Cloud(BaseNode): port_number += 1 self._ports_mapping = ports + @property + def nios(self): + return self._nios + def _interfaces(self): return gns3server.utils.interfaces.interfaces() @@ -202,6 +206,7 @@ class Cloud(BaseNode): rhost=nio.rhost, rport=nio.rport)) + yield from self._ubridge_apply_filters(bridge_name, nio.filters) if port_info["type"] in ("ethernet", "tap"): if sys.platform.startswith("win"): @@ -311,6 +316,19 @@ class Cloud(BaseNode): self.status = "stopped" self._nios[port_number] = nio + @asyncio.coroutine + def update_nio(self, port_number, nio): + """ + Update an nio on this node + + :param nio: NIO instance to add + :param port_number: port to allocate for the NIO + """ + + bridge_name = "{}-{}".format(self._id, port_number) + if self._ubridge_hypervisor and self._ubridge_hypervisor.is_running(): + yield from self._ubridge_apply_filters(bridge_name, nio.filters) + @asyncio.coroutine def _delete_ubridge_connection(self, port_number): """ diff --git a/gns3server/compute/nios/nio_udp.py b/gns3server/compute/nios/nio_udp.py index 96811a94..36305312 100644 --- a/gns3server/compute/nios/nio_udp.py +++ b/gns3server/compute/nios/nio_udp.py @@ -38,6 +38,7 @@ class NIOUDP(NIO): self._lport = lport self._rhost = rhost self._rport = rport + assert isinstance(filters, dict) self._filters = filters @property diff --git a/gns3server/controller/link.py b/gns3server/controller/link.py index 0053c984..cc61c7f0 100644 --- a/gns3server/controller/link.py +++ b/gns3server/controller/link.py @@ -365,7 +365,12 @@ 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', 'qemu', 'docker'): + if node["node"].node_type in ('vpcs', + 'dynamips', + 'qemu', + 'cloud', + 'nat', + 'docker'): return node["node"] return None diff --git a/gns3server/handlers/api/compute/cloud_handler.py b/gns3server/handlers/api/compute/cloud_handler.py index 242fa534..bcba2c6f 100644 --- a/gns3server/handlers/api/compute/cloud_handler.py +++ b/gns3server/handlers/api/compute/cloud_handler.py @@ -199,6 +199,33 @@ class CloudHandler: response.set_status(201) response.json(nio) + @Route.put( + r"/projects/{project_id}/cloud/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 Cloud instance") + def update_nio(request, response): + + builtin_manager = Builtin.instance() + node = builtin_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"]) + nio = node.nios[int(request.match_info["adapter_number"])] + if "filters" in request.json and nio: + nio.filters = request.json["filters"] + yield from node.update_nio(int(request.match_info["port_number"]), nio) + response.set_status(201) + response.json(request.json) + @Route.delete( r"/projects/{project_id}/cloud/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio", parameters={ diff --git a/gns3server/handlers/api/compute/nat_handler.py b/gns3server/handlers/api/compute/nat_handler.py index 3e190ffa..90ab80ea 100644 --- a/gns3server/handlers/api/compute/nat_handler.py +++ b/gns3server/handlers/api/compute/nat_handler.py @@ -198,6 +198,33 @@ class NatHandler: response.set_status(201) response.json(nio) + @Route.put( + r"/projects/{project_id}/nat/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 NAT instance") + def update_nio(request, response): + + builtin_manager = Builtin.instance() + node = builtin_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"]) + nio = node.nios[int(request.match_info["adapter_number"])] + if "filters" in request.json and nio: + nio.filters = request.json["filters"] + yield from node.update_nio(int(request.match_info["port_number"]), nio) + response.set_status(201) + response.json(request.json) + @Route.delete( r"/projects/{project_id}/nat/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio", parameters={ diff --git a/tests/compute/builtin/nodes/test_cloud.py b/tests/compute/builtin/nodes/test_cloud.py index f8eb4a9a..228d6351 100644 --- a/tests/compute/builtin/nodes/test_cloud.py +++ b/tests/compute/builtin/nodes/test_cloud.py @@ -26,7 +26,7 @@ from tests.utils import asyncio_patch @pytest.fixture def nio(): - return NIOUDP(4242, "127.0.0.1", 4343, []) + return NIOUDP(4242, "127.0.0.1", 4343, {}) @pytest.fixture @@ -159,6 +159,7 @@ def test_linux_ethernet_raw_add_nio(linux_platform, project, async_run, nio): ubridge_mock.assert_has_calls([ call("bridge create {}-0".format(cloud._id)), call("bridge add_nio_udp {}-0 4242 127.0.0.1 4343".format(cloud._id)), + call('bridge reset_packet_filters {}-0'.format(cloud._id)), call("bridge add_nio_linux_raw {}-0 \"eth0\"".format(cloud._id)), call("bridge start {}-0".format(cloud._id)), ]) @@ -188,6 +189,7 @@ def test_linux_ethernet_raw_add_nio_bridge(linux_platform, project, async_run, n ubridge_mock.assert_has_calls([ call("bridge create {}-0".format(cloud._id)), call("bridge add_nio_udp {}-0 4242 127.0.0.1 4343".format(cloud._id)), + call('bridge reset_packet_filters {}-0'.format(cloud._id)), call("bridge add_nio_tap \"{}-0\" \"{}\"".format(cloud._id, tap)), call("brctl addif \"bridge0\" \"{}\"".format(tap)), call("bridge start {}-0".format(cloud._id)), diff --git a/tests/compute/dynamips/test_ethernet_switch.py b/tests/compute/dynamips/test_ethernet_switch.py index e316da7f..6089a634 100644 --- a/tests/compute/dynamips/test_ethernet_switch.py +++ b/tests/compute/dynamips/test_ethernet_switch.py @@ -24,9 +24,9 @@ def test_arp_command(async_run): node = AsyncioMagicMock() node.name = "Test" node.nios = {} - node.nios[0] = NIOUDP(55, "127.0.0.1", 56, []) + node.nios[0] = NIOUDP(55, "127.0.0.1", 56, {}) node.nios[0].name = "Ethernet0" - node.nios[1] = NIOUDP(55, "127.0.0.1", 56, []) + node.nios[1] = NIOUDP(55, "127.0.0.1", 56, {}) node.nios[1].name = "Ethernet1" node._hypervisor.send = AsyncioMagicMock(return_value=["0050.7966.6801 1 Ethernet0", "0050.7966.6802 1 Ethernet1"]) console = EthernetSwitchConsole(node) diff --git a/tests/compute/test_base_node.py b/tests/compute/test_base_node.py index 635c9966..e4825782 100644 --- a/tests/compute/test_base_node.py +++ b/tests/compute/test_base_node.py @@ -125,12 +125,11 @@ def test_change_aux_port(node, port_manager): def test_update_ubridge_udp_connection(node, async_run): - filters = [{ - "type": "latency", - "value": 10 - }] + filters = { + "latency": [10] + } - snio = NIOUDP(1245, "localhost", 1246, []) + snio = NIOUDP(1245, "localhost", 1246, {}) dnio = NIOUDP(1245, "localhost", 1244, filters) with asyncio_patch("gns3server.compute.base_node.BaseNode._ubridge_apply_filters") as mock: async_run(node.update_ubridge_udp_connection('VPCS-10', snio, dnio)) diff --git a/tests/handlers/api/compute/test_cloud.py b/tests/handlers/api/compute/test_cloud.py index 7bfad05c..737b3c6e 100644 --- a/tests/handlers/api/compute/test_cloud.py +++ b/tests/handlers/api/compute/test_cloud.py @@ -68,6 +68,24 @@ def test_cloud_nio_create_udp(http_compute, vm): assert response.json["type"] == "nio_udp" +def test_cloud_nio_update_udp(http_compute, vm): + http_compute.post("/projects/{project_id}/cloud/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"}) + response = http_compute.put("/projects/{project_id}/cloud/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}/cloud/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio" + assert response.json["type"] == "nio_udp" + + def test_cloud_delete_nio(http_compute, vm): http_compute.post("/projects/{project_id}/cloud/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), {"type": "nio_udp", "lport": 4242, diff --git a/tests/handlers/api/compute/test_nat.py b/tests/handlers/api/compute/test_nat.py index 75e7ffbf..07504410 100644 --- a/tests/handlers/api/compute/test_nat.py +++ b/tests/handlers/api/compute/test_nat.py @@ -69,6 +69,24 @@ def test_nat_nio_create_udp(http_compute, vm): assert response.json["type"] == "nio_udp" +def test_nat_nio_update_udp(http_compute, vm): + http_compute.post("/projects/{project_id}/nat/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"}) + response = http_compute.put("/projects/{project_id}/nat/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}/nat/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio" + assert response.json["type"] == "nio_udp" + + def test_nat_delete_nio(http_compute, vm): with asyncio_patch("gns3server.compute.builtin.nodes.nat.Nat.add_nio"): http_compute.post("/projects/{project_id}/nat/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), {"type": "nio_udp",