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:
YueGuobin committed 2026-10-06 21:17:50 +08:00
1 parent 35cf964882
commit 1542a222f4
5 files changed
+133 -140

No files matched your search

+8 -24
View File
@@ -489,25 +489,13 @@ class IOLDockerVM(VendorDockerVM):
return
self._tap_datapath = True
for adapter_number in range(0, len(self._ethernet_adapters)):
for port_number in range(0, self._ethernet_adapters[adapter_number].interfaces):
tap = self._tap_name(adapter_number, port_number)
# A persistent TAP outlives a crash: sweep any leftover of a
# previous run before recreating it (a surviving device would
# also keep its stale tc qdisc).
with contextlib.suppress(UbridgeError):
await self._ubridge_send(f'tap delete "{tap}"')
await self._ubridge_send(f'tap create "{tap}"')
# Carrier off until a link attaches (the anchor contract).
await self._ubridge_send(f'link set "{tap}" down')
self._kernel_taps[(adapter_number, port_number)] = tap
log.debug(
"IOL container '%s': anchor TAP %s created for bay %d unit %d",
self._name,
tap,
adapter_number,
port_number,
)
# Every bay/unit anchors. No set_owner: the container port bridges
# hold these fds, and only while a kernel link is attached.
await self._create_anchor_taps(
(adapter_number, port_number)
for adapter_number in range(0, len(self._ethernet_adapters))
for port_number in range(0, self._ethernet_adapters[adapter_number].interfaces)
)
async def _ensure_anchor(self, adapter_number, port_number):
"""
@@ -650,11 +638,7 @@ class IOLDockerVM(VendorDockerVM):
for port_number in range(0, adapter.interfaces):
with contextlib.suppress(UbridgeError):
await self._ubridge_send(f"bridge delete {self._bridge_name(adapter_number, port_number)}")
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._tap_datapath = False
self._ubridge_tc_caps = None
await super()._stop_ubridge()