From 52fcc8349461cdf52ad2e0b5caf493bf858e3720 Mon Sep 17 00:00:00 2001 From: tikD1337 <199419268+tikD1337@users.noreply.github.com> Date: Fri, 23 Oct 2026 15:00:27 +0200 Subject: [PATCH 1/2] Gate the TAP/kernel datapath on uBridge >= 1.3.1 uBridge 1.2.3 already has the tap module, so the persistent-TAP probes (_probe_persistent_taps on the node, probe_tap_support and siblings for /capabilities) pass on it. QEMU, Dynamips and VPCS nodes then anchor on TAPs and the controller builds kernel links that need 1.3.x commands: kernel markers and the eBPF tc modes (1.3.0) and per-interface kernel captures (`capture stop_kernel `, 1.3.1). On 1.2.3 a MikroTik CHR node sent frames but never got DHCP past DISCOVER/OFFER, and stopping a capture failed with "Bad number of parameters". Add Hypervisor.supports_kernel_datapath (version >= 1.3.1) and refuse older builds in the node-side probe and the four anchoring capability probes (tap, iol tap, bridge tap, vxlan). The existing fallback then applies: nodes keep the legacy relay datapath with the usual warning, which now names the installed version, and links stay on the relay. Co-Authored-By: Claude Opus 5.5 --- gns3server/compute/kernel_datapath.py | 11 +++++++++ gns3server/compute/ubridge/hypervisor.py | 21 ++++++++++++++++ gns3server/compute/ubridge/tc_probe.py | 22 ++++++++++++++++- .../compute/qemu/test_qemu_kernel_datapath.py | 23 ++++++++++++++++++ tests/compute/ubridge/test_hypervisor.py | 18 ++++++++++++++ tests/compute/ubridge/test_tc_probe.py | 24 +++++++++++++++++++ 6 files changed, 118 insertions(+), 1 deletion(-) diff --git a/gns3server/compute/kernel_datapath.py b/gns3server/compute/kernel_datapath.py index bbc2317ad..8dea76fd6 100644 --- a/gns3server/compute/kernel_datapath.py +++ b/gns3server/compute/kernel_datapath.py @@ -53,6 +53,7 @@ import socket import psutil from gns3server.compute.error import NodeError +from gns3server.compute.ubridge.hypervisor import KERNEL_DATAPATH_MINIMUM_VERSION from gns3server.compute.ubridge.ubridge_error import UbridgeError from gns3server.utils.tc_capabilities import FILTER_EBPF_MODES, parse_tc_capabilities, usable_ebpf_modes @@ -158,8 +159,18 @@ class KernelDatapathMixin: "Unknown command"/"Unknown module", a build without CAP_NET_ADMIN fails outright. The caller composes its own warning and fallback: the legacy relay datapath keeps working either way. + + A build older than the kernel dataplane is refused without asking: + 1.2.3 already has the tap module, so its `tap create` succeeds, + but the anchors it creates would then carry kernel links that need + 1.3.x commands (per-interface kernel captures, kernel markers). """ + if not self.ubridge.supports_kernel_datapath: + return UbridgeError( + f"uBridge {self.ubridge.version} is too old for the kernel datapath " + f"(>= {KERNEL_DATAPATH_MINIMUM_VERSION} required)" + ) try: await self._ubridge_send(f'tap create "{probe_name}"') except UbridgeError as e: diff --git a/gns3server/compute/ubridge/hypervisor.py b/gns3server/compute/ubridge/hypervisor.py index 8a7b78090..6126337bd 100644 --- a/gns3server/compute/ubridge/hypervisor.py +++ b/gns3server/compute/ubridge/hypervisor.py @@ -34,6 +34,13 @@ from .ubridge_hypervisor import UBridgeHypervisor log = logging.getLogger(__name__) +# The kernel dataplane the persistent-TAP anchors feed (kernel links, kernel +# markers and captures) needs uBridge >= 1.3.1: 1.3.0 introduced it, 1.3.1 +# made kernel captures per interface (`capture stop_kernel `, which the +# server always sends). The tap module itself is older (1.2.3), so a +# successful `tap create` alone does not mean the dataplane is there. +KERNEL_DATAPATH_MINIMUM_VERSION = "1.3.1" + class Hypervisor(UBridgeHypervisor): """ @@ -149,6 +156,20 @@ class Hypervisor(UBridgeHypervisor): return self._version + @property + def supports_kernel_datapath(self): + """ + Whether this uBridge is recent enough for the kernel dataplane + (persistent-TAP anchors, kernel links). False until the version is + known, i.e. before start(). + + :returns: boolean + """ + + if not self._version: + return False + return parse_version(self._version) >= parse_version(KERNEL_DATAPATH_MINIMUM_VERSION) + async def _check_ubridge_version(self, env=None): """ Checks if the ubridge executable version meets the minimum required. diff --git a/gns3server/compute/ubridge/tc_probe.py b/gns3server/compute/ubridge/tc_probe.py index 3da66c42e..9d68451af 100644 --- a/gns3server/compute/ubridge/tc_probe.py +++ b/gns3server/compute/ubridge/tc_probe.py @@ -45,7 +45,7 @@ from gns3server.config import Config from gns3server.utils.tc_capabilities import parse_tc_capabilities from gns3server.utils.vxlan import VXLAN_VNI_MAX, VXLAN_VNI_MIN -from .hypervisor import Hypervisor +from .hypervisor import KERNEL_DATAPATH_MINIMUM_VERSION, Hypervisor from .ubridge_error import UbridgeError log = logging.getLogger(__name__) @@ -65,6 +65,22 @@ _vxlan_cache: dict[tuple[str, int, int, int], Optional[bool]] = {} _lock = asyncio.Lock() +def _require_kernel_datapath(hypervisor): + """ + Refuse a uBridge older than the kernel dataplane before any anchoring + probe asks it anything. Its own commands can still succeed — 1.2.3 has + the tap module, so `tap create` answers OK — while the kernel links + built on that answer need 1.3.x commands it lacks. Raised as a + UbridgeError so the caller's usual failure path reports unknown. + """ + + if not hypervisor.supports_kernel_datapath: + raise UbridgeError( + f"uBridge {hypervisor.version} is too old for the kernel datapath " + f"(>= {KERNEL_DATAPATH_MINIMUM_VERSION} required)" + ) + + async def probe_tc_capabilities(timeout: float = 15.0): # noqa: ASYNC109 - operation-specific timeout is intentional """ Probe the configured uBridge's tc module once per binary identity. @@ -176,6 +192,7 @@ async def _probe_tap(path, config, timeout): # noqa: ASYNC109 - operation-speci async def ask(): await hypervisor.start() + _require_kernel_datapath(hypervisor) await hypervisor.connect() await hypervisor.send(f"tap create {name}") try: @@ -255,6 +272,7 @@ async def _probe_iol_tap(path, config, timeout): # noqa: ASYNC109 - operation-s async def ask(): await hypervisor.start() + _require_kernel_datapath(hypervisor) await hypervisor.connect() await hypervisor.send(f"iol_bridge create {bridge} {_IOL_PROBE_BRIDGE_ID}") try: @@ -336,6 +354,7 @@ async def _probe_bridge_tap(path, config, timeout): # noqa: ASYNC109 - operatio async def ask(): await hypervisor.start() + _require_kernel_datapath(hypervisor) await hypervisor.connect() try: await hypervisor.send(f"bridge delete_nio_tap gns3brtapprobe{uuid.uuid4().hex[:4]} any") @@ -432,6 +451,7 @@ async def _probe_vxlan(path, config, timeout): # noqa: ASYNC109 - operation-spe async def ask(): await hypervisor.start() + _require_kernel_datapath(hypervisor) await hypervisor.connect() await hypervisor.send(f"vxlan create {name} {vni} remote=127.0.0.1 dstport={dstport}") try: diff --git a/tests/compute/qemu/test_qemu_kernel_datapath.py b/tests/compute/qemu/test_qemu_kernel_datapath.py index a2d57592a..a1e3bab06 100644 --- a/tests/compute/qemu/test_qemu_kernel_datapath.py +++ b/tests/compute/qemu/test_qemu_kernel_datapath.py @@ -122,6 +122,29 @@ async def test_prepare_tap_datapath_falls_back_without_the_tap_module(vm): assert emit.called +@pytest.mark.asyncio +async def test_prepare_tap_datapath_falls_back_below_the_kernel_dataplane_version(vm): + """ + uBridge 1.2.3 has the tap module, so a `tap create` probe alone passes + — and the node then runs on TAP anchors whose kernel links the build + cannot drive. Below the kernel-dataplane version the node keeps the + legacy socket-netdev datapath without creating any TAP, and the warning + names the installed version. + """ + + vm._ubridge_hypervisor.supports_kernel_datapath = False + vm._ubridge_hypervisor.version = "1.2.3" + with patch.object(vm.project, "emit") as emit: + await vm._prepare_tap_datapath() + + assert vm._tap_datapath is False + assert vm._kernel_taps == {} + assert not any(c.args[0].startswith("tap ") for c in vm._ubridge_send.call_args_list) + message = emit.call_args.args[1]["message"] + assert "1.2.3" in message + assert "1.3.1" in message + + @pytest.mark.asyncio async def test_tap_name_fits_ifnamsiz(vm): """ diff --git a/tests/compute/ubridge/test_hypervisor.py b/tests/compute/ubridge/test_hypervisor.py index 74f0be660..53d736797 100644 --- a/tests/compute/ubridge/test_hypervisor.py +++ b/tests/compute/ubridge/test_hypervisor.py @@ -194,6 +194,24 @@ async def test_accepts_ubridge_1_2_3(tmp_path, monkeypatch): assert hyp.version == "1.2.3" +@pytest.mark.parametrize( + "version, supported", + [ + ("", False), # not started yet: version unknown + ("1.2.3", False), # has the tap module, but no kernel dataplane + ("1.3.0", False), # no per-interface capture stop_kernel + ("1.3.1", True), + ("1.3.2", True), + ("1.4.0", True), + ], +) +def test_supports_kernel_datapath(tmp_path, monkeypatch, version, supported): + + hyp = _make("unix", tmp_path, monkeypatch) + hyp._version = version + assert hyp.supports_kernel_datapath is supported + + # --------------------------------------------------------------------------- # start: fail-fast on an immediately-exiting uBridge # --------------------------------------------------------------------------- diff --git a/tests/compute/ubridge/test_tc_probe.py b/tests/compute/ubridge/test_tc_probe.py index cab22077a..f529d397d 100644 --- a/tests/compute/ubridge/test_tc_probe.py +++ b/tests/compute/ubridge/test_tc_probe.py @@ -50,6 +50,9 @@ class FakeHypervisor: # doesn't exist), None = answer 100 (an impossible reply on the probe's # uuid name — the probe must treat it as unknown) bridge_tap_error = "214-bridge 'nosuch' doesn't exist" + # False = a build older than the kernel dataplane (uBridge < 1.3.1) + supports_kernel_datapath = True + version = "1.3.1" spawned = 0 def __init__(self, project, path, working_dir, transport, host, node_id): @@ -90,6 +93,8 @@ def probe_env(monkeypatch, tmp_path): FakeHypervisor.error = None FakeHypervisor.error_prefix = None FakeHypervisor.bridge_tap_error = "214-bridge 'nosuch' doesn't exist" + FakeHypervisor.supports_kernel_datapath = True + FakeHypervisor.version = "1.3.1" FakeHypervisor.reply = ["netem=delay,rate;ebpf=1;cbpf=1;ebpf_modes=nth,quota,window,flow"] tc_probe._cache.clear() tc_probe._tap_cache.clear() @@ -285,6 +290,25 @@ async def test_probe_bridge_tap_support_missing_binary(monkeypatch): assert FakeHypervisor.spawned == 0 +@pytest.mark.parametrize( + "probe", [probe_tap_support, probe_iol_tap_support, probe_bridge_tap_support, probe_vxlan_support] +) +async def test_anchor_probes_unknown_below_the_kernel_dataplane_version(probe): + """ + uBridge 1.2.3 already has the tap module, so its `tap create` succeeds — + but the kernel links built on that answer need commands that only 1.3.x + has (per-interface `capture stop_kernel`, kernel markers, ...). A build + below the kernel-dataplane version is unknown without being asked + anything, so every node stays on the relay datapath. + """ + + FakeHypervisor.supports_kernel_datapath = False + FakeHypervisor.version = "1.2.3" + assert await probe() is None + assert FakeHypervisor.spawned == 1 + assert FakeHypervisor.last.commands == [] + + class _FixedUuid: """A uuid4 stand-in whose .int/.hex pin the probe's random draws.""" From 07b0c21fe50d28c33d9cc33b10816d53264ef941 Mon Sep 17 00:00:00 2001 From: tikD1337 <199419268+tikD1337@users.noreply.github.com> Date: Sun, 25 Oct 2026 01:39:52 +0200 Subject: [PATCH 2/2] Require uBridge >= 1.3.1 instead of gating the kernel datapath Review feedback: 3.1 is still in alpha and the GNS3 VM ships a matching uBridge, so a hard minimum is simpler than a version-based degrade path, and it also covers Docker endpoints and the builtin Ethernet switch, which never went through the anchor probes. Raise minimum_required_version to 1.3.1 and drop supports_kernel_datapath and its uses; the relay fallback for capability problems (no CAP_NET_ADMIN) is unchanged. Co-Authored-By: Claude Opus 5.5 --- gns3server/compute/kernel_datapath.py | 11 ------ gns3server/compute/ubridge/hypervisor.py | 29 ++------------- gns3server/compute/ubridge/tc_probe.py | 22 +---------- .../compute/qemu/test_qemu_kernel_datapath.py | 23 ------------ tests/compute/ubridge/test_hypervisor.py | 37 +++++++------------ tests/compute/ubridge/test_tc_probe.py | 24 ------------ 6 files changed, 18 insertions(+), 128 deletions(-) diff --git a/gns3server/compute/kernel_datapath.py b/gns3server/compute/kernel_datapath.py index 8dea76fd6..bbc2317ad 100644 --- a/gns3server/compute/kernel_datapath.py +++ b/gns3server/compute/kernel_datapath.py @@ -53,7 +53,6 @@ import socket import psutil from gns3server.compute.error import NodeError -from gns3server.compute.ubridge.hypervisor import KERNEL_DATAPATH_MINIMUM_VERSION from gns3server.compute.ubridge.ubridge_error import UbridgeError from gns3server.utils.tc_capabilities import FILTER_EBPF_MODES, parse_tc_capabilities, usable_ebpf_modes @@ -159,18 +158,8 @@ class KernelDatapathMixin: "Unknown command"/"Unknown module", a build without CAP_NET_ADMIN fails outright. The caller composes its own warning and fallback: the legacy relay datapath keeps working either way. - - A build older than the kernel dataplane is refused without asking: - 1.2.3 already has the tap module, so its `tap create` succeeds, - but the anchors it creates would then carry kernel links that need - 1.3.x commands (per-interface kernel captures, kernel markers). """ - if not self.ubridge.supports_kernel_datapath: - return UbridgeError( - f"uBridge {self.ubridge.version} is too old for the kernel datapath " - f"(>= {KERNEL_DATAPATH_MINIMUM_VERSION} required)" - ) try: await self._ubridge_send(f'tap create "{probe_name}"') except UbridgeError as e: diff --git a/gns3server/compute/ubridge/hypervisor.py b/gns3server/compute/ubridge/hypervisor.py index 6126337bd..1f2db23b0 100644 --- a/gns3server/compute/ubridge/hypervisor.py +++ b/gns3server/compute/ubridge/hypervisor.py @@ -34,13 +34,6 @@ from .ubridge_hypervisor import UBridgeHypervisor log = logging.getLogger(__name__) -# The kernel dataplane the persistent-TAP anchors feed (kernel links, kernel -# markers and captures) needs uBridge >= 1.3.1: 1.3.0 introduced it, 1.3.1 -# made kernel captures per interface (`capture stop_kernel `, which the -# server always sends). The tap module itself is older (1.2.3), so a -# successful `tap create` alone does not mean the dataplane is there. -KERNEL_DATAPATH_MINIMUM_VERSION = "1.3.1" - class Hypervisor(UBridgeHypervisor): """ @@ -156,20 +149,6 @@ class Hypervisor(UBridgeHypervisor): return self._version - @property - def supports_kernel_datapath(self): - """ - Whether this uBridge is recent enough for the kernel dataplane - (persistent-TAP anchors, kernel links). False until the version is - known, i.e. before start(). - - :returns: boolean - """ - - if not self._version: - return False - return parse_version(self._version) >= parse_version(KERNEL_DATAPATH_MINIMUM_VERSION) - async def _check_ubridge_version(self, env=None): """ Checks if the ubridge executable version meets the minimum required. @@ -179,10 +158,10 @@ class Hypervisor(UBridgeHypervisor): match = re.search(r"ubridge version ([0-9a-z\.]+)", output) if match: self._version = match.group(1) - # uBridge >= 1.2.3 is required for the AF_UNIX control - # channel, marker filters, builtin Ethernet Switch support, - # and Docker TAP carrier control. - minimum_required_version = "1.2.3" + # uBridge >= 1.3.1 is required for the kernel datapath: + # persistent TAP anchors, kernel links, kernel markers and + # per-interface kernel captures. + minimum_required_version = "1.3.1" if parse_version(self._version) < parse_version(minimum_required_version): raise UbridgeError(f"uBridge executable version must be >= {minimum_required_version}") else: diff --git a/gns3server/compute/ubridge/tc_probe.py b/gns3server/compute/ubridge/tc_probe.py index 9d68451af..3da66c42e 100644 --- a/gns3server/compute/ubridge/tc_probe.py +++ b/gns3server/compute/ubridge/tc_probe.py @@ -45,7 +45,7 @@ from gns3server.config import Config from gns3server.utils.tc_capabilities import parse_tc_capabilities from gns3server.utils.vxlan import VXLAN_VNI_MAX, VXLAN_VNI_MIN -from .hypervisor import KERNEL_DATAPATH_MINIMUM_VERSION, Hypervisor +from .hypervisor import Hypervisor from .ubridge_error import UbridgeError log = logging.getLogger(__name__) @@ -65,22 +65,6 @@ _vxlan_cache: dict[tuple[str, int, int, int], Optional[bool]] = {} _lock = asyncio.Lock() -def _require_kernel_datapath(hypervisor): - """ - Refuse a uBridge older than the kernel dataplane before any anchoring - probe asks it anything. Its own commands can still succeed — 1.2.3 has - the tap module, so `tap create` answers OK — while the kernel links - built on that answer need 1.3.x commands it lacks. Raised as a - UbridgeError so the caller's usual failure path reports unknown. - """ - - if not hypervisor.supports_kernel_datapath: - raise UbridgeError( - f"uBridge {hypervisor.version} is too old for the kernel datapath " - f"(>= {KERNEL_DATAPATH_MINIMUM_VERSION} required)" - ) - - async def probe_tc_capabilities(timeout: float = 15.0): # noqa: ASYNC109 - operation-specific timeout is intentional """ Probe the configured uBridge's tc module once per binary identity. @@ -192,7 +176,6 @@ async def _probe_tap(path, config, timeout): # noqa: ASYNC109 - operation-speci async def ask(): await hypervisor.start() - _require_kernel_datapath(hypervisor) await hypervisor.connect() await hypervisor.send(f"tap create {name}") try: @@ -272,7 +255,6 @@ async def _probe_iol_tap(path, config, timeout): # noqa: ASYNC109 - operation-s async def ask(): await hypervisor.start() - _require_kernel_datapath(hypervisor) await hypervisor.connect() await hypervisor.send(f"iol_bridge create {bridge} {_IOL_PROBE_BRIDGE_ID}") try: @@ -354,7 +336,6 @@ async def _probe_bridge_tap(path, config, timeout): # noqa: ASYNC109 - operatio async def ask(): await hypervisor.start() - _require_kernel_datapath(hypervisor) await hypervisor.connect() try: await hypervisor.send(f"bridge delete_nio_tap gns3brtapprobe{uuid.uuid4().hex[:4]} any") @@ -451,7 +432,6 @@ async def _probe_vxlan(path, config, timeout): # noqa: ASYNC109 - operation-spe async def ask(): await hypervisor.start() - _require_kernel_datapath(hypervisor) await hypervisor.connect() await hypervisor.send(f"vxlan create {name} {vni} remote=127.0.0.1 dstport={dstport}") try: diff --git a/tests/compute/qemu/test_qemu_kernel_datapath.py b/tests/compute/qemu/test_qemu_kernel_datapath.py index a1e3bab06..a2d57592a 100644 --- a/tests/compute/qemu/test_qemu_kernel_datapath.py +++ b/tests/compute/qemu/test_qemu_kernel_datapath.py @@ -122,29 +122,6 @@ async def test_prepare_tap_datapath_falls_back_without_the_tap_module(vm): assert emit.called -@pytest.mark.asyncio -async def test_prepare_tap_datapath_falls_back_below_the_kernel_dataplane_version(vm): - """ - uBridge 1.2.3 has the tap module, so a `tap create` probe alone passes - — and the node then runs on TAP anchors whose kernel links the build - cannot drive. Below the kernel-dataplane version the node keeps the - legacy socket-netdev datapath without creating any TAP, and the warning - names the installed version. - """ - - vm._ubridge_hypervisor.supports_kernel_datapath = False - vm._ubridge_hypervisor.version = "1.2.3" - with patch.object(vm.project, "emit") as emit: - await vm._prepare_tap_datapath() - - assert vm._tap_datapath is False - assert vm._kernel_taps == {} - assert not any(c.args[0].startswith("tap ") for c in vm._ubridge_send.call_args_list) - message = emit.call_args.args[1]["message"] - assert "1.2.3" in message - assert "1.3.1" in message - - @pytest.mark.asyncio async def test_tap_name_fits_ifnamsiz(vm): """ diff --git a/tests/compute/ubridge/test_hypervisor.py b/tests/compute/ubridge/test_hypervisor.py index 53d736797..2a8788475 100644 --- a/tests/compute/ubridge/test_hypervisor.py +++ b/tests/compute/ubridge/test_hypervisor.py @@ -168,48 +168,37 @@ async def test_stop_tcp_has_no_socket_to_unlink(tmp_path, monkeypatch): @pytest.mark.asyncio -async def test_requires_ubridge_1_2_3(tmp_path, monkeypatch): +@pytest.mark.parametrize( + "version", + [ + "1.2.3", # has the tap module, but no kernel dataplane + "1.3.0", # no per-interface capture stop_kernel + ], +) +async def test_requires_ubridge_1_3_1(tmp_path, monkeypatch, version): hyp = _make("unix", tmp_path, monkeypatch) with patch( "gns3server.compute.ubridge.hypervisor.subprocess_check_output", new_callable=AsyncMock, - return_value="ubridge version 1.2.2", + return_value=f"ubridge version {version}", ): - with pytest.raises(UbridgeError, match=r">= 1\.2\.3"): + with pytest.raises(UbridgeError, match=r">= 1\.3\.1"): await hyp._check_ubridge_version() @pytest.mark.asyncio -async def test_accepts_ubridge_1_2_3(tmp_path, monkeypatch): +async def test_accepts_ubridge_1_3_1(tmp_path, monkeypatch): hyp = _make("unix", tmp_path, monkeypatch) with patch( "gns3server.compute.ubridge.hypervisor.subprocess_check_output", new_callable=AsyncMock, - return_value="ubridge version 1.2.3", + return_value="ubridge version 1.3.1", ): await hyp._check_ubridge_version() - assert hyp.version == "1.2.3" - - -@pytest.mark.parametrize( - "version, supported", - [ - ("", False), # not started yet: version unknown - ("1.2.3", False), # has the tap module, but no kernel dataplane - ("1.3.0", False), # no per-interface capture stop_kernel - ("1.3.1", True), - ("1.3.2", True), - ("1.4.0", True), - ], -) -def test_supports_kernel_datapath(tmp_path, monkeypatch, version, supported): - - hyp = _make("unix", tmp_path, monkeypatch) - hyp._version = version - assert hyp.supports_kernel_datapath is supported + assert hyp.version == "1.3.1" # --------------------------------------------------------------------------- diff --git a/tests/compute/ubridge/test_tc_probe.py b/tests/compute/ubridge/test_tc_probe.py index f529d397d..cab22077a 100644 --- a/tests/compute/ubridge/test_tc_probe.py +++ b/tests/compute/ubridge/test_tc_probe.py @@ -50,9 +50,6 @@ class FakeHypervisor: # doesn't exist), None = answer 100 (an impossible reply on the probe's # uuid name — the probe must treat it as unknown) bridge_tap_error = "214-bridge 'nosuch' doesn't exist" - # False = a build older than the kernel dataplane (uBridge < 1.3.1) - supports_kernel_datapath = True - version = "1.3.1" spawned = 0 def __init__(self, project, path, working_dir, transport, host, node_id): @@ -93,8 +90,6 @@ def probe_env(monkeypatch, tmp_path): FakeHypervisor.error = None FakeHypervisor.error_prefix = None FakeHypervisor.bridge_tap_error = "214-bridge 'nosuch' doesn't exist" - FakeHypervisor.supports_kernel_datapath = True - FakeHypervisor.version = "1.3.1" FakeHypervisor.reply = ["netem=delay,rate;ebpf=1;cbpf=1;ebpf_modes=nth,quota,window,flow"] tc_probe._cache.clear() tc_probe._tap_cache.clear() @@ -290,25 +285,6 @@ async def test_probe_bridge_tap_support_missing_binary(monkeypatch): assert FakeHypervisor.spawned == 0 -@pytest.mark.parametrize( - "probe", [probe_tap_support, probe_iol_tap_support, probe_bridge_tap_support, probe_vxlan_support] -) -async def test_anchor_probes_unknown_below_the_kernel_dataplane_version(probe): - """ - uBridge 1.2.3 already has the tap module, so its `tap create` succeeds — - but the kernel links built on that answer need commands that only 1.3.x - has (per-interface `capture stop_kernel`, kernel markers, ...). A build - below the kernel-dataplane version is unknown without being asked - anything, so every node stays on the relay datapath. - """ - - FakeHypervisor.supports_kernel_datapath = False - FakeHypervisor.version = "1.2.3" - assert await probe() is None - assert FakeHypervisor.spawned == 1 - assert FakeHypervisor.last.commands == [] - - class _FixedUuid: """A uuid4 stand-in whose .int/.hex pin the probe's random draws."""