diff --git a/gns3server/api/routes/controller/links.py b/gns3server/api/routes/controller/links.py index bb723927a..ec5e4f58a 100644 --- a/gns3server/api/routes/controller/links.py +++ b/gns3server/api/routes/controller/links.py @@ -454,12 +454,14 @@ async def create_marker( Required privilege: Link.Modify """ + name = marker_data.name or f"marker-{link.id[:8]}" await link.start_marker( - name=marker_data.name or f"marker-{link.id[:8]}", + name=name, bpf=marker_data.bpf, tag=marker_data.tag, + color=marker_data.color, ) - return link.markers.get(marker_data.name, {}) + return link.markers.get(name, {}) @router.delete( @@ -499,6 +501,7 @@ async def update_marker( name=marker_name, bpf=marker_data.bpf if marker_data.bpf else None, tag=marker_data.tag, + color=marker_data.color, ) return link.markers.get(marker_name, {}) diff --git a/gns3server/controller/udp_link.py b/gns3server/controller/udp_link.py index 73f006cea..0ef0ea9ac 100644 --- a/gns3server/controller/udp_link.py +++ b/gns3server/controller/udp_link.py @@ -335,13 +335,15 @@ class UDPLink(Link): } self._marker_capture_nodes[name] = capture_side - async def start_marker(self, name, bpf, tag=None): + async def start_marker(self, name, bpf, tag=None, color=None): """ Attach a traffic-insight marker to this link. :param name: stable filter name — echoed in MARK signals + pcap identity :param bpf: libpcap BPF expression :param tag: optional correlation id + :param color: optional hex color for the Web UI (e.g. '#ff5722'); stored + with the link and persisted in the topology, never sent to uBridge """ if name in self._markers: @@ -361,7 +363,7 @@ class UDPLink(Link): data=data, ) self._store_capture_node_for_marker(name, marker_side) - self._markers[name].update({"bpf": bpf, "tag": tag, "enabled": True}) + self._markers[name].update({"bpf": bpf, "tag": tag, "enabled": True, "color": color}) self._project.emit_notification("link.updated", self.asdict()) self._project.dump() @@ -389,7 +391,7 @@ class UDPLink(Link): self._project.emit_notification("link.updated", self.asdict()) self._project.dump() - async def update_marker(self, name, bpf=None, tag=None, enabled=None): + async def update_marker(self, name, bpf=None, tag=None, enabled=None, color=None): """ Update an existing marker. A BPF change requires delete+re-add so the ubridge side flushes the pcap and the new filter takes effect. @@ -398,6 +400,7 @@ class UDPLink(Link): :param bpf: new BPF expression (None = keep existing) :param tag: new tag id (None = keep existing) :param enabled: toggle (None = keep existing) + :param color: new hex color (None = keep existing) """ marker_info = self._markers.get(name) @@ -407,11 +410,15 @@ class UDPLink(Link): new_bpf = bpf if bpf is not None else marker_info["bpf"] new_tag = tag if tag is not None else marker_info.get("tag") new_enabled = enabled if enabled is not None else marker_info.get("enabled", True) + new_color = color if color is not None else marker_info.get("color") if not new_enabled and marker_info.get("enabled", True): # Toggle off: remove from ubridge but keep state. await self.stop_marker(name) - self._markers[name] = {**marker_info, "bpf": new_bpf, "tag": new_tag, "enabled": False} + self._markers[name] = { + **marker_info, "bpf": new_bpf, "tag": new_tag, + "enabled": False, "color": new_color, + } self._project.emit_notification("link.updated", self.asdict()) self._project.dump() return @@ -441,7 +448,13 @@ class UDPLink(Link): ), data=data, ) - self._markers[name] = {**marker_info, "bpf": new_bpf, "tag": new_tag, "enabled": True} + self._markers[name] = { + **marker_info, "bpf": new_bpf, "tag": new_tag, + "enabled": True, "color": new_color, + } + elif new_color != marker_info.get("color"): + # Color-only change: no ubridge round-trip, just update state. + self._markers[name] = {**marker_info, "color": new_color} self._project.emit_notification("link.updated", self.asdict()) self._project.dump() diff --git a/gns3server/schemas/controller/links.py b/gns3server/schemas/controller/links.py index 7a0676e04..b21afae04 100644 --- a/gns3server/schemas/controller/links.py +++ b/gns3server/schemas/controller/links.py @@ -153,6 +153,10 @@ class MarkerCreate(BaseModel): bpf: str tag: Optional[int] = None link_id: Optional[str] = None + color: Optional[str] = Field( + None, + description="User-chosen hex color for this marker in the Web UI, e.g. '#ff5722'" + ) class MarkerDelete(BaseModel):