From 4cc5dbc2280832fe64a71f0b3f8d8495bf6dadda Mon Sep 17 00:00:00 2001 From: grossmj Date: Mon, 1 Apr 2019 19:47:31 +0700 Subject: [PATCH] Fix remote packet capture and make sure packet capture is stopped when deleting an NIO. Fixes https://github.com/GNS3/gns3-gui/issues/2753 --- gns3server/compute/builtin/nodes/cloud.py | 3 +++ gns3server/compute/docker/docker_vm.py | 3 +++ gns3server/compute/dynamips/nios/nio.py | 14 ++++++++++++++ gns3server/compute/dynamips/nodes/atm_switch.py | 3 +++ gns3server/compute/dynamips/nodes/ethernet_hub.py | 3 +++ .../compute/dynamips/nodes/ethernet_switch.py | 3 +++ .../compute/dynamips/nodes/frame_relay_switch.py | 3 +++ gns3server/compute/dynamips/nodes/router.py | 3 +++ gns3server/compute/iou/iou_vm.py | 2 ++ gns3server/compute/qemu/qemu_vm.py | 3 +++ gns3server/compute/traceng/traceng_vm.py | 3 +++ gns3server/compute/virtualbox/virtualbox_vm.py | 3 +++ gns3server/compute/vmware/vmware_vm.py | 3 +++ gns3server/compute/vpcs/vpcs_vm.py | 3 +++ gns3server/controller/link.py | 12 ++++++++++++ gns3server/schemas/link.py | 8 ++++++-- gns3server/version.py | 2 +- tests/controller/test_link.py | 3 ++- 18 files changed, 73 insertions(+), 4 deletions(-) diff --git a/gns3server/compute/builtin/nodes/cloud.py b/gns3server/compute/builtin/nodes/cloud.py index a9f7fe57..4ab5e742 100644 --- a/gns3server/compute/builtin/nodes/cloud.py +++ b/gns3server/compute/builtin/nodes/cloud.py @@ -441,6 +441,7 @@ class Cloud(BaseNode): if port_number not in self._nios: raise NodeError("Port {} is not allocated".format(port_number)) + await self.stop_capture(port_number) nio = self._nios[port_number] if isinstance(nio, NIOUDP): self.manager.port_manager.release_udp_port(nio.lport, self._project) @@ -504,6 +505,8 @@ class Cloud(BaseNode): """ nio = self.get_nio(port_number) + if not nio.capturing: + return nio.stopPacketCapture() bridge_name = "{}-{}".format(self._id, port_number) await self._ubridge_send("bridge stop_capture {name}".format(name=bridge_name)) diff --git a/gns3server/compute/docker/docker_vm.py b/gns3server/compute/docker/docker_vm.py index 47a0b34e..2db4b451 100644 --- a/gns3server/compute/docker/docker_vm.py +++ b/gns3server/compute/docker/docker_vm.py @@ -942,6 +942,7 @@ class DockerVM(BaseNode): raise DockerError("Adapter {adapter_number} doesn't exist on Docker VM '{name}'".format(name=self.name, adapter_number=adapter_number)) + await self.stop_capture(adapter_number) if self.ubridge: nio = adapter.get_nio(0) bridge_name = 'bridge{}'.format(adapter_number) @@ -1073,6 +1074,8 @@ class DockerVM(BaseNode): """ nio = self.get_nio(adapter_number) + if not nio.capturing: + return nio.stopPacketCapture() if self.status == "started" and self.ubridge: await self._stop_ubridge_capture(adapter_number) diff --git a/gns3server/compute/dynamips/nios/nio.py b/gns3server/compute/dynamips/nios/nio.py index dddf08ae..e705c117 100644 --- a/gns3server/compute/dynamips/nios/nio.py +++ b/gns3server/compute/dynamips/nios/nio.py @@ -41,6 +41,7 @@ class NIO: self._name = name self._filters = {} self._suspended = False + self._capturing = False self._bandwidth = None # no bandwidth constraint by default self._input_filter = None # no input filter applied by default self._output_filter = None # no output filter applied by default @@ -115,6 +116,9 @@ class NIO: self._input_filter = filter_name self._output_filter = filter_name + if filter_name == "capture": + self._capturing = True + async def unbind_filter(self, direction): """ Removes packet filter for this NIO. @@ -136,6 +140,7 @@ class NIO: elif direction == "both": self._input_filter = None self._output_filter = None + self._capturing = False async def setup_filter(self, direction, options): """ @@ -269,6 +274,15 @@ class NIO: assert isinstance(new_filters, dict) self._filters = new_filters + @property + def capturing(self): + """ + Returns either a capture is configured on this NIO. + :returns: boolean + """ + + return self._capturing + def __str__(self): """ NIO string representation. diff --git a/gns3server/compute/dynamips/nodes/atm_switch.py b/gns3server/compute/dynamips/nodes/atm_switch.py index 5d9ef849..9c90f7ea 100644 --- a/gns3server/compute/dynamips/nodes/atm_switch.py +++ b/gns3server/compute/dynamips/nodes/atm_switch.py @@ -181,6 +181,7 @@ class ATMSwitch(Device): if port_number not in self._nios: raise DynamipsError("Port {} is not allocated".format(port_number)) + await self.stop_capture(port_number) # remove VCs mapped with the port for source, destination in self._active_mappings.copy().items(): if len(source) == 3 and len(destination) == 3: @@ -466,6 +467,8 @@ class ATMSwitch(Device): """ nio = self.get_nio(port_number) + if not nio.capturing: + return await nio.unbind_filter("both") log.info('ATM switch "{name}" [{id}]: stopping packet capture on port {port}'.format(name=self._name, id=self._id, diff --git a/gns3server/compute/dynamips/nodes/ethernet_hub.py b/gns3server/compute/dynamips/nodes/ethernet_hub.py index 16e50b73..3b512185 100644 --- a/gns3server/compute/dynamips/nodes/ethernet_hub.py +++ b/gns3server/compute/dynamips/nodes/ethernet_hub.py @@ -162,6 +162,7 @@ class EthernetHub(Bridge): if port_number not in self._mappings: raise DynamipsError("Port {} is not allocated".format(port_number)) + await self.stop_capture(port_number) nio = self._mappings[port_number] if isinstance(nio, NIOUDP): self.manager.port_manager.release_udp_port(nio.lport, self._project) @@ -226,6 +227,8 @@ class EthernetHub(Bridge): """ nio = self.get_nio(port_number) + if not nio.capturing: + return await nio.unbind_filter("both") log.info('Ethernet hub "{name}" [{id}]: stopping packet capture on port {port}'.format(name=self._name, id=self._id, diff --git a/gns3server/compute/dynamips/nodes/ethernet_switch.py b/gns3server/compute/dynamips/nodes/ethernet_switch.py index cabf96a9..89f75e4e 100644 --- a/gns3server/compute/dynamips/nodes/ethernet_switch.py +++ b/gns3server/compute/dynamips/nodes/ethernet_switch.py @@ -283,6 +283,7 @@ class EthernetSwitch(Device): if port_number not in self._nios: raise DynamipsError("Port {} is not allocated".format(port_number)) + await self.stop_capture(port_number) nio = self._nios[port_number] if isinstance(nio, NIOUDP): self.manager.port_manager.release_udp_port(nio.lport, self._project) @@ -455,6 +456,8 @@ class EthernetSwitch(Device): """ nio = self.get_nio(port_number) + if not nio.capturing: + return await nio.unbind_filter("both") log.info('Ethernet switch "{name}" [{id}]: stopping packet capture on port {port}'.format(name=self._name, id=self._id, diff --git a/gns3server/compute/dynamips/nodes/frame_relay_switch.py b/gns3server/compute/dynamips/nodes/frame_relay_switch.py index 4a8af42b..e2441548 100644 --- a/gns3server/compute/dynamips/nodes/frame_relay_switch.py +++ b/gns3server/compute/dynamips/nodes/frame_relay_switch.py @@ -183,6 +183,7 @@ class FrameRelaySwitch(Device): if port_number not in self._nios: raise DynamipsError("Port {} is not allocated".format(port_number)) + await self.stop_capture(port_number) # remove VCs mapped with the port for source, destination in self._active_mappings.copy().items(): source_port, source_dlci = source @@ -352,6 +353,8 @@ class FrameRelaySwitch(Device): """ nio = self.get_nio(port_number) + if not nio.capturing: + return await nio.unbind_filter("both") log.info('Frame relay switch "{name}" [{id}]: stopping packet capture on port {port}'.format(name=self._name, id=self._id, diff --git a/gns3server/compute/dynamips/nodes/router.py b/gns3server/compute/dynamips/nodes/router.py index bb754d3c..d351e3f2 100644 --- a/gns3server/compute/dynamips/nodes/router.py +++ b/gns3server/compute/dynamips/nodes/router.py @@ -1324,6 +1324,7 @@ class Router(BaseNode): raise DynamipsError("Port {port_number} does not exist on adapter {adapter}".format(adapter=adapter, port_number=port_number)) + await self.stop_capture(slot_number, port_number) await self.slot_disable_nio(slot_number, port_number) await self._hypervisor.send('vm slot_remove_nio_binding "{name}" {slot_number} {port_number}'.format(name=self._name, slot_number=slot_number, @@ -1477,6 +1478,8 @@ class Router(BaseNode): raise DynamipsError("Port {slot_number}/{port_number} is not connected".format(slot_number=slot_number, port_number=port_number)) + if not nio.capturing: + return await nio.unbind_filter("both") log.info('Router "{name}" [{id}]: stopping packet capture on port {slot_number}/{port_number}'.format(name=self._name, diff --git a/gns3server/compute/iou/iou_vm.py b/gns3server/compute/iou/iou_vm.py index a572c47a..dfce1a8c 100644 --- a/gns3server/compute/iou/iou_vm.py +++ b/gns3server/compute/iou/iou_vm.py @@ -1315,6 +1315,8 @@ class IOUVM(BaseNode): """ nio = self.get_nio(adapter_number, port_number) + if not nio.capturing: + return nio.stopPacketCapture() log.info('IOU "{name}" [{id}]: stopping packet capture on {adapter_number}/{port_number}'.format(name=self._name, id=self._id, diff --git a/gns3server/compute/qemu/qemu_vm.py b/gns3server/compute/qemu/qemu_vm.py index a7f21ada..690ee5f8 100644 --- a/gns3server/compute/qemu/qemu_vm.py +++ b/gns3server/compute/qemu/qemu_vm.py @@ -1275,6 +1275,7 @@ class QemuVM(BaseNode): raise QemuError('Adapter {adapter_number} does not exist on QEMU VM "{name}"'.format(name=self._name, adapter_number=adapter_number)) + await self.stop_capture(adapter_number) if self.is_running(): await self._control_vm("set_link gns3-{} off".format(adapter_number)) await self._ubridge_send("bridge delete {name}".format(name="QEMU-{}-{}".format(self._id, adapter_number))) @@ -1342,6 +1343,8 @@ class QemuVM(BaseNode): """ nio = self.get_nio(adapter_number) + if not nio.capturing: + return nio.stopPacketCapture() if self.ubridge: diff --git a/gns3server/compute/traceng/traceng_vm.py b/gns3server/compute/traceng/traceng_vm.py index 22689063..e1a0b4c0 100644 --- a/gns3server/compute/traceng/traceng_vm.py +++ b/gns3server/compute/traceng/traceng_vm.py @@ -348,6 +348,7 @@ class TraceNGVM(BaseNode): raise TraceNGError("Port {port_number} doesn't exist in adapter {adapter}".format(adapter=self._ethernet_adapter, port_number=port_number)) + await self.stop_capture(port_number) if self.is_running(): await self._ubridge_send("bridge delete {name}".format(name="TraceNG-{}".format(self._id))) @@ -409,6 +410,8 @@ class TraceNGVM(BaseNode): """ nio = self.get_nio(port_number) + if not nio.capturing: + return nio.stopPacketCapture() if self.ubridge: diff --git a/gns3server/compute/virtualbox/virtualbox_vm.py b/gns3server/compute/virtualbox/virtualbox_vm.py index 9372de08..23218c35 100644 --- a/gns3server/compute/virtualbox/virtualbox_vm.py +++ b/gns3server/compute/virtualbox/virtualbox_vm.py @@ -1059,6 +1059,7 @@ class VirtualBoxVM(BaseNode): raise VirtualBoxError("Adapter {adapter_number} doesn't exist on VirtualBox VM '{name}'".format(name=self.name, adapter_number=adapter_number)) + await self.stop_capture(adapter_number) if self.is_running(): await self._ubridge_send("bridge delete {name}".format(name="VBOX-{}-{}".format(self._id, adapter_number))) vm_state = await self._get_vm_state() @@ -1134,6 +1135,8 @@ class VirtualBoxVM(BaseNode): """ nio = self.get_nio(adapter_number) + if not nio.capturing: + return nio.stopPacketCapture() if self.ubridge: diff --git a/gns3server/compute/vmware/vmware_vm.py b/gns3server/compute/vmware/vmware_vm.py index 0d74b510..f52400cf 100644 --- a/gns3server/compute/vmware/vmware_vm.py +++ b/gns3server/compute/vmware/vmware_vm.py @@ -779,6 +779,7 @@ class VMwareVM(BaseNode): raise VMwareError("Adapter {adapter_number} doesn't exist on VMware VM '{name}'".format(name=self.name, adapter_number=adapter_number)) + await self.stop_capture(adapter_number) nio = adapter.get_nio(0) if isinstance(nio, NIOUDP): self.manager.port_manager.release_udp_port(nio.lport, self._project) @@ -916,6 +917,8 @@ class VMwareVM(BaseNode): """ nio = self.get_nio(adapter_number) + if not nio.capturing: + return nio.stopPacketCapture() if self._started: diff --git a/gns3server/compute/vpcs/vpcs_vm.py b/gns3server/compute/vpcs/vpcs_vm.py index cd2ebac9..afc31e73 100644 --- a/gns3server/compute/vpcs/vpcs_vm.py +++ b/gns3server/compute/vpcs/vpcs_vm.py @@ -405,6 +405,7 @@ class VPCSVM(BaseNode): raise VPCSError("Port {port_number} doesn't exist on adapter {adapter}".format(adapter=self._ethernet_adapter, port_number=port_number)) + await self.stop_capture(port_number) if self.is_running(): await self._ubridge_send("bridge delete {name}".format(name="VPCS-{}".format(self._id))) @@ -466,6 +467,8 @@ class VPCSVM(BaseNode): """ nio = self.get_nio(port_number) + if not nio.capturing: + return nio.stopPacketCapture() if self.ubridge: diff --git a/gns3server/controller/link.py b/gns3server/controller/link.py index 1b2c27a9..60e19612 100644 --- a/gns3server/controller/link.py +++ b/gns3server/controller/link.py @@ -390,6 +390,17 @@ class Link: else: return None + @property + def capture_compute_id(self): + """ + Get the capture compute ID. + """ + + if self._capture_node: + return self.capture_node["node"].compute.id + else: + return None + def available_filters(self): """ Return the list of filters compatible with this link @@ -455,6 +466,7 @@ class Link: "capturing": self._capturing, "capture_file_name": self._capture_file_name, "capture_file_path": self.capture_file_path, + "capture_compute_id": self.capture_compute_id, "link_type": self._link_type, "filters": self._filters, "suspend": self._suspended diff --git a/gns3server/schemas/link.py b/gns3server/schemas/link.py index 05376888..6dd5ffe4 100644 --- a/gns3server/schemas/link.py +++ b/gns3server/schemas/link.py @@ -74,11 +74,15 @@ LINK_OBJECT_SCHEMA = { "type": "boolean" }, "capture_file_name": { - "description": "Read only property. The name of the capture file if capture is running", + "description": "Read only property. The name of the capture file if a capture is running", "type": ["string", "null"] }, "capture_file_path": { - "description": "Read only property. The full path of the capture file if capture is running", + "description": "Read only property. The full path of the capture file if a capture is running", + "type": ["string", "null"] + }, + "capture_compute_id": { + "description": "Read only property. The compute identifier where a capture is running", "type": ["string", "null"] }, "link_type": { diff --git a/gns3server/version.py b/gns3server/version.py index d1014810..1feb317c 100644 --- a/gns3server/version.py +++ b/gns3server/version.py @@ -23,7 +23,7 @@ # or negative for a release candidate or beta (after the base version # number has been incremented) -__version__ = "2.2.0dev8" +__version__ = "2.2.0dev9" __version_info__ = (2, 2, 0, 99) # If it's a git checkout try to add the commit diff --git a/tests/controller/test_link.py b/tests/controller/test_link.py index c64c4815..38797c2a 100644 --- a/tests/controller/test_link.py +++ b/tests/controller/test_link.py @@ -235,7 +235,8 @@ def test_json(async_run, project, compute, link): "link_type": "ethernet", "capturing": False, "capture_file_name": None, - "capture_file_path": None + "capture_file_path": None, + "capture_compute_id": None } assert link.__json__(topology_dump=True) == { "link_id": link.id,