mirror of
https://github.com/GNS3/gns3-server.git
synced 2026-11-06 07:11:13 +02:00
refactor: share the anchor-TAP lifecycle in the kernel-datapath mixin
The persistent-TAP lifecycle — probe, sweep, create, register, delete — was implemented four times, once per node type, and the copies had drifted (probe mechanism, _tap_datapath reset, keep-vs-recreate policy). The mechanical half moves into KernelDatapathMixin: - _probe_persistent_taps(probe_name): the scratch-TAP probe, returning the refusal for the caller's own warning (QEMU, Dynamips); - _create_anchor_taps(ports, set_owner, keep_existing): sweep + create + optional ownership handover + carrier down + registry, with the ports enumerated by the node type and named through the new _tap_name hook; - _delete_anchor_taps(): the suppressed tap sweep + per-link bridge cleanup + registry clear. Each node type keeps what its fd holder dictates: the release step before the tap sweep (the QEMU process, the IOL bridge, the hypervisor's NIO bindings, the container port bridges), its probe policy, and its registry lifetime. QEMU's now-unconditional registry clear aligns it with the other three; everything else is behavior-preserving.
This commit is contained in:
5 files changed
+133
-140
No files matched your search
@@ -42,7 +42,6 @@ from ...base_node import BaseNode
|
||||
from ...error import NodeError
|
||||
from ...kernel_datapath import KernelDatapathMixin
|
||||
from ...nios.nio_bridge import NIOBridge
|
||||
from ...ubridge.ubridge_error import UbridgeError
|
||||
from ..adapters.adapter import ETHERNET_ADAPTERS, ETHERNET_WICS
|
||||
from ..dynamips_error import DynamipsError
|
||||
from ..nios.nio_tap import NIOTAP
|
||||
@@ -1648,19 +1647,15 @@ class Router(KernelDatapathMixin, BaseNode):
|
||||
# uBridge (re)started: the tc capabilities must be probed again.
|
||||
self._ubridge_tc_caps = None
|
||||
if not self._tap_datapath:
|
||||
probe = f"gd{self._id.replace('-', '')[:8]}prob"
|
||||
try:
|
||||
await self._ubridge_send(f'tap create "{probe}"')
|
||||
except UbridgeError as e:
|
||||
probe_error = await self._probe_persistent_taps(f"gd{self._id.replace('-', '')[:8]}prob")
|
||||
if probe_error is not None:
|
||||
message = (
|
||||
f"Router '{self._name}': uBridge cannot create persistent TAPs ({e}); "
|
||||
f"Router '{self._name}': uBridge cannot create persistent TAPs ({probe_error}); "
|
||||
"this node runs on the relay datapath and cannot carry kernel links"
|
||||
)
|
||||
log.warning(message)
|
||||
self.project.emit("log.warning", {"message": message})
|
||||
return
|
||||
with contextlib.suppress(UbridgeError):
|
||||
await self._ubridge_send(f'tap delete "{probe}"')
|
||||
self._tap_datapath = True
|
||||
|
||||
await self._create_taps()
|
||||
@@ -1675,31 +1670,15 @@ class Router(KernelDatapathMixin, BaseNode):
|
||||
|
||||
async def _create_taps(self):
|
||||
"""
|
||||
Create the persistent TAP every Ethernet slot/port owns. Each TAP
|
||||
starts DOWN (carrier off until a kernel link attaches); uBridge
|
||||
creates the device and hands ownership to this user, so the
|
||||
unprivileged Dynamips hypervisor can open it. A port whose anchor
|
||||
already exists (a restart, or an adapter hot-added to a running
|
||||
router) keeps it — an anchor is never recreated under a live link.
|
||||
A persistent TAP outlives its creator, so a leftover from a previous
|
||||
run (crash, kill) is swept first.
|
||||
Create the persistent TAP every Ethernet slot/port owns (WIC ports
|
||||
included), handed to this user so the unprivileged Dynamips
|
||||
hypervisor can open the device. Unlike QEMU's, these anchors outlive
|
||||
a node stop — the hypervisor keeps its NIO bindings — so a port that
|
||||
already holds one keeps it: an anchor is never recreated under a
|
||||
live link (a restart, or an adapter hot-added to a running router).
|
||||
"""
|
||||
|
||||
for slot_number, port_number in self._ethernet_slot_ports():
|
||||
if (slot_number, port_number) in self._kernel_taps:
|
||||
continue
|
||||
tap = self._tap_name(slot_number, port_number)
|
||||
with contextlib.suppress(UbridgeError):
|
||||
await self._ubridge_send(f'tap delete "{tap}"')
|
||||
await self._ubridge_send(f'tap create "{tap}"')
|
||||
try:
|
||||
await self._ubridge_send(f"tap set_owner {tap} {os.getuid()}")
|
||||
except UbridgeError as e:
|
||||
# Only root could open the TAP now: the hypervisor would fail
|
||||
# to bind its NIO, which is worth surfacing at that point.
|
||||
log.warning("Router '%s': could not hand TAP %s to uid %s: %s", self._name, tap, os.getuid(), e)
|
||||
await self._ubridge_send(f'link set "{tap}" down')
|
||||
self._kernel_taps[(slot_number, port_number)] = tap
|
||||
await self._create_anchor_taps(self._ethernet_slot_ports(), set_owner=True, keep_existing=True)
|
||||
|
||||
async def _attach_kernel_nio(self, slot_number, port_number, nio):
|
||||
"""
|
||||
@@ -1792,11 +1771,7 @@ class Router(KernelDatapathMixin, BaseNode):
|
||||
with contextlib.suppress(DynamipsError, OSError):
|
||||
await tap_nio.delete()
|
||||
self._tap_nios.clear()
|
||||
for tap in self._kernel_taps.values():
|
||||
with contextlib.suppress(UbridgeError):
|
||||
await self._ubridge_send(f'tap delete "{tap}"')
|
||||
await self._remove_kernel_bridges()
|
||||
self._kernel_taps.clear()
|
||||
await self._delete_anchor_taps()
|
||||
self._ubridge_tc_caps = None
|
||||
await super()._stop_ubridge()
|
||||
|
||||
|
||||
Reference in new issue
Block a user