fix(marker): restore markers from topology on project load

_create_link_from_topology_data now restores traffic-insight markers
(mirroring how filters are restored via update_filters), so markers —
including their color — survive project close/reopen and server restart.

Guard start_marker's uBridge POST with 'if self._created' (exactly as
update_filters guards its update() call): during project load the link
is not yet created, so only _markers state is recorded and the marker
is applied once via the NIO flow in create()/_ubridge_apply_markers —
no double application.
This commit is contained in:
YueGuobin 2026-07-12 21:57:47 +08:00
parent 3f15ac38f6
commit 88c03d1431
No known key found for this signature in database
2 changed files with 30 additions and 7 deletions

View File

@ -765,6 +765,22 @@ class Project:
"Dropping invalid filters on link %s: %s",
link_data.get("link_id"), e
)
# Restore traffic-insight markers. Each marker's capture side is resolved
# when the link is (re)created and the marker is applied to uBridge via
# _ubridge_apply_markers in add_ubridge_udp_connection.
for name, marker in (link_data.get("markers") or {}).items():
try:
await link.start_marker(
name=name,
bpf=marker["bpf"],
tag=marker.get("tag"),
color=marker.get("color"),
)
except (ControllerError, KeyError) as e:
log.warning(
"Dropping marker %s on link %s: %s",
name, link_data.get("link_id"), e
)
if "link_style" in link_data:
await link.update_link_style(link_data["link_style"])
if "show_filters_icon" in link_data:

View File

@ -355,15 +355,22 @@ class UDPLink(Link):
raise ControllerError(f"Invalid BPF expression: {result.get('error', 'unknown error')}")
marker_side = self._choose_marker_side()
data = {"name": name, "bpf": bpf, "tag": tag, "link_id": self._id}
await marker_side["node"].post(
"/adapters/{adapter_number}/ports/{port_number}/markers/start".format(
adapter_number=marker_side["adapter_number"], port_number=marker_side["port_number"]
),
data=data,
)
# Record state + runtime capture-side ref unconditionally (so stop/update
# work and the marker is persisted), but only push to uBridge when the
# link is already live. During project load the link is not yet created
# (self._created is False); the marker then rides the NIO via create()
# and is applied once by _ubridge_apply_markers — mirroring exactly how
# update_filters guards its update() call.
self._store_capture_node_for_marker(name, marker_side)
self._markers[name].update({"bpf": bpf, "tag": tag, "enabled": True, "color": color})
if self._created:
data = {"name": name, "bpf": bpf, "tag": tag, "link_id": self._id}
await marker_side["node"].post(
"/adapters/{adapter_number}/ports/{port_number}/markers/start".format(
adapter_number=marker_side["adapter_number"], port_number=marker_side["port_number"]
),
data=data,
)
self._project.emit_notification("link.updated", self.asdict())
self._project.dump()