mirror of
https://github.com/hansjone/netx.git
synced 2026-10-10 22:03:22 +08:00
Skip LLDP placeholder NEs from discovery until credentials are filled.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
90bd323115
commit
2cbdc896f6
2 changed files with 38 additions and 31 deletions
|
|
@ -7,6 +7,7 @@ from fastapi import HTTPException
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
|
|
||||||
from .cli_resolve import get_default_profile, infer_device_type_vendor
|
from .cli_resolve import get_default_profile, infer_device_type_vendor
|
||||||
|
from .device_types import LLDP_DISCOVERED_NE_SOURCE
|
||||||
from .models import ManagedNE, TopoDiscoverJob, TopoDiscoverJobItem, UmeInventoryNE
|
from .models import ManagedNE, TopoDiscoverJob, TopoDiscoverJobItem, UmeInventoryNE
|
||||||
from .topology_common import _RAW_PREVIEW_MAX
|
from .topology_common import _RAW_PREVIEW_MAX
|
||||||
from .topology_schemas import (
|
from .topology_schemas import (
|
||||||
|
|
@ -24,6 +25,22 @@ def _raw_preview(raw: str, *, limit: int = _RAW_PREVIEW_MAX) -> str:
|
||||||
return f"{text[:limit]}\n...[truncated preview {limit}/{len(text)} chars]"
|
return f"{text[:limit]}\n...[truncated preview {limit}/{len(text)} chars]"
|
||||||
|
|
||||||
|
|
||||||
|
def _is_lldp_placeholder_ne(ne: ManagedNE) -> bool:
|
||||||
|
"""LLDP SSH placeholders have no credentials until the operator promotes them."""
|
||||||
|
return str(ne.source or "").strip().lower() in {LLDP_DISCOVERED_NE_SOURCE, "lldp"}
|
||||||
|
|
||||||
|
|
||||||
|
def _managed_target_dict(ne: ManagedNE) -> dict[str, str]:
|
||||||
|
return {
|
||||||
|
"ne_id": ne.id,
|
||||||
|
"ume_ne_id": "",
|
||||||
|
"ne_name": ne.name or "",
|
||||||
|
"ne_ip": ne.ip_address or "",
|
||||||
|
"vendor": ne.vendor or "",
|
||||||
|
"device_type": ne.device_type or "",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def _job_out(
|
def _job_out(
|
||||||
db: Session,
|
db: Session,
|
||||||
job: TopoDiscoverJob,
|
job: TopoDiscoverJob,
|
||||||
|
|
@ -138,21 +155,15 @@ def _resolve_scan_targets(
|
||||||
targets: list[dict[str, str]] = []
|
targets: list[dict[str, str]] = []
|
||||||
if scope == "all_inventory":
|
if scope == "all_inventory":
|
||||||
# Managed inventory first; then UME NEs not already covered by the same management IP.
|
# Managed inventory first; then UME NEs not already covered by the same management IP.
|
||||||
|
# Skip LLDP placeholders (no login yet); once promoted they join normal inventory.
|
||||||
managed_ips: set[str] = set()
|
managed_ips: set[str] = set()
|
||||||
for ne in db.query(ManagedNE).all():
|
for ne in db.query(ManagedNE).all():
|
||||||
|
if _is_lldp_placeholder_ne(ne):
|
||||||
|
continue
|
||||||
ip = str(ne.ip_address or "").strip()
|
ip = str(ne.ip_address or "").strip()
|
||||||
if ip:
|
if ip:
|
||||||
managed_ips.add(ip)
|
managed_ips.add(ip)
|
||||||
targets.append(
|
targets.append(_managed_target_dict(ne))
|
||||||
{
|
|
||||||
"ne_id": ne.id,
|
|
||||||
"ume_ne_id": "",
|
|
||||||
"ne_name": ne.name or "",
|
|
||||||
"ne_ip": ne.ip_address or "",
|
|
||||||
"vendor": ne.vendor or "",
|
|
||||||
"device_type": ne.device_type or "",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
for ume in db.query(UmeInventoryNE).all():
|
for ume in db.query(UmeInventoryNE).all():
|
||||||
uid = str(ume.ne_id or "").strip()
|
uid = str(ume.ne_id or "").strip()
|
||||||
if not uid:
|
if not uid:
|
||||||
|
|
@ -173,19 +184,10 @@ def _resolve_scan_targets(
|
||||||
if mid in seen:
|
if mid in seen:
|
||||||
continue
|
continue
|
||||||
ne = db.get(ManagedNE, mid)
|
ne = db.get(ManagedNE, mid)
|
||||||
if ne is None:
|
if ne is None or _is_lldp_placeholder_ne(ne):
|
||||||
continue
|
continue
|
||||||
seen.add(mid)
|
seen.add(mid)
|
||||||
targets.append(
|
targets.append(_managed_target_dict(ne))
|
||||||
{
|
|
||||||
"ne_id": ne.id,
|
|
||||||
"ume_ne_id": "",
|
|
||||||
"ne_name": ne.name or "",
|
|
||||||
"ne_ip": ne.ip_address or "",
|
|
||||||
"vendor": ne.vendor or "",
|
|
||||||
"device_type": ne.device_type or "",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
for uid in ume_ids:
|
for uid in ume_ids:
|
||||||
key = f"ume:{uid}"
|
key = f"ume:{uid}"
|
||||||
if key in seen:
|
if key in seen:
|
||||||
|
|
@ -206,17 +208,10 @@ def _resolve_scan_targets(
|
||||||
for mid in list(filter_ids):
|
for mid in list(filter_ids):
|
||||||
ne = db.get(ManagedNE, mid)
|
ne = db.get(ManagedNE, mid)
|
||||||
if ne is not None:
|
if ne is not None:
|
||||||
targets.append(
|
|
||||||
{
|
|
||||||
"ne_id": ne.id,
|
|
||||||
"ume_ne_id": "",
|
|
||||||
"ne_name": ne.name or "",
|
|
||||||
"ne_ip": ne.ip_address or "",
|
|
||||||
"vendor": ne.vendor or "",
|
|
||||||
"device_type": ne.device_type or "",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
filter_ids.discard(mid)
|
filter_ids.discard(mid)
|
||||||
|
if _is_lldp_placeholder_ne(ne):
|
||||||
|
continue
|
||||||
|
targets.append(_managed_target_dict(ne))
|
||||||
for uid in list(filter_ids):
|
for uid in list(filter_ids):
|
||||||
row = _ume_target_dict(db, uid, default_profile)
|
row = _ume_target_dict(db, uid, default_profile)
|
||||||
if row is not None:
|
if row is not None:
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ from datetime import datetime, timedelta
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
|
||||||
from netx_api.db import Base, SessionLocal, engine
|
from netx_api.db import Base, SessionLocal, engine
|
||||||
|
from netx_api.device_types import LLDP_DISCOVERED_NE_SOURCE
|
||||||
from netx_api.lldp_collect_schemas import LldpCollectPolicyUpdate
|
from netx_api.lldp_collect_schemas import LldpCollectPolicyUpdate
|
||||||
from netx_api.lldp_collect_service import (
|
from netx_api.lldp_collect_service import (
|
||||||
build_discover_request,
|
build_discover_request,
|
||||||
|
|
@ -255,6 +256,14 @@ class LldpCollectTests(unittest.TestCase):
|
||||||
device_type="cisco_ios",
|
device_type="cisco_ios",
|
||||||
ip_address="10.20.30.1",
|
ip_address="10.20.30.1",
|
||||||
)
|
)
|
||||||
|
placeholder = ManagedNE(
|
||||||
|
id=f"ph-{suffix}",
|
||||||
|
name=f"PH-{suffix}",
|
||||||
|
vendor="Other",
|
||||||
|
device_type="generic",
|
||||||
|
ip_address="",
|
||||||
|
source=LLDP_DISCOVERED_NE_SOURCE,
|
||||||
|
)
|
||||||
ume_only = UmeInventoryNE(
|
ume_only = UmeInventoryNE(
|
||||||
ne_id=f"u-{suffix}",
|
ne_id=f"u-{suffix}",
|
||||||
ne_name=f"U-{suffix}",
|
ne_name=f"U-{suffix}",
|
||||||
|
|
@ -271,6 +280,7 @@ class LldpCollectTests(unittest.TestCase):
|
||||||
ne_type="ZXCTN",
|
ne_type="ZXCTN",
|
||||||
)
|
)
|
||||||
self.db.add(managed)
|
self.db.add(managed)
|
||||||
|
self.db.add(placeholder)
|
||||||
self.db.add(ume_only)
|
self.db.add(ume_only)
|
||||||
self.db.add(ume_dup)
|
self.db.add(ume_dup)
|
||||||
self.db.commit()
|
self.db.commit()
|
||||||
|
|
@ -280,9 +290,11 @@ class LldpCollectTests(unittest.TestCase):
|
||||||
self.assertIn((managed.id, ""), ids)
|
self.assertIn((managed.id, ""), ids)
|
||||||
self.assertIn((ume_only.ne_id, ume_only.ne_id), ids)
|
self.assertIn((ume_only.ne_id, ume_only.ne_id), ids)
|
||||||
self.assertNotIn((ume_dup.ne_id, ume_dup.ne_id), ids)
|
self.assertNotIn((ume_dup.ne_id, ume_dup.ne_id), ids)
|
||||||
|
self.assertNotIn((placeholder.id, ""), ids)
|
||||||
|
|
||||||
# Cleanup shared DB rows created by this test.
|
# Cleanup shared DB rows created by this test.
|
||||||
self.db.delete(managed)
|
self.db.delete(managed)
|
||||||
|
self.db.delete(placeholder)
|
||||||
self.db.delete(ume_only)
|
self.db.delete(ume_only)
|
||||||
self.db.delete(ume_dup)
|
self.db.delete(ume_dup)
|
||||||
self.db.commit()
|
self.db.commit()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue