mirror of
https://github.com/GNS3/gns3-server.git
synced 2026-09-18 16:00:46 +03:00
marker: key _marker_filter_bridges by (name, link_id) so multi-link nodes toggle every copy
The _marker_filter_bridges dict was keyed by marker name alone, so when one node hosted the same filter name on several links (IUOL-BRIDGE per node with many bays/units, or a multi-interface router), successive apply calls overwrote earlier entries. pause_marker_definition then toggled only the last recorded bridge/location — other copies stayed active and kept emitting. Key by (name, link_id) so each copy is independent, and iterate all matching entries in _ubridge_set_marker_filter_state (both generic bridge and IOU iol_bridge override). Toggle route existence checks also iterate matching names. Tests updated.
This commit is contained in:
parent
34b644a548
commit
ff907da5f6
@ -269,7 +269,7 @@ async def toggle_cloud_marker(
|
|||||||
Toggle a marker filter on/off without an NIO rebuild (ubridge contract §3.2).
|
Toggle a marker filter on/off without an NIO rebuild (ubridge contract §3.2).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if marker_name not in node._marker_filter_bridges:
|
if not any(n == marker_name for (n, lid) in node._marker_filter_bridges):
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_404_NOT_FOUND,
|
status_code=status.HTTP_404_NOT_FOUND,
|
||||||
detail=f"Marker '{marker_name}' is not installed on this node",
|
detail=f"Marker '{marker_name}' is not installed on this node",
|
||||||
|
|||||||
@ -423,7 +423,7 @@ async def toggle_docker_marker(
|
|||||||
Toggle a marker filter on/off without an NIO rebuild (ubridge contract §3.2).
|
Toggle a marker filter on/off without an NIO rebuild (ubridge contract §3.2).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if marker_name not in node._marker_filter_bridges:
|
if not any(n == marker_name for (n, lid) in node._marker_filter_bridges):
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_404_NOT_FOUND,
|
status_code=status.HTTP_404_NOT_FOUND,
|
||||||
detail=f"Marker '{marker_name}' is not installed on this node",
|
detail=f"Marker '{marker_name}' is not installed on this node",
|
||||||
|
|||||||
@ -382,7 +382,7 @@ async def toggle_dynamips_marker(
|
|||||||
Toggle a marker filter on/off without an NIO rebuild (ubridge contract §3.2).
|
Toggle a marker filter on/off without an NIO rebuild (ubridge contract §3.2).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if marker_name not in node._marker_filter_bridges:
|
if not any(n == marker_name for (n, lid) in node._marker_filter_bridges):
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_404_NOT_FOUND,
|
status_code=status.HTTP_404_NOT_FOUND,
|
||||||
detail=f"Marker '{marker_name}' is not installed on this node",
|
detail=f"Marker '{marker_name}' is not installed on this node",
|
||||||
|
|||||||
@ -361,7 +361,7 @@ async def toggle_iou_marker(
|
|||||||
Toggle a marker filter on/off without an NIO rebuild (ubridge contract §3.2).
|
Toggle a marker filter on/off without an NIO rebuild (ubridge contract §3.2).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if marker_name not in node._marker_filter_bridges:
|
if not any(n == marker_name for (n, lid) in node._marker_filter_bridges):
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_404_NOT_FOUND,
|
status_code=status.HTTP_404_NOT_FOUND,
|
||||||
detail=f"Marker '{marker_name}' is not installed on this node",
|
detail=f"Marker '{marker_name}' is not installed on this node",
|
||||||
|
|||||||
@ -453,7 +453,7 @@ async def toggle_qemu_marker(
|
|||||||
Toggle a marker filter on/off without an NIO rebuild (ubridge contract §3.2).
|
Toggle a marker filter on/off without an NIO rebuild (ubridge contract §3.2).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if marker_name not in node._marker_filter_bridges:
|
if not any(n == marker_name for (n, lid) in node._marker_filter_bridges):
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_404_NOT_FOUND,
|
status_code=status.HTTP_404_NOT_FOUND,
|
||||||
detail=f"Marker '{marker_name}' is not installed on this node",
|
detail=f"Marker '{marker_name}' is not installed on this node",
|
||||||
|
|||||||
@ -360,7 +360,7 @@ async def toggle_vpcs_marker(
|
|||||||
Toggle a marker filter on/off without an NIO rebuild (ubridge contract §3.2).
|
Toggle a marker filter on/off without an NIO rebuild (ubridge contract §3.2).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if marker_name not in node._marker_filter_bridges:
|
if not any(n == marker_name for (n, lid) in node._marker_filter_bridges):
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_404_NOT_FOUND,
|
status_code=status.HTTP_404_NOT_FOUND,
|
||||||
detail=f"Marker '{marker_name}' is not installed on this node",
|
detail=f"Marker '{marker_name}' is not installed on this node",
|
||||||
|
|||||||
@ -1181,29 +1181,29 @@ class BaseNode:
|
|||||||
)
|
)
|
||||||
# Remember which bridge hosts this filter so an instant on/off toggle
|
# Remember which bridge hosts this filter so an instant on/off toggle
|
||||||
# (no NIO rebuild) can resolve it by name alone.
|
# (no NIO rebuild) can resolve it by name alone.
|
||||||
self._marker_filter_bridges[name] = bridge_name
|
# keyed (name, link_id) so a node that hosts markers for several links
|
||||||
|
# (e.g. IOU with one IOL-BRIDGE and many bays/units) records each
|
||||||
|
# copy independently — toggle below iterates all matching entries.
|
||||||
|
self._marker_filter_bridges[name, link_id] = bridge_name
|
||||||
|
|
||||||
async def _ubridge_set_marker_filter_state(self, name, enabled):
|
async def _ubridge_set_marker_filter_state(self, name, enabled):
|
||||||
"""
|
"""
|
||||||
Toggle an installed marker filter on/off with a single uBridge command
|
Toggle an installed marker filter on/off with a single uBridge command
|
||||||
(``bridge enable_packet_filter … on|off``) — no NIO reset/reapply, so the
|
(``bridge enable_packet_filter … on|off``) — no NIO reset/reapply, so the
|
||||||
pcap identity and emitted counter are preserved (ubridge contract §3.2).
|
pcap identity and emitted counter are preserved (ubridge contract §3.2).
|
||||||
The bridge is resolved from the name→bridge map populated at apply time;
|
The bridge is resolved from the (name, link_id)→bridge map populated at
|
||||||
IOU overrides this for its ``iol_bridge`` command shape.
|
apply time; entries are iterated so a node that hosts the same marker name
|
||||||
|
on several links (e.g. IOU with one IOL-BRIDGE per node) toggles every
|
||||||
|
copy. IOU overrides this for its ``iol_bridge`` command shape.
|
||||||
|
|
||||||
:param name: marker filter name
|
:param name: marker filter name
|
||||||
:param enabled: True = on (signal+pcap), False = off (paused tap)
|
:param enabled: True = on (signal+pcap), False = off (paused tap)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
bridge_name = self._marker_filter_bridges.get(name)
|
|
||||||
if not bridge_name:
|
|
||||||
# Marker not installed on this uBridge (node not started, or not yet
|
|
||||||
# applied). The controller-layer `enabled` is still authoritative and
|
|
||||||
# is honoured when the node starts and applies the marker, so a
|
|
||||||
# toggle here is a no-op rather than an error.
|
|
||||||
return
|
|
||||||
state = "on" if enabled else "off"
|
state = "on" if enabled else "off"
|
||||||
await self._ubridge_send(f"bridge enable_packet_filter {bridge_name} {name} {state}")
|
for (n, lid), bridge_name in list(self._marker_filter_bridges.items()):
|
||||||
|
if n == name:
|
||||||
|
await self._ubridge_send(f"bridge enable_packet_filter {bridge_name} {name} {state}")
|
||||||
|
|
||||||
async def _ubridge_marker_pause(self):
|
async def _ubridge_marker_pause(self):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@ -1330,18 +1330,15 @@ class IOUVM(BaseNode):
|
|||||||
str(self.project.id), self._id, name, link_id, tag
|
str(self.project.id), self._id, name, link_id, tag
|
||||||
)
|
)
|
||||||
# Record name -> location (bridge bay unit) for instant toggle.
|
# Record name -> location (bridge bay unit) for instant toggle.
|
||||||
self._marker_filter_bridges[name] = location
|
self._marker_filter_bridges[name, link_id] = location
|
||||||
|
|
||||||
async def _ubridge_set_marker_filter_state(self, name, enabled):
|
async def _ubridge_set_marker_filter_state(self, name, enabled):
|
||||||
"""IOU override: toggle via ``iol_bridge enable_packet_filter <bridge> <bay> <unit> <name> on|off``."""
|
"""IOU override: toggle every (name, link_id) entry via ``iol_bridge``."""
|
||||||
|
|
||||||
location = self._marker_filter_bridges.get(name)
|
|
||||||
if not location:
|
|
||||||
# Marker not installed on this uBridge (node not started, or not yet
|
|
||||||
# applied); controller-layer `enabled` is authoritative. No-op.
|
|
||||||
return
|
|
||||||
state = "on" if enabled else "off"
|
state = "on" if enabled else "off"
|
||||||
await self._ubridge_send(f"iol_bridge enable_packet_filter {location} {name} {state}")
|
for (n, lid), location in list(self._marker_filter_bridges.items()):
|
||||||
|
if n == name:
|
||||||
|
await self._ubridge_send(f"iol_bridge enable_packet_filter {location} {name} {state}")
|
||||||
|
|
||||||
async def adapter_remove_nio_binding(self, adapter_number, port_number):
|
async def adapter_remove_nio_binding(self, adapter_number, port_number):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@ -180,7 +180,7 @@ async def test_set_marker_filter_state_off(compute_project, manager):
|
|||||||
|
|
||||||
node = VPCSVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", compute_project, manager)
|
node = VPCSVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", compute_project, manager)
|
||||||
node._ubridge_send = AsyncioMagicMock()
|
node._ubridge_send = AsyncioMagicMock()
|
||||||
node._marker_filter_bridges["m"] = "VPCS-10"
|
node._marker_filter_bridges["m", "L"] = "VPCS-10"
|
||||||
await node._ubridge_set_marker_filter_state("m", False)
|
await node._ubridge_set_marker_filter_state("m", False)
|
||||||
node._ubridge_send.assert_called_with("bridge enable_packet_filter VPCS-10 m off")
|
node._ubridge_send.assert_called_with("bridge enable_packet_filter VPCS-10 m off")
|
||||||
|
|
||||||
@ -190,7 +190,7 @@ async def test_set_marker_filter_state_on(compute_project, manager):
|
|||||||
|
|
||||||
node = VPCSVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", compute_project, manager)
|
node = VPCSVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", compute_project, manager)
|
||||||
node._ubridge_send = AsyncioMagicMock()
|
node._ubridge_send = AsyncioMagicMock()
|
||||||
node._marker_filter_bridges["m"] = "VPCS-10"
|
node._marker_filter_bridges["m", "L"] = "VPCS-10"
|
||||||
await node._ubridge_set_marker_filter_state("m", True)
|
await node._ubridge_set_marker_filter_state("m", True)
|
||||||
node._ubridge_send.assert_called_with("bridge enable_packet_filter VPCS-10 m on")
|
node._ubridge_send.assert_called_with("bridge enable_packet_filter VPCS-10 m on")
|
||||||
|
|
||||||
@ -226,4 +226,4 @@ async def test_apply_markers_turns_disabled_filter_off(compute_project, manager)
|
|||||||
mm.instance.return_value.register = MagicMock()
|
mm.instance.return_value.register = MagicMock()
|
||||||
await node._ubridge_apply_markers("VPCS-10", nio)
|
await node._ubridge_apply_markers("VPCS-10", nio)
|
||||||
node._ubridge_send.assert_any_call("bridge enable_packet_filter VPCS-10 m off")
|
node._ubridge_send.assert_any_call("bridge enable_packet_filter VPCS-10 m off")
|
||||||
assert node._marker_filter_bridges["m"] == "VPCS-10"
|
assert node._marker_filter_bridges["m", "L1"] == "VPCS-10"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user