mirror of
https://github.com/GNS3/gns3-server.git
synced 2026-10-28 19:22:22 +02:00
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:
parent
a40503e44b
commit
caa054986f
@ -20,6 +20,7 @@ import asyncio
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from gns3server.config import Config
|
from gns3server.config import Config
|
||||||
|
from gns3server.utils.application_id import is_iol_runner_environment
|
||||||
from gns3server.utils.kernel_anchor import kernel_anchor_name
|
from gns3server.utils.kernel_anchor import kernel_anchor_name
|
||||||
from gns3server.utils.packet_filter_validation import (
|
from gns3server.utils.packet_filter_validation import (
|
||||||
KERNEL_UNSUPPORTED_FILTERS,
|
KERNEL_UNSUPPORTED_FILTERS,
|
||||||
@ -56,14 +57,18 @@ log = logging.getLogger(__name__)
|
|||||||
def _is_unix_socket_docker(node):
|
def _is_unix_socket_docker(node):
|
||||||
"""
|
"""
|
||||||
Best-effort detection of vendor containers bridged through AF_UNIX
|
Best-effort detection of vendor containers bridged through AF_UNIX
|
||||||
socket pairs (GNS3_UNIX_SOCKET_NIO in their environment): they have no
|
socket pairs: they have no host-side interface to enslave into a kernel
|
||||||
host-side interface to enslave into a kernel bridge. The compute side
|
bridge. Two ways in — the generic GNS3_UNIX_SOCKET_NIO knob, or the IOL
|
||||||
|
runner marker, whose class (IOLDockerVM, selected on GNS3_IOL_RUNNER)
|
||||||
|
forces the unix-socket wiring whatever the knob says. The compute side
|
||||||
independently rejects kernel-datapath NIOs on these containers, so a
|
independently rejects kernel-datapath NIOs on these containers, so a
|
||||||
missed detection here only surfaces as a clearer-late error.
|
missed detection surfaces as a clearer-late error — except on the switch
|
||||||
|
fast path, where an absent anchor is deferred forever by design (a link
|
||||||
|
to a stopped node must not fail), making the miss a silently dead cable.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
environment = (node.properties or {}).get("environment") or ""
|
environment = (node.properties or {}).get("environment") or ""
|
||||||
return "GNS3_UNIX_SOCKET_NIO" in environment
|
return "GNS3_UNIX_SOCKET_NIO" in environment or is_iol_runner_environment(environment)
|
||||||
|
|
||||||
|
|
||||||
class UDPLink(Link):
|
class UDPLink(Link):
|
||||||
|
|||||||
@ -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
|
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
|
@pytest.mark.asyncio
|
||||||
async def test_kernel_datapath_disabled_by_config(project):
|
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()
|
switch.put = AsyncioMagicMock()
|
||||||
|
|
||||||
await link.node_started(peer)
|
await link.node_started(peer)
|
||||||
switch.put.assert_called_once_with(
|
switch.put.assert_called_once_with("/adapters/0/ports/0/nio", data=entries[0][3], timeout=120)
|
||||||
"/adapters/0/ports/0/nio", data=entries[0][3], timeout=120
|
|
||||||
)
|
|
||||||
|
|
||||||
# a relay link never re-pushes anything
|
# a relay link never re-pushes anything
|
||||||
switch.put.reset_mock()
|
switch.put.reset_mock()
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user