fix(controller): don't push the synthetic suspend filter to kernel links

update_suspend() piggybacks on the filter system: get_active_filters()
returns {"frequency_drop": [-1]} while a link is suspended so relay-based
node types drop all traffic at the uBridge filter. Kernel-datapath links
implement suspend natively via interface carrier, and pushing the
synthetic filter tripped the compute-side guard, making suspend fail
with 409 on every kernel link (and leaving controller/kernel state torn).
_get_node_filters() now returns empty filters for kernel links.
This commit is contained in:
YueGuobin 2026-09-28 00:39:03 +08:00
parent 37d7b9121e
commit 58b97b4ce5
No known key found for this signature in database
2 changed files with 36 additions and 0 deletions

View File

@ -88,6 +88,13 @@ class UDPLink(Link):
:returns: Tuple of (node1_filters, node2_filters)
"""
# Kernel-datapath links implement suspend natively (interface carrier
# driven by the NIO suspend flag). The synthetic frequency_drop filter
# that get_active_filters() injects for suspended links is a
# relay-datapath emulation mechanism — sending it would trip the
# compute-side kernel guard, so kernel links always push empty filters.
if self.kernel_datapath:
return {}, {}
filter_node = self._get_filter_node()
return (
self.get_active_filters() if filter_node == node1 else {},

View File

@ -225,3 +225,32 @@ async def test_kernel_datapath_property(project):
assert link.kernel_datapath is False
await link._prepare()
assert link.kernel_datapath is True
@pytest.mark.asyncio
async def test_update_suspend_kernel_link_sends_no_synthetic_filter(project):
"""
Suspend is emulated on the relay datapath via a synthetic frequency_drop
filter (get_active_filters); kernel links implement it natively via
interface carrier and must push empty filters — the compute-side guard
rejects non-empty filters on kernel NIOs.
"""
link, node1, node2 = await _kernel_link(project)
await link._prepare()
bodies = []
async def capture_put(path, data=None, **kwargs):
bodies.append(data)
node1.put = capture_put
node2.put = capture_put
await link.update_suspend(True)
assert link._suspended is True
assert len(bodies) == 2
for body in bodies:
assert body["filters"] == {}
assert body["suspend"] is True