mirror of
https://github.com/hansjone/netx.git
synced 2026-10-09 02:00:46 +08:00
Harden topology canvas edge delete, discover cancel, and map UX.
Persist Fabric edge deletes, stop LLDP jobs from the canvas, surface truncation/placeholders/stale edges, and fix LLDP job deep-links. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
5e567408e7
commit
617d4245b0
14 changed files with 468 additions and 238 deletions
|
|
@ -4,6 +4,8 @@ from __future__ import annotations
|
|||
from .topology_fabric_links import (
|
||||
_apply_missing_and_purge,
|
||||
_mark_replaced_port_peers,
|
||||
delete_fabric_edge,
|
||||
delete_fabric_edges,
|
||||
merge_duplicate_fabric_nodes,
|
||||
upsert_fabric_edge,
|
||||
)
|
||||
|
|
@ -37,6 +39,8 @@ __all__ = [
|
|||
"_match_hit_to_fabric_node",
|
||||
"_node_out",
|
||||
"_nodes_by_ids",
|
||||
"delete_fabric_edge",
|
||||
"delete_fabric_edges",
|
||||
"ensure_fabric_node_for_managed",
|
||||
"ensure_fabric_node_for_ume",
|
||||
"ensure_lldp_discovered_managed_ne",
|
||||
|
|
|
|||
|
|
@ -511,4 +511,39 @@ def merge_duplicate_fabric_nodes(db: Session) -> dict[str, int]:
|
|||
return {"merged": merged, "placeholders_removed": placeholders_removed}
|
||||
|
||||
|
||||
def delete_fabric_edges(db: Session, edge_ids: list[str]) -> dict[str, int]:
|
||||
"""Hard-delete fabric edges and any per-view edge styles."""
|
||||
ids = [str(x).strip() for x in (edge_ids or []) if str(x).strip()]
|
||||
# De-dupe while preserving order.
|
||||
seen: set[str] = set()
|
||||
ordered: list[str] = []
|
||||
for i in ids:
|
||||
if i in seen:
|
||||
continue
|
||||
seen.add(i)
|
||||
ordered.append(i)
|
||||
if not ordered:
|
||||
raise HTTPException(status_code=400, detail="edge_ids_required")
|
||||
rows = db.query(TopoFabricEdge).filter(TopoFabricEdge.id.in_(ordered)).all()
|
||||
found_ids = [str(r.id) for r in rows]
|
||||
if not found_ids:
|
||||
raise HTTPException(status_code=404, detail="fabric_edge_not_found")
|
||||
db.query(TopoViewEdgeStyle).filter(
|
||||
TopoViewEdgeStyle.fabric_edge_id.in_(found_ids)
|
||||
).delete(synchronize_session=False)
|
||||
db.query(TopoFabricEdge).filter(TopoFabricEdge.id.in_(found_ids)).delete(
|
||||
synchronize_session=False
|
||||
)
|
||||
db.commit()
|
||||
try:
|
||||
refresh_fabric_stats(db)
|
||||
except Exception: # noqa: BLE001
|
||||
pass
|
||||
return {"deleted": len(found_ids)}
|
||||
|
||||
|
||||
def delete_fabric_edge(db: Session, edge_id: str) -> dict[str, int]:
|
||||
return delete_fabric_edges(db, [edge_id])
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ from .topology_schemas import (
|
|||
ClassifyRuleCreate,
|
||||
ClassifyRuleUpdate,
|
||||
FabricDiscoverRequest,
|
||||
FabricEdgesDeleteRequest,
|
||||
FabricManualEdgeIn,
|
||||
FabricNodesBulkTagRequest,
|
||||
FabricNodesDeleteRequest,
|
||||
|
|
@ -45,6 +46,8 @@ from .topology_schemas import (
|
|||
)
|
||||
from .topology_discover import get_discover_job, start_discover_job
|
||||
from .topology_fabric import (
|
||||
delete_fabric_edge,
|
||||
delete_fabric_edges,
|
||||
get_fabric_neighborhood,
|
||||
get_fabric_summary,
|
||||
list_fabric_edges,
|
||||
|
|
@ -175,6 +178,21 @@ def api_fabric_manual_edge(
|
|||
}
|
||||
|
||||
|
||||
@router.delete("/fabric/edges/{edge_id}")
|
||||
def api_fabric_delete_edge(edge_id: str, db: Session = Depends(get_db)) -> dict[str, Any]:
|
||||
out = delete_fabric_edge(db, edge_id)
|
||||
return {"ok": True, **out}
|
||||
|
||||
|
||||
@router.post("/fabric/edges/delete")
|
||||
def api_fabric_delete_edges(
|
||||
body: FabricEdgesDeleteRequest,
|
||||
db: Session = Depends(get_db),
|
||||
) -> dict[str, Any]:
|
||||
out = delete_fabric_edges(db, body.edge_ids)
|
||||
return {"ok": True, **out}
|
||||
|
||||
|
||||
@router.post("/fabric/discover")
|
||||
def api_fabric_discover(
|
||||
body: FabricDiscoverRequest | None = None,
|
||||
|
|
|
|||
|
|
@ -259,6 +259,7 @@ class ViewNodeOut(BaseModel):
|
|||
vendor: str = ""
|
||||
device_type: str = ""
|
||||
connect_status: str = ""
|
||||
managed_source: str = "" # manual | ume_sync | lldp | topology | webcrt | …
|
||||
|
||||
|
||||
class ViewEdgeOut(BaseModel):
|
||||
|
|
@ -390,6 +391,14 @@ class FabricManualEdgeIn(BaseModel):
|
|||
b_port: str = ""
|
||||
|
||||
|
||||
class FabricEdgesDeleteRequest(BaseModel):
|
||||
edge_ids: list[str] = Field(default_factory=list, min_length=1)
|
||||
|
||||
|
||||
class FabricEdgesDeleteOut(BaseModel):
|
||||
deleted: int = 0
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Classify rules + slices + search
|
||||
# ---------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -21,6 +21,8 @@ from .topology_fabric import (
|
|||
_mark_replaced_port_peers,
|
||||
_match_hit_to_fabric_node,
|
||||
_node_out,
|
||||
delete_fabric_edge,
|
||||
delete_fabric_edges,
|
||||
ensure_fabric_node_for_managed,
|
||||
ensure_fabric_node_for_ume,
|
||||
ensure_lldp_discovered_managed_ne,
|
||||
|
|
@ -68,6 +70,8 @@ __all__ = [
|
|||
"create_folder",
|
||||
"create_topology_placeholder_on_view",
|
||||
"create_view",
|
||||
"delete_fabric_edge",
|
||||
"delete_fabric_edges",
|
||||
"delete_folder",
|
||||
"delete_view",
|
||||
"ensure_fabric_node_for_managed",
|
||||
|
|
|
|||
|
|
@ -99,6 +99,14 @@ def _connect_status_for_node(db: Session, n: TopoFabricNode) -> str:
|
|||
return ""
|
||||
|
||||
|
||||
def _managed_source_for_node(db: Session, n: TopoFabricNode) -> str:
|
||||
if n.managed_ne_id:
|
||||
ne = db.get(ManagedNE, n.managed_ne_id)
|
||||
if ne is not None:
|
||||
return str(ne.source or "").strip()
|
||||
return ""
|
||||
|
||||
|
||||
def get_view_graph(db: Session, view_id: str) -> TopologyViewGraphOut:
|
||||
view = _get_view_or_404(db, view_id)
|
||||
vnodes = db.query(TopoViewNode).filter(TopoViewNode.view_id == view.id).all()
|
||||
|
|
@ -135,6 +143,7 @@ def get_view_graph(db: Session, view_id: str) -> TopologyViewGraphOut:
|
|||
vendor=(fn.vendor if fn else "") or "",
|
||||
device_type=(fn.device_type if fn else "") or "",
|
||||
connect_status=_connect_status_for_node(db, fn) if fn else "",
|
||||
managed_source=_managed_source_for_node(db, fn) if fn else "",
|
||||
)
|
||||
)
|
||||
edges_out: list[ViewEdgeOut] = []
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue