mirror of
https://github.com/GNS3/gns3-server.git
synced 2026-11-06 07:11:13 +02:00
test(e2e): take the switch anchors out of NetworkManager's reach
The esw kernel fast-path scenario deletes and re-creates a link, which re-enslaves the peer anchor — and on NetworkManager hosts NM's netdev state machine can release an NM-managed TAP from its bridge some 60-70 ms after enslavement, ~50% of re-enslavements, while the tap is in NM's not-yet-assumed state. The freed port stays administratively up and no GNS3 process logs anything: the membership assert sees the join, then the ping measures a silent blackhole (this was the line-277 e2e failure). Root-caused with a server-free harness (one uBridge, one fresh tap, one bridge, an addif/delif loop): the same tap frees 20/40 while NM-managed, 0/40 after `nmcli device set <tap> managed no`, 20/40 again when re-managed. Docker's veth host ends are not NM-managed, which is why only TAP anchors ever showed it — including the "anchor lost its bridge membership after a ports_mapping update (TAP peers only)" incident from the switch's development, whose conclusion is corrected in the docs. Unmanage the anchors once the routers have booted, before the scenario re-enslaves them; document the failure signature and the host-side remedy (per-device nmcli call, or NM unmanaged-devices configuration) in the L2-anchor spec and the switch feature doc.
This commit is contained in:
4 files changed
+51
-4
No files matched your search
@@ -1053,6 +1053,32 @@ def bridge_members(bridge):
|
||||
return sorted(os.listdir(f"/sys/class/net/{bridge}/brif"))
|
||||
|
||||
|
||||
def unmanage_from_networkmanager(*names):
|
||||
"""Best-effort ``nmcli device set <name> managed no`` for the given
|
||||
interfaces.
|
||||
|
||||
NetworkManager (desktop hosts) manages the TAP devices uBridge creates.
|
||||
While a tap is in NM's not-yet-assumed state, NM's netdev-event state
|
||||
machine can *release* it from its bridge some 60-70 ms after
|
||||
enslavement (measured: ~50% of re-enslavements on an NM host): the port
|
||||
is left administratively up, out of the bridge, with no server-side log
|
||||
or error — a silent blackhole. Scenarios that detach and re-enslave an
|
||||
anchor (link delete + recreate, node restart) must call this once their
|
||||
nodes have booted, or they are nondeterministically flaky on such
|
||||
hosts. Silently a no-op where nmcli is unavailable.
|
||||
"""
|
||||
nmcli = shutil.which("nmcli")
|
||||
if not nmcli:
|
||||
return
|
||||
for name in names:
|
||||
# S603: resolved binary, fixed argv, no shell
|
||||
subprocess.run( # noqa: S603
|
||||
[nmcli, "device", "set", name, "managed", "no"],
|
||||
capture_output=True,
|
||||
check=False,
|
||||
)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# L2-anchor spec assertions (docs/design/ubridge-l2-anchor-spec.md §E)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@@ -146,6 +146,15 @@ def test_ethernet_switch_kernel_fast_path():
|
||||
c1 = _boot(server, pid, r1_id)
|
||||
c2 = _boot(server, pid, r2_id)
|
||||
|
||||
# Keep NetworkManager's hands off the anchors: on NM hosts its
|
||||
# netdev state machine can release a freshly re-enslaved tap from
|
||||
# the bridge ~60 ms after the join (silently — the port stays up,
|
||||
# the server keeps believing it is attached), which the link
|
||||
# delete/recreate below triggers nondeterministically. The first
|
||||
# join at node start is unaffected; this covers everything after
|
||||
# it.
|
||||
harness.unmanage_from_networkmanager(a1, a2)
|
||||
|
||||
# Deferred joins completed by the node-start re-push: the anchors
|
||||
# ARE the switch ports now.
|
||||
assert harness.wait_until(lambda: harness.bridge_members(sw_bridge) == sorted([a1, a2]), timeout=15), (
|
||||
|
||||
Reference in new issue
Block a user