mirror of
https://github.com/GNS3/gns3-server.git
synced 2026-11-03 05:51:15 +02:00
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:
6 files changed
+291
-4
No files matched your search
@@ -218,6 +218,100 @@ def get_project_markers(project: Project = Depends(dep_project)) -> dict:
|
||||
return project.markers
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Project-level marker definitions (global rules inherited by every link)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@router.get(
|
||||
"/{project_id}/marker-definitions",
|
||||
dependencies=[Depends(has_privilege("Project.Audit"))]
|
||||
)
|
||||
def get_marker_definitions(project: Project = Depends(dep_project)) -> dict:
|
||||
"""
|
||||
Return all project-level marker definitions with their bound link IDs.
|
||||
|
||||
Required privilege: Project.Audit
|
||||
"""
|
||||
|
||||
result = {}
|
||||
for name, d in project.marker_definitions.items():
|
||||
# Collect which links currently carry an inherited copy.
|
||||
bound = [
|
||||
lid for lid, link in project.links.items()
|
||||
if f"global-{name}" in link.markers
|
||||
and link.markers[f"global-{name}"].get("inherited_from") == name
|
||||
]
|
||||
result[name] = {**d, "link_ids": bound}
|
||||
return result
|
||||
|
||||
|
||||
@router.post(
|
||||
"/{project_id}/marker-definitions",
|
||||
status_code=status.HTTP_201_CREATED,
|
||||
dependencies=[Depends(has_privilege("Project.Modify"))]
|
||||
)
|
||||
async def create_marker_definition(
|
||||
def_data: schemas.MarkerDefinitionCreate,
|
||||
project: Project = Depends(dep_project)
|
||||
) -> dict:
|
||||
"""
|
||||
Create a project-level marker definition and fan out to every link.
|
||||
|
||||
Required privilege: Project.Modify
|
||||
"""
|
||||
|
||||
name = def_data.name or f"def-{project.id[:8]}"
|
||||
await project.create_marker_definition(
|
||||
name=name,
|
||||
bpf=def_data.bpf,
|
||||
tag=def_data.tag,
|
||||
color=def_data.color,
|
||||
)
|
||||
return project.marker_definitions.get(name, {})
|
||||
|
||||
|
||||
@router.put(
|
||||
"/{project_id}/marker-definitions/{def_name}",
|
||||
dependencies=[Depends(has_privilege("Project.Modify"))]
|
||||
)
|
||||
async def update_marker_definition(
|
||||
def_name: str,
|
||||
def_data: schemas.MarkerDefinitionCreate,
|
||||
project: Project = Depends(dep_project)
|
||||
) -> dict:
|
||||
"""
|
||||
Update a marker definition and sync all inherited copies on every link.
|
||||
|
||||
Required privilege: Project.Modify
|
||||
"""
|
||||
|
||||
await project.update_marker_definition(
|
||||
name=def_name,
|
||||
bpf=def_data.bpf if def_data.bpf else None,
|
||||
tag=def_data.tag,
|
||||
color=def_data.color,
|
||||
)
|
||||
return project.marker_definitions.get(def_name, {})
|
||||
|
||||
|
||||
@router.delete(
|
||||
"/{project_id}/marker-definitions/{def_name}",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
dependencies=[Depends(has_privilege("Project.Modify"))]
|
||||
)
|
||||
async def delete_marker_definition(
|
||||
def_name: str,
|
||||
project: Project = Depends(dep_project)
|
||||
) -> None:
|
||||
"""
|
||||
Delete a marker definition and remove all inherited copies from every link.
|
||||
|
||||
Required privilege: Project.Modify
|
||||
"""
|
||||
|
||||
await project.delete_marker_definition(def_name)
|
||||
|
||||
|
||||
@router.post(
|
||||
"/{project_id}/close",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
|
||||
Reference in new issue
Block a user