From cdc27c37a7d17aceb3eab58c8a620daf78511a4b Mon Sep 17 00:00:00 2001 From: YueGuobin Date: Mon, 13 Jul 2026 23:32:42 +0800 Subject: [PATCH] fix(marker): clean inherited markers on def delete delete_marker_definition removed the def but left the inherited global-* copies on every link: it called stop_marker(), which rejects inherited markers (409), and the ControllerError was swallowed as a warning. The orphaned copies were in-memory only (_persist_markers filters inherited markers) so a restart hid the symptom, but during a running session they were undeletable via either the per-link or project API. Add the same inherited=True bypass that update_marker already has, and pass it from the def-delete fan-out so the copies are removed for real. --- gns3server/controller/project.py | 2 +- gns3server/controller/udp_link.py | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/gns3server/controller/project.py b/gns3server/controller/project.py index 2d1671f31..9ada6506a 100644 --- a/gns3server/controller/project.py +++ b/gns3server/controller/project.py @@ -994,7 +994,7 @@ class Project: marker_name = f"global-{name}" if marker_name in link.markers and link.markers[marker_name].get("inherited_from") == name: try: - await link.stop_marker(marker_name) + await link.stop_marker(marker_name, inherited=True) except ControllerError: # A missing compute or broken link shouldn't block the delete. log.warning( diff --git a/gns3server/controller/udp_link.py b/gns3server/controller/udp_link.py index de797b77f..609c17495 100644 --- a/gns3server/controller/udp_link.py +++ b/gns3server/controller/udp_link.py @@ -367,7 +367,7 @@ class UDPLink(Link): self._project.emit_notification("link.updated", self.asdict()) self._project.dump() - async def stop_marker(self, name): + async def stop_marker(self, name, inherited=False): """ Remove a traffic-insight marker from this link. @@ -376,12 +376,14 @@ class UDPLink(Link): drops it from uBridge. Mirrors how deleting a packet filter works. :param name: filter name to remove + :param inherited: set by project-level def-delete to bypass the + inheritance guard (the project layer is the legitimate remover) """ if name not in self._markers: raise ControllerNotFoundError(f"Marker '{name}' not found on link {self._id}") - if self._markers[name].get("inherited_from"): + if self._markers[name].get("inherited_from") and not inherited: raise ControllerError( f"Marker '{name}' is inherited from the project-level " f"definition '{self._markers[name]['inherited_from']}'. "