feat(marker): add project-level marker definition inheritance

Project-level marker definitions fan out to every link (existing and new).
A definition is stored once on Project._marker_definitions; when applied to
a link the marker is named "global-{def_name}" — the "global" prefix was
pre-reserved in the schema, so inherited and per-link markers can never
collide, nor will their registry keys.

Key behavior:
- POST /projects/{pid}/marker-definitions → fan out to all existing links
- PUT  /projects/{pid}/marker-definitions/{name} → sync all inherited copies
- DELETE → remove every inherited copy from every link
- New links auto-inherit all active defs (hook in UDPLink.create)
- Per-link DELETE/PUT of a "global-*" marker is rejected (409)
- Inherited markers are NOT persisted in the topology; they are re-created
  from _marker_definitions on project load
- Compute side is untouched — the marker reaches uBridge via the existing
  start_marker→update→NIO→ubridge pipeline

Files:
- controller/project.py — _marker_definitions + CRUD + fanout + topology load
- controller/link.py — Link.inherit_marker() + asdict() filter
- controller/udp_link.py — guards on stop/update + create() inheritance hook
- controller/topology.py — persist marker_definitions in project topology
- schemas/controller/links.py — MarkerDefinitionCreate schema
- api/routes/controller/projects.py — REST endpoints (marker-definitions)
This commit is contained in:
YueGuobin committed 2026-07-16 00:39:52 +08:00
1 parent 369badc6c2
commit 31991fe359
6 files changed
+291 -4

No files matched your search

+23
View File
@@ -168,3 +168,26 @@ class MarkerCreate(BaseModel):
)
class MarkerDefinitionCreate(BaseModel):
"""
Body for creating / updating a project-level marker definition.
The definition is a template — when applied to a link the marker name is
prefixed with ``global-`` (e.g. ``arp`` → ``global-arp``) so it can never
collide with a per-link private marker.
"""
name: Optional[str] = Field(
None,
pattern=r"^(?i)(?!global)[A-Za-z0-9][A-Za-z0-9_.-]*$",
max_length=128,
description="Unique definition name. Auto-generated when absent.",
)
bpf: str
tag: Optional[int] = None
color: Optional[str] = Field(
None,
description="User-chosen hex color for the marker in the Web UI, e.g. '#ff5722'",
)