mirror of
https://github.com/hansjone/netx.git
synced 2026-10-09 02:00:46 +08:00
Project discover neighbors from scanned seeds only.
Accept optional seed ids on project-neighbors; canvas discover passes scanned fabric nodes and places new peers near them. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
7cd89aaae9
commit
03ef9eb39a
9 changed files with 137 additions and 18 deletions
|
|
@ -43,6 +43,7 @@ from .topology_schemas import (
|
|||
ViewNodesRemove,
|
||||
ViewPopulateRequest,
|
||||
ViewPositionsPatch,
|
||||
ViewProjectNeighborsRequest,
|
||||
)
|
||||
from .topology_discover import get_discover_job, start_discover_job
|
||||
from .topology_fabric import (
|
||||
|
|
@ -319,8 +320,14 @@ def api_create_topology_placeholder(
|
|||
|
||||
|
||||
@router.post("/views/{view_id}/project-neighbors")
|
||||
def api_project_neighbors(view_id: str, db: Session = Depends(get_db)) -> dict[str, Any]:
|
||||
return project_fabric_neighbors_to_view(db, view_id).model_dump()
|
||||
def api_project_neighbors(
|
||||
view_id: str,
|
||||
body: ViewProjectNeighborsRequest | None = None,
|
||||
db: Session = Depends(get_db),
|
||||
) -> dict[str, Any]:
|
||||
return project_fabric_neighbors_to_view(
|
||||
db, view_id, body or ViewProjectNeighborsRequest()
|
||||
).model_dump()
|
||||
|
||||
|
||||
@router.post("/views/{view_id}/populate")
|
||||
|
|
|
|||
|
|
@ -238,6 +238,18 @@ class ViewPopulateRequest(BaseModel):
|
|||
freeze_after: bool = True
|
||||
|
||||
|
||||
class ViewProjectNeighborsRequest(BaseModel):
|
||||
"""Project fabric neighbors onto a view.
|
||||
|
||||
When seed ids are omitted, expand from every node already on the canvas.
|
||||
When provided, expand only from those seeds (resolved to fabric nodes that
|
||||
are already placed on the view).
|
||||
"""
|
||||
|
||||
seed_fabric_node_ids: list[str] = Field(default_factory=list)
|
||||
managed_ne_ids: list[str] = Field(default_factory=list)
|
||||
|
||||
|
||||
class ViewNodeIn(BaseModel):
|
||||
fabric_node_id: str = Field(min_length=1, max_length=64)
|
||||
x: float = 0.0
|
||||
|
|
|
|||
|
|
@ -70,6 +70,7 @@ from .topology_schemas import (
|
|||
ViewPopulateOut,
|
||||
ViewPopulateRequest,
|
||||
ViewPositionsPatch,
|
||||
ViewProjectNeighborsRequest,
|
||||
)
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
|
@ -288,18 +289,31 @@ def _place_fabric_ids_on_view(
|
|||
fabric_ids: list[str],
|
||||
*,
|
||||
existing: set[str],
|
||||
near_fabric_ids: set[str] | None = None,
|
||||
) -> int:
|
||||
now = _utcnow()
|
||||
added = 0
|
||||
vnodes = db.query(TopoViewNode).filter(TopoViewNode.view_id == view.id).all()
|
||||
max_x = max((float(vn.x or 0) for vn in vnodes), default=40.0)
|
||||
base_x = max_x + 200.0
|
||||
anchor = None
|
||||
near = {str(x).strip() for x in (near_fabric_ids or set()) if str(x).strip()}
|
||||
if near:
|
||||
for vn in vnodes:
|
||||
if vn.fabric_node_id in near:
|
||||
anchor = vn
|
||||
break
|
||||
if anchor is not None:
|
||||
base_x = float(anchor.x or 0.0) + 200.0
|
||||
base_y = float(anchor.y or 0.0)
|
||||
else:
|
||||
max_x = max((float(vn.x or 0) for vn in vnodes), default=40.0)
|
||||
base_x = max_x + 200.0
|
||||
base_y = 40.0
|
||||
cols = max(1, int(len(fabric_ids) ** 0.5) or 1)
|
||||
for i, fid in enumerate(fabric_ids):
|
||||
if fid in existing or db.get(TopoFabricNode, fid) is None:
|
||||
continue
|
||||
x = base_x + (i % cols) * 180.0
|
||||
y = 40.0 + (i // cols) * 120.0
|
||||
y = base_y + (i // cols) * 120.0
|
||||
db.add(
|
||||
TopoViewNode(
|
||||
id=uuid4().hex,
|
||||
|
|
@ -897,8 +911,16 @@ def _neighbor_ids(
|
|||
return found
|
||||
|
||||
|
||||
def project_fabric_neighbors_to_view(db: Session, view_id: str) -> TopologyViewGraphOut:
|
||||
"""Add in-scope fabric neighbors onto the leaf view (bounded by membership)."""
|
||||
def project_fabric_neighbors_to_view(
|
||||
db: Session,
|
||||
view_id: str,
|
||||
body: ViewProjectNeighborsRequest | None = None,
|
||||
) -> TopologyViewGraphOut:
|
||||
"""Add in-scope fabric neighbors onto the leaf view (bounded by membership).
|
||||
|
||||
Optional seeds limit expansion to neighbors of those fabric nodes (must
|
||||
already be on the view). Empty seeds → expand from every canvas node.
|
||||
"""
|
||||
merge_duplicate_fabric_nodes(db)
|
||||
view = _get_view_or_404(db, view_id)
|
||||
mem = _membership_for_view(view)
|
||||
|
|
@ -909,6 +931,7 @@ def project_fabric_neighbors_to_view(db: Session, view_id: str) -> TopologyViewG
|
|||
hops = int(mem.get("expand_hops") or 1)
|
||||
filt = dict(view.filter or {})
|
||||
layer = str(filt.get("layer") or "physical").strip() or "physical"
|
||||
req = body or ViewProjectNeighborsRequest()
|
||||
|
||||
vnodes = db.query(TopoViewNode).filter(TopoViewNode.view_id == view.id).all()
|
||||
# Drop placements pointing at missing fabric rows only (keep LLDP placeholders).
|
||||
|
|
@ -929,7 +952,25 @@ def project_fabric_neighbors_to_view(db: Session, view_id: str) -> TopologyViewG
|
|||
g.truncate_reason = g.truncate_reason or "membership_cap"
|
||||
return g
|
||||
|
||||
peer_ids = _neighbor_ids(db, seed_ids=existing, layer=layer, hops=hops)
|
||||
seed_ids: set[str] = {
|
||||
str(x).strip() for x in (req.seed_fabric_node_ids or []) if str(x).strip()
|
||||
}
|
||||
for mid in req.managed_ne_ids or []:
|
||||
mid_s = str(mid or "").strip()
|
||||
if not mid_s:
|
||||
continue
|
||||
for fid in existing:
|
||||
fn = db.get(TopoFabricNode, fid)
|
||||
if fn is not None and str(fn.managed_ne_id or "").strip() == mid_s:
|
||||
seed_ids.add(fid)
|
||||
if seed_ids:
|
||||
seed_ids &= existing
|
||||
if not seed_ids:
|
||||
return get_view_graph(db, view.id)
|
||||
else:
|
||||
seed_ids = set(existing)
|
||||
|
||||
peer_ids = _neighbor_ids(db, seed_ids=seed_ids, layer=layer, hops=hops)
|
||||
to_add: list[str] = []
|
||||
for peer in sorted(peer_ids):
|
||||
if peer in existing:
|
||||
|
|
@ -947,7 +988,9 @@ def project_fabric_neighbors_to_view(db: Session, view_id: str) -> TopologyViewG
|
|||
|
||||
truncated = len(peer_ids) > len(to_add)
|
||||
if to_add:
|
||||
_place_fabric_ids_on_view(db, view, to_add, existing=existing)
|
||||
_place_fabric_ids_on_view(
|
||||
db, view, to_add, existing=existing, near_fabric_ids=seed_ids
|
||||
)
|
||||
db.commit()
|
||||
g = get_view_graph(db, view.id)
|
||||
if truncated:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue