fix(controller): keep IOL runner containers off the kernel datapath

The unix-socket container gate matched GNS3_UNIX_SOCKET_NIO in the node
environment only, but the IOL runner reaches the same wiring through the
GNS3_IOL_RUNNER marker: the compute selects IOLDockerVM on it, and that
class forces the AF_UNIX socket pairs whatever the generic knob says —
and the documented environment for those nodes carries only the marker.
Such a container was therefore handed a kernel link its compute cannot
serve. The plain case failed at link creation (no veth end to enslave);
on the switch fast path it was worse — the switch defers a join against a
missing anchor by design (a link to a stopped node must not fail), so the
cable was silently dead instead of merely slow.

Match both markers, through the shared is_iol_runner_environment helper so
the controller and the compute select the same way. Verified live on a
real server: docker↔IOL-XE and switch↔IOL-XE links stay relay while the
docker↔switch control link still takes the kernel datapath.
This commit is contained in:
YueGuobin committed 2026-10-06 00:21:09 +08:00
1 parent a40503e44b
commit caa054986f
2 files changed
+51 -7

No files matched your search

+42 -3
View File
@@ -419,6 +419,47 @@ async def test_kernel_datapath_not_eligible_for_unix_socket_containers(project):
assert link._kernel_datapath_eligible(node1, node2) is False
@pytest.mark.asyncio
async def test_kernel_datapath_not_eligible_for_iol_runner_containers(project):
"""
The IOL runner marker is the other way into the unix-socket datapath: the
compute selects IOLDockerVM on GNS3_IOL_RUNNER and that class wires the
adapters through AF_UNIX socket pairs whatever the generic
GNS3_UNIX_SOCKET_NIO knob says — and the documented environment for
those nodes carries only the marker. Matching the knob alone let them
through to a kernel link their compute cannot serve.
"""
link, node1, node2 = await _kernel_link(project)
node2._properties = {"environment": "GNS3_IOL_RUNNER=1"}
assert link._kernel_datapath_eligible(node1, node2) is False
# the marker travels in a multi-line environment like any other knob
node2._properties = {"environment": "GNS3_IOL_RUNNER=1\nGNS3_IOL_STARTUP_CONFIG=cfg.txt"}
assert link._kernel_datapath_eligible(node1, node2) is False
@pytest.mark.asyncio
async def test_kernel_datapath_not_eligible_for_an_iol_runner_container_on_a_switch(project):
"""
The switch fast path absorbs the peer's anchor, and a container bridged
through unix sockets has none — nor will it ever. The switch defers a
join against a stopped peer by design (it must not fail the link), so
such a cable would be silently dead instead of merely slow: the gate has
to run before the switch branch, keeping the link on the relay the
switch's own port TAP serves.
"""
compute = MagicMock()
compute.id = "compute-1"
compute.capabilities = {"ubridge_tap": True, "ubridge_iol_tap": True}
switch = _node(project, compute, "sw1", node_type="ethernet_switch")
peer = _node(project, compute, "iol1", environment="GNS3_IOL_RUNNER=1")
link = await _link(project, switch, peer)
assert link._kernel_datapath_eligible(switch, peer) is False
@pytest.mark.asyncio
async def test_kernel_datapath_disabled_by_config(project):
@@ -915,9 +956,7 @@ async def test_switch_link_repushes_the_switch_nio_on_node_start(project):
switch.put = AsyncioMagicMock()
await link.node_started(peer)
switch.put.assert_called_once_with(
"/adapters/0/ports/0/nio", data=entries[0][3], timeout=120
)
switch.put.assert_called_once_with("/adapters/0/ports/0/nio", data=entries[0][3], timeout=120)
# a relay link never re-pushes anything
switch.put.reset_mock()