From 5b839c22e94a3c17d31dac2290282e96a40f0188 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Mon, 17 Jul 2017 11:21:54 +0200 Subject: [PATCH] Filters support for IOU Fix #1113 --- gns3server/compute/base_node.py | 16 +++++--- gns3server/compute/iou/iou_vm.py | 40 +++++++++++++++++++ gns3server/controller/link.py | 6 ++- .../handlers/api/compute/iou_handler.py | 32 +++++++++++++++ tests/handlers/api/compute/test_iou.py | 18 +++++++++ 5 files changed, 105 insertions(+), 7 deletions(-) diff --git a/gns3server/compute/base_node.py b/gns3server/compute/base_node.py index 0021d3cb..1f3b42fb 100644 --- a/gns3server/compute/base_node.py +++ b/gns3server/compute/base_node.py @@ -597,25 +597,29 @@ class BaseNode: :param filters: Array of filter dictionnary """ yield from self._ubridge_send('bridge reset_packet_filters ' + bridge_name) + for filter in self._build_filter_list(filters): + cmd = 'bridge add_packet_filter {} {}'.format(bridge_name, filter) + yield from self._ubridge_send(cmd) + + def _build_filter_list(self, filters): + """ + :returns: Iterator building a list of filter + """ i = 0 for (filter_type, values) in filters.items(): if isinstance(values[0], str): for line in values[0].split('\n'): line = line.strip() - cmd = "bridge add_packet_filter {bridge_name} {filter_name} {filter_type} {filter_value}".format( - bridge_name=bridge_name, + yield "{filter_name} {filter_type} {filter_value}".format( filter_name="filter" + str(i), filter_type=filter_type, filter_value='"{}" {}'.format(line, " ".join([str(v) for v in values[1:]]))).strip() - yield from self._ubridge_send(cmd) i += 1 else: - cmd = "bridge add_packet_filter {bridge_name} {filter_name} {filter_type} {filter_value}".format( - bridge_name=bridge_name, + yield "{filter_name} {filter_type} {filter_value}".format( filter_name="filter" + str(i), filter_type=filter_type, filter_value=" ".join([str(v) for v in values])) - yield from self._ubridge_send(cmd) i += 1 @asyncio.coroutine diff --git a/gns3server/compute/iou/iou_vm.py b/gns3server/compute/iou/iou_vm.py index d1264ca3..1a1240b0 100644 --- a/gns3server/compute/iou/iou_vm.py +++ b/gns3server/compute/iou/iou_vm.py @@ -548,6 +548,7 @@ class IOUVM(BaseNode): output_file=nio.pcap_output_file, data_link_type=re.sub("^DLT_", "", nio.pcap_data_link_type))) + yield from self._ubridge_apply_filters(bay_id, unit_id, nio.filters) unit_id += 1 bay_id += 1 @@ -736,6 +737,10 @@ class IOUVM(BaseNode): log.warn("could not read {}: {}".format(self._iou_stdout_file, e)) return output + @property + def adapters(self): + return self._adapters + @property def ethernet_adapters(self): """ @@ -828,6 +833,41 @@ class IOUVM(BaseNode): lport=nio.lport, rhost=nio.rhost, rport=nio.rport)) + yield from self._ubridge_apply_filters(adapter_number, port_number, nio.filters) + + @asyncio.coroutine + def adapter_update_nio_binding(self, adapter_number, port_number, nio): + """ + Update a port NIO binding. + + :param adapter_number: adapter number + :param port_number: port number + :param nio: NIO instance to add to the adapter + """ + + if self.ubridge: + yield from self._ubridge_apply_filters(adapter_number, port_number, nio.filters) + + @asyncio.coroutine + def _ubridge_apply_filters(self, adapter_number, port_number, filters): + """ + Apply filter like rate limiting + + :param adapter_number: adapter number + :param port_number: port number + :param filters: Array of filter dictionnary + """ + bridge_name = "IOL-BRIDGE-{}".format(self.application_id + 512) + location = '{bridge_name} {bay} {unit}'.format( + bridge_name=bridge_name, + bay=adapter_number, + unit=port_number) + yield from self._ubridge_send('iol_bridge reset_packet_filters ' + location) + for filter in self._build_filter_list(filters): + cmd = 'iol_bridge add_packet_filter {} {}'.format( + location, + filter) + yield from self._ubridge_send(cmd) @asyncio.coroutine def adapter_remove_nio_binding(self, adapter_number, port_number): diff --git a/gns3server/controller/link.py b/gns3server/controller/link.py index 0053c984..3044ec8b 100644 --- a/gns3server/controller/link.py +++ b/gns3server/controller/link.py @@ -365,7 +365,11 @@ 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', + 'docker', + 'iou'): return node["node"] return None diff --git a/gns3server/handlers/api/compute/iou_handler.py b/gns3server/handlers/api/compute/iou_handler.py index e81ab11b..18761c25 100644 --- a/gns3server/handlers/api/compute/iou_handler.py +++ b/gns3server/handlers/api/compute/iou_handler.py @@ -225,6 +225,38 @@ class IOUHandler: response.set_status(201) response.json(nio) + @Route.put( + r"/projects/{project_id}/iou/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 where the nio should be added" + }, + status_codes={ + 201: "NIO updated", + 400: "Invalid request", + 404: "Instance doesn't exist" + }, + description="Update a NIO from a IOU instance", + input=NIO_SCHEMA, + output=NIO_SCHEMA) + def update_nio(request, response): + + iou_manager = IOU.instance() + vm = iou_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"]) + adapter_number = int(request.match_info["adapter_number"]) + port_number = int(request.match_info["port_number"]) + nio = vm.adapters[adapter_number].get_nio(port_number) + if "filters" in request.json and nio: + nio.filters = request.json["filters"] + yield from vm.adapter_update_nio_binding( + adapter_number, + port_number, + nio) + response.set_status(201) + response.json(nio) + @Route.delete( r"/projects/{project_id}/iou/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio", parameters={ diff --git a/tests/handlers/api/compute/test_iou.py b/tests/handlers/api/compute/test_iou.py index f31f367a..3f6f120d 100644 --- a/tests/handlers/api/compute/test_iou.py +++ b/tests/handlers/api/compute/test_iou.py @@ -205,6 +205,24 @@ def test_iou_nio_create_udp(http_compute, vm): assert response.json["type"] == "nio_udp" +def test_iou_nio_update_udp(http_compute, vm): + response = http_compute.post("/projects/{project_id}/iou/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}/iou/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}/iou/nodes/{node_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio" + assert response.json["type"] == "nio_udp" + + def test_iou_nio_create_ethernet(http_compute, vm, ethernet_device): response = http_compute.post("/projects/{project_id}/iou/nodes/{node_id}/adapters/1/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), {"type": "nio_ethernet", "ethernet_device": ethernet_device,