mirror of
https://github.com/hansjone/netx.git
synced 2026-10-09 06:40:45 +08:00
Fix ZTE LLDP Scope table parse and skip discover without vendor.
Match ZXROS Scope/Holdtime columns, disable Netmiko cmd_verify on show commands, and do not SSH LLDP when vendor/device_type cannot be resolved. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
393fd0e0b4
commit
d2fb33ec0d
9 changed files with 146 additions and 19 deletions
|
|
@ -1,15 +1,32 @@
|
|||
Value LOCAL_INTERFACE (\S+)
|
||||
Value SCOPE (\S+)
|
||||
Value CHASSIS_ID (\S+)
|
||||
Value NEIGHBOR_PORT_ID (\S+)
|
||||
Value NEIGHBOR_NAME (\S+.*)
|
||||
Value HOLDTIME (\d+)
|
||||
Value NEIGHBOR_NAME (\S.*?)
|
||||
|
||||
Start
|
||||
^Local\s+Interface\s+Chassis\s+ID -> Neighbors
|
||||
^Local\s+Interface\s+Scope\s+Chassis\s+ID -> Neighbors
|
||||
^Local\s+Interface\s+Chassis\s+ID -> NeighborsSimple
|
||||
^Total\s+neighbors
|
||||
^-+
|
||||
^\s*$$
|
||||
^.* -> Start
|
||||
|
||||
Neighbors
|
||||
^${LOCAL_INTERFACE}\s+${CHASSIS_ID}\s+${NEIGHBOR_PORT_ID}\s+${NEIGHBOR_NAME}\s*$$ -> Record
|
||||
^${LOCAL_INTERFACE}\s+${SCOPE}\s+${CHASSIS_ID}\s+${NEIGHBOR_PORT_ID}\s+${HOLDTIME}\s+${NEIGHBOR_NAME}\s*$$ -> Record
|
||||
^-+
|
||||
^Total\s+neighbors
|
||||
^\s*$$
|
||||
^Local\s+Interface -> Neighbors
|
||||
^.* -> Start
|
||||
^\S+\s*#\s*$$ -> End
|
||||
^.*#\s*$$ -> End
|
||||
^.* -> Neighbors
|
||||
|
||||
NeighborsSimple
|
||||
^${LOCAL_INTERFACE}\s+${CHASSIS_ID}\s+${NEIGHBOR_PORT_ID}\s+${NEIGHBOR_NAME}\s*$$ -> Record
|
||||
^-+
|
||||
^Total\s+neighbors
|
||||
^\s*$$
|
||||
^\S+\s*#\s*$$ -> End
|
||||
^.*#\s*$$ -> End
|
||||
^.* -> NeighborsSimple
|
||||
|
|
|
|||
|
|
@ -38,8 +38,22 @@ def send_show_command(conn: Any, command: str, *, read_timeout: int = 120) -> st
|
|||
|
||||
Do not use ``send_command_timing`` for Cisco config collection: long idle during
|
||||
``Building configuration...`` is treated as end-of-output and truncates the config.
|
||||
|
||||
``cmd_verify=False``: Netmiko's default echo check often raises
|
||||
``Pattern not detected: 'show\\ lldp\\ ...'`` on IOSv / hop / slow echo paths.
|
||||
"""
|
||||
cmd = str(command or "").strip()
|
||||
if not cmd:
|
||||
return ""
|
||||
return str(conn.send_command(command_string=cmd, read_timeout=read_timeout) or "")
|
||||
try:
|
||||
return str(
|
||||
conn.send_command(
|
||||
command_string=cmd,
|
||||
read_timeout=read_timeout,
|
||||
cmd_verify=False,
|
||||
)
|
||||
or ""
|
||||
)
|
||||
except TypeError:
|
||||
# Older Netmiko without cmd_verify kwarg.
|
||||
return str(conn.send_command(command_string=cmd, read_timeout=read_timeout) or "")
|
||||
|
|
|
|||
|
|
@ -23,7 +23,13 @@ from .topology_fabric import (
|
|||
ensure_fabric_node_for_ume,
|
||||
upsert_fabric_edge,
|
||||
)
|
||||
from .topology_lldp import NeighborHit, parse_neighbor_output, parser_meta, pick_neighbor_command
|
||||
from .topology_lldp import (
|
||||
NeighborHit,
|
||||
can_discover_lldp,
|
||||
parse_neighbor_output,
|
||||
parser_meta,
|
||||
pick_neighbor_command,
|
||||
)
|
||||
|
||||
|
||||
def _discover_one_target(
|
||||
|
|
@ -70,10 +76,21 @@ def _discover_one_target(
|
|||
# Release unique-index locks before slow SSH.
|
||||
db.commit()
|
||||
|
||||
cmd, _proto = pick_neighbor_command(
|
||||
vendor=target.get("vendor") or "",
|
||||
device_type=target.get("device_type") or "",
|
||||
)
|
||||
vendor = target.get("vendor") or ""
|
||||
device_type = target.get("device_type") or ""
|
||||
pkey, is_stub = parser_meta(vendor=vendor, device_type=device_type)
|
||||
if not can_discover_lldp(vendor=vendor, device_type=device_type):
|
||||
return {
|
||||
**base,
|
||||
"ok": False,
|
||||
"command": "",
|
||||
"parser_key": pkey,
|
||||
"parser_stub": True,
|
||||
"error": "vendor_or_device_type_required",
|
||||
"raw_preview": "",
|
||||
}
|
||||
|
||||
cmd, _proto = pick_neighbor_command(vendor=vendor, device_type=device_type)
|
||||
exec_kwargs: dict[str, Any] = {"read_timeout_sec": 60}
|
||||
if target.get("ume_ne_id") and not db.get(ManagedNE, target["ne_id"]):
|
||||
exec_kwargs["ume_ne_id"] = target["ume_ne_id"]
|
||||
|
|
@ -102,14 +119,12 @@ def _discover_one_target(
|
|||
}
|
||||
|
||||
raw = str(exec_out.get("output") or "")
|
||||
pkey, is_stub = parser_meta(
|
||||
vendor=target.get("vendor") or "", device_type=target.get("device_type") or ""
|
||||
)
|
||||
hits = parse_neighbor_output(
|
||||
raw,
|
||||
protocol="lldp",
|
||||
vendor=target.get("vendor") or "",
|
||||
device_type=target.get("device_type") or "",
|
||||
vendor=vendor,
|
||||
device_type=device_type,
|
||||
command=cmd,
|
||||
)
|
||||
stub_flag = bool(is_stub and raw.strip() and not hits)
|
||||
|
||||
|
|
|
|||
|
|
@ -159,6 +159,11 @@ def pick_neighbor_command(
|
|||
return lldp_command_for_vendor(vendor, device_type), "lldp"
|
||||
|
||||
|
||||
def can_discover_lldp(*, vendor: str = "", device_type: str = "") -> bool:
|
||||
"""False when inventory has no usable vendor/device_type (do not SSH a guess command)."""
|
||||
return resolve_vendor_key(vendor, device_type) != "generic"
|
||||
|
||||
|
||||
def parser_meta(*, vendor: str = "", device_type: str = "") -> tuple[str, bool]:
|
||||
"""Return (parser_key, is_stub)."""
|
||||
key = resolve_vendor_key(vendor, device_type)
|
||||
|
|
@ -202,6 +207,17 @@ def _map_lldp_rows(rows: list[dict[str, Any]]) -> list[NeighborHit]:
|
|||
return hits
|
||||
|
||||
|
||||
def _strip_collect_wrapper(text: str) -> str:
|
||||
"""Drop SecureCRT-style headers injected by ``_collect_on_device``."""
|
||||
lines: list[str] = []
|
||||
for ln in str(text or "").splitlines():
|
||||
s = ln.strip()
|
||||
if s.startswith(">>>") and '"String"' in s:
|
||||
continue
|
||||
lines.append(ln)
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def _parse_lldp_via_ntc(
|
||||
text: str,
|
||||
*,
|
||||
|
|
@ -226,15 +242,18 @@ def parse_neighbor_output(
|
|||
command: str = "",
|
||||
) -> list[NeighborHit]:
|
||||
"""Parse neighbor CLI via TextFSM only (custom then community)."""
|
||||
raw = str(text or "")
|
||||
raw = _strip_collect_wrapper(text)
|
||||
if not raw.strip():
|
||||
return []
|
||||
_ = protocol # CDP discovery removed; always parse as LLDP
|
||||
if not can_discover_lldp(vendor=vendor, device_type=device_type):
|
||||
return []
|
||||
cmd = (command or "").strip() or lldp_command_for_vendor(vendor, device_type)
|
||||
return _parse_lldp_via_ntc(
|
||||
raw,
|
||||
vendor=vendor,
|
||||
device_type=device_type,
|
||||
command=command or lldp_command_for_vendor(vendor, device_type),
|
||||
command=cmd,
|
||||
)
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue