Harden topology editor save/discover flow and edge editing.

Guard unsaved changes, cancel discovery, block invalid links, preserve discovered_at on PUT, and normalize interface names for discovery keys.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
oliver 2026-08-01 12:50:33 +08:00
parent 99fce837f0
commit 2382cb8269
11 changed files with 416 additions and 128 deletions

View file

@ -550,3 +550,51 @@ def _kv(text: str, pattern: str) -> str:
if not m:
return ""
return str(m.group(1) or "").strip()
# Long media names → short canonical form (case-insensitive prefix).
_IFNAME_PREFIXES: tuple[tuple[str, str], ...] = (
("tengigabitethernet", "te"),
("ten-gigabitethernet", "te"),
("gigabitethernet", "gi"),
("fastethernet", "fa"),
("ethernet", "eth"),
("xgigabitethernet", "xge"),
("100ge", "100ge"),
("40ge", "40ge"),
("25ge", "25ge"),
("10ge", "10ge"),
("ge-trunk", "ge-trunk"),
("eth-trunk", "eth-trunk"),
("port-channel", "po"),
("portchannel", "po"),
("loopback", "lo"),
("vlanif", "vlanif"),
("vlan", "vlan"),
("mgmteth", "mgmt"),
("management", "mgmt"),
("hundredgige", "hu"),
("fiftygige", "fi"),
("fortygige", "fo"),
("twentyfivegige", "twe"),
("twogigabitethernet", "tw"),
)
def normalize_ifname(name: str) -> str:
"""Canonicalize interface names so Gi0/0 and GigabitEthernet0/0 share a key."""
raw = str(name or "").strip()
if not raw:
return ""
s = re.sub(r"\s+", "", raw).lower()
s = s.replace("_", "/")
for long, short in _IFNAME_PREFIXES:
if s.startswith(long):
rest = s[len(long) :]
if rest.startswith((":", "/", "-")) or rest == "" or rest[0].isdigit():
if rest.startswith(":"):
rest = rest[1:]
return f"{short}{rest}"
break
# Already-short forms: gi0/0, te1/0/1, xge0/0/1, 10ge1/0/1
return s