diff --git a/db.py b/db.py index 17cdecb..6cf85db 100644 --- a/db.py +++ b/db.py @@ -178,6 +178,36 @@ def upsert_link(chassis_a, port_a, chassis_b, port_b): conn.close() +def prune_stale_links(chassis_id, current): + """ + Drop links on a just-scanned switch that its own LLDP table no longer backs. + current = set of (local_port, neighbor_chassis_id) the switch reported this + scan. Dept scans don't clear_links() first, so without this a re-cabled port + (e.g. a new switch inserted mid-run) keeps its old link alongside the new one. + Must run after the switch's upsert_link calls, which rewrite its side of each + surviving link to the self-reported port name compared here. + """ + conn = get_conn() + rows = conn.execute(""" + SELECT id, chassis_a, port_a, chassis_b, port_b FROM links + WHERE chassis_a=? OR chassis_b=? + """, (chassis_id, chassis_id)).fetchall() + stale = [] + for r in rows: + if r['chassis_a'] == chassis_id: + key = (r['port_a'], r['chassis_b']) + else: + key = (r['port_b'], r['chassis_a']) + if key not in current: + stale.append((r['id'],)) + conn.executemany("DELETE FROM links WHERE id=?", stale) + conn.commit() + conn.close() + if stale: + logger.info(f"prune_stale_links: removed {len(stale)} stale link(s) on {chassis_id}") + return len(stale) + + def clear_stale_switches(since_dt): """Remove switches not seen since the given datetime string.""" conn = get_conn() diff --git a/scanner.py b/scanner.py index 40e6107..476df99 100644 --- a/scanner.py +++ b/scanner.py @@ -4,7 +4,7 @@ import threading from nocodb_client import get_switches from ssh_client import scan_all_switches from db import ( - upsert_switch, upsert_link, clear_links, + upsert_switch, upsert_link, clear_links, prune_stale_links, log_scan_start, log_scan_finish, merge_duplicate_switches ) from parser import parse_neighbor_description @@ -126,6 +126,10 @@ def run_scan(dept: str = None, workers: int = 5, login_delay: int = 3): chassis_b=neighbor["chassis_id"], port_b=neighbor["remote_port"], ) + prune_stale_links( + result["chassis_id"], + {(n["local_port"], n["chassis_id"]) for n in result["neighbors"]}, + ) else: scan_state["fail"] += 1 error = result.get("error", "Unknown error")