netx/netx_api/topology_schemas.py
oliver 2382cb8269 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>
2026-08-01 12:50:33 +08:00

122 lines
3 KiB
Python

"""Pydantic schemas for topology maps / nodes / edges."""
from __future__ import annotations
from datetime import datetime
from pydantic import BaseModel, Field
class TopologyMapCreate(BaseModel):
name: str = Field(min_length=1, max_length=256)
remark: str = Field(default="", max_length=1024)
class TopologyMapUpdate(BaseModel):
name: str | None = Field(default=None, min_length=1, max_length=256)
remark: str | None = Field(default=None, max_length=1024)
class TopologyMapOut(BaseModel):
id: str
name: str
remark: str
node_count: int = 0
edge_count: int = 0
created_at: datetime | None = None
updated_at: datetime | None = None
class TopologyNodeIn(BaseModel):
id: str = Field(min_length=1, max_length=64)
managed_ne_id: str = ""
ume_ne_id: str = ""
label: str = ""
x: float = 0.0
y: float = 0.0
created_at: datetime | None = None
class TopologyEdgeIn(BaseModel):
id: str = Field(min_length=1, max_length=64)
source_node_id: str = Field(min_length=1, max_length=64)
target_node_id: str = Field(min_length=1, max_length=64)
source_port: str = ""
target_port: str = ""
source: str = "manual"
stroke_color: str = Field(default="", max_length=32)
stroke_width: int = Field(default=0, ge=0, le=12)
line_style: str = Field(default="", max_length=16)
discovered_at: datetime | None = None
created_at: datetime | None = None
class TopologyNodeOut(BaseModel):
id: str
map_id: str
managed_ne_id: str = ""
ume_ne_id: str = ""
label: str = ""
x: float = 0.0
y: float = 0.0
ne_name: str = ""
ne_ip: str = ""
vendor: str = ""
protocol: str = ""
connect_status: str = ""
class TopologyEdgeOut(BaseModel):
id: str
map_id: str
source_node_id: str
target_node_id: str
source_port: str = ""
target_port: str = ""
source: str = "manual"
stroke_color: str = ""
stroke_width: int = 0
line_style: str = ""
discovered_at: datetime | None = None
class TopologyGraphOut(BaseModel):
map: TopologyMapOut
nodes: list[TopologyNodeOut]
edges: list[TopologyEdgeOut]
class TopologyGraphPut(BaseModel):
nodes: list[TopologyNodeIn] = Field(default_factory=list)
edges: list[TopologyEdgeIn] = Field(default_factory=list)
class TopologyDiscoverRequest(BaseModel):
"""Run LLDP/CDP discovery for managed NEs currently on the map."""
protocol: str = Field(default="auto", description="auto | lldp | cdp")
ne_ids: list[str] | None = None
class TopologyDiscoverNeResult(BaseModel):
ne_id: str
ne_name: str = ""
ne_ip: str = ""
ok: bool = False
command: str = ""
neighbors: int = 0
edges_added: int = 0
edges_updated: int = 0
error: str = ""
raw_preview: str = ""
class TopologyDiscoverOut(BaseModel):
map_id: str
protocol: str
scanned: int = 0
edges_added: int = 0
edges_updated: int = 0
edges_stale: int = 0
results: list[TopologyDiscoverNeResult] = Field(default_factory=list)
graph: TopologyGraphOut | None = None