mirror of
https://github.com/GNS3/gns3-server.git
synced 2026-11-03 05:51:15 +02:00
feat(docker): unify adapter interfaces on veth, allow kernel links at runtime
Every Docker adapter is now born as a veth pair at container start (unified interface) regardless of NIO type: guest end in the container namespace, host end in the root namespace. Kernel links enslave the host end into the per-link Linux bridge; relay links attach it to the uBridge relay via AF_PACKET (bridge add_nio_ethernet) — the TAP path is gone. This makes the datapath a runtime decision: - links can be created on running containers (kernel or relay) without touching container interfaces — the controller stopped-node eligibility check is removed - project reopen re-prepares relay links and upgrades eligible ones to the kernel datapath - packet capture start/stop now key on the NIO type (not veth presence) so relay-over-veth captures use the relay capture, kernel ones the AF_PACKET module - vendor unix-socket containers keep their wiring: no veth is required on the relay path
This commit is contained in:
6 files changed
+121
-105
No files matched your search
@@ -125,10 +125,11 @@ async def test_add_ubridge_kernel_connection(vm):
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_add_ubridge_connection_none_nio_stays_on_tap_path(vm):
|
||||
async def test_add_ubridge_connection_none_nio_creates_veth(vm):
|
||||
"""
|
||||
Unconnected adapters keep the relay TAP: the veth variant is only created
|
||||
for adapters whose NIO is a kernel one at start time.
|
||||
Unconnected adapters are born as veths too (the unified interface): the
|
||||
interface exists inside the container with carrier off, and any link
|
||||
type can be attached later — at runtime — without rebirthing it.
|
||||
"""
|
||||
|
||||
vm._ubridge_hypervisor = MagicMock()
|
||||
@@ -136,11 +137,42 @@ async def test_add_ubridge_connection_none_nio_stays_on_tap_path(vm):
|
||||
|
||||
await vm._add_ubridge_connection(None, 0)
|
||||
|
||||
host_ifc, guest_ifc = vm._veth_names(0, 0)
|
||||
vm._ubridge_hypervisor.assert_has_calls([
|
||||
call.send("bridge create bridge0"),
|
||||
call.send("bridge add_nio_tap bridge0 tap-gns3-e0 off"),
|
||||
call.send(f'docker create_veth "{host_ifc}" "{guest_ifc}"'),
|
||||
call.send(f'link set "{host_ifc}" down'),
|
||||
call.send(f'docker move_to_ns {guest_ifc} 42 eth0'),
|
||||
], any_order=True)
|
||||
assert vm._kernel_veths == {}
|
||||
assert vm._kernel_veths[(0, 0)] == host_ifc
|
||||
# no relay bridge and no TAP for an unconnected adapter
|
||||
assert "bridge0" not in vm._bridges
|
||||
for c in vm._ubridge_hypervisor.method_calls:
|
||||
assert "add_nio_tap" not in str(c)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_add_ubridge_connection_udp_relays_over_veth(vm):
|
||||
"""
|
||||
A relay NIO at start: the adapter is born as a veth and the uBridge
|
||||
relay attaches to its host end via AF_PACKET (add_nio_ethernet) —
|
||||
no TAP anywhere.
|
||||
"""
|
||||
|
||||
vm._ubridge_hypervisor = MagicMock()
|
||||
vm._namespace = 42
|
||||
|
||||
nio = vm.manager.create_nio({"type": "nio_udp", "lport": 4242, "rport": 4343, "rhost": "127.0.0.1"})
|
||||
await vm._add_ubridge_connection(nio, 0)
|
||||
|
||||
host_ifc, _ = vm._veth_names(0, 0)
|
||||
vm._ubridge_hypervisor.assert_has_calls([
|
||||
call.send(f'docker create_veth "{host_ifc}" "{vm._veth_names(0, 0)[1]}"'),
|
||||
call.send("bridge create bridge0"),
|
||||
call.send(f'bridge add_nio_ethernet bridge0 "{host_ifc}"'),
|
||||
call.send("bridge add_nio_udp bridge0 4242 127.0.0.1 4343"),
|
||||
call.send("bridge start bridge0"),
|
||||
], any_order=True)
|
||||
assert "bridge0" in vm._bridges
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
|
||||
@@ -1541,14 +1541,17 @@ async def test_add_ubridge_connection(vm):
|
||||
vm._namespace = 42
|
||||
await vm._add_ubridge_connection(nio, 0)
|
||||
|
||||
host_ifc, guest_ifc = vm._veth_names(0, 0)
|
||||
calls = [
|
||||
call.send(f'docker create_veth "{host_ifc}" "{guest_ifc}"'),
|
||||
call.send(f'link set "{host_ifc}" down'),
|
||||
call.send(f"docker move_to_ns {guest_ifc} 42 eth0"),
|
||||
call.send("bridge create bridge0"),
|
||||
call.send("bridge add_nio_tap bridge0 tap-gns3-e0 off"),
|
||||
call.send("docker move_to_ns tap-gns3-e0 42 eth0"),
|
||||
call.send(f'bridge add_nio_ethernet bridge0 "{host_ifc}"'),
|
||||
call.send("bridge add_nio_udp bridge0 4242 127.0.0.1 4343"),
|
||||
call.send('bridge start_capture bridge0 "/tmp/capture.pcap"'),
|
||||
call.send("bridge start bridge0"),
|
||||
call.send("bridge set_nio_tap_carrier bridge0 on"),
|
||||
call.send(f'link set "{host_ifc}" up'),
|
||||
]
|
||||
assert "bridge0" in vm._bridges
|
||||
# We need to check any_order otherwise mock is confused by asyncio
|
||||
@@ -1571,11 +1574,13 @@ async def test_add_ubridge_connections_with_base_mac_address(vm):
|
||||
nio = vm.manager.create_nio(nio_params)
|
||||
await vm._add_ubridge_connection(nio, 1)
|
||||
|
||||
_, guest0 = vm._veth_names(0, 0)
|
||||
_, guest1 = vm._veth_names(1, 0)
|
||||
calls = [
|
||||
call.send("bridge create bridge0"),
|
||||
call.send("bridge create bridge1"),
|
||||
call.send("docker set_mac_addr tap-gns3-e0 02:42:42:42:42:00"),
|
||||
call.send("docker set_mac_addr tap-gns3-e0 02:42:42:42:42:01"),
|
||||
call.send(f"docker set_mac_addr {guest0} 02:42:42:42:42:00"),
|
||||
call.send(f"docker set_mac_addr {guest1} 02:42:42:42:42:01"),
|
||||
]
|
||||
|
||||
# We need to check any_order otherwise mock is confused by asyncio
|
||||
@@ -1591,12 +1596,14 @@ async def test_add_ubridge_connection_none_nio(vm):
|
||||
|
||||
await vm._add_ubridge_connection(nio, 0)
|
||||
|
||||
host_ifc, guest_ifc = vm._veth_names(0, 0)
|
||||
calls = [
|
||||
call.send("bridge create bridge0"),
|
||||
call.send("bridge add_nio_tap bridge0 tap-gns3-e0 off"),
|
||||
call.send("docker move_to_ns tap-gns3-e0 42 eth0"),
|
||||
call.send(f'docker create_veth "{host_ifc}" "{guest_ifc}"'),
|
||||
call.send(f'link set "{host_ifc}" down'),
|
||||
call.send(f"docker move_to_ns {guest_ifc} 42 eth0"),
|
||||
]
|
||||
assert "bridge0" in vm._bridges
|
||||
# unconnected adapter: interface exists (carrier off), no relay bridge
|
||||
assert "bridge0" not in vm._bridges
|
||||
# We need to check any_order ortherwise mock is confused by asyncio
|
||||
vm._ubridge_hypervisor.assert_has_calls(calls, any_order=True)
|
||||
|
||||
@@ -1626,19 +1633,6 @@ async def test_add_ubridge_connection_invalid_adapter_number(vm):
|
||||
await vm._add_ubridge_connection(nio, 12)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_add_ubridge_connection_no_free_interface(vm):
|
||||
|
||||
nio = {"type": "nio_udp", "lport": 4242, "rport": 4343, "rhost": "127.0.0.1"}
|
||||
nio = vm.manager.create_nio(nio)
|
||||
with pytest.raises(DockerError):
|
||||
# We create fake ethernet interfaces for docker
|
||||
interfaces = [f"tap-gns3-e{index}" for index in range(4096)]
|
||||
|
||||
with patch("psutil.net_if_addrs", return_value=interfaces):
|
||||
await vm._add_ubridge_connection(nio, 0)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_adapter_add_nio_binding_1(vm):
|
||||
|
||||
|
||||
@@ -314,7 +314,7 @@ async def test_move_to_ns_uses_renamed_interface(compute_project, manager):
|
||||
# adapter 0 should be renamed to mgmt0
|
||||
move_calls = [c for c in vm._ubridge_hypervisor.method_calls if "move_to_ns" in str(c)]
|
||||
assert move_calls, "move_to_ns was not sent"
|
||||
assert call.send("docker move_to_ns tap-gns3-e0 42 mgmt0") in move_calls
|
||||
assert call.send(f"docker move_to_ns {vm._veth_names(0, 0)[1]} 42 mgmt0") in move_calls
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@@ -326,7 +326,7 @@ async def test_move_to_ns_falls_back_to_eth(compute_project, manager):
|
||||
nio = manager.create_nio({"type": "nio_udp", "lport": 4242, "rport": 4343, "rhost": "127.0.0.1"})
|
||||
await vm._add_ubridge_connection(nio, 1)
|
||||
move_calls = [c for c in vm._ubridge_hypervisor.method_calls if "move_to_ns" in str(c)]
|
||||
assert call.send("docker move_to_ns tap-gns3-e0 42 eth1") in move_calls
|
||||
assert call.send(f"docker move_to_ns {vm._veth_names(1, 0)[1]} 42 eth1") in move_calls
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
Reference in new issue
Block a user