Files
lldp-mapper/scanner.py
T
dstephenson 40d4679a59 Initial commit — LLDP network mapper
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-21 20:56:13 +00:00

123 lines
3.7 KiB
Python

# scanner.py - Orchestrates the full scan pipeline
import logging
from nocodb_client import get_switch_ips
from ssh_client import scan_all_switches
from db import (
upsert_switch, upsert_link, clear_links,
log_scan_start, log_scan_finish, merge_duplicate_switches
)
from exports import run_all_exports
logger = logging.getLogger(__name__)
# Global scan state (shared with Flask via import)
scan_state = {
"running": False,
"done": 0,
"total": 0,
"current_ip": None,
"current_hostname": None,
"ok": 0,
"fail": 0,
"errors": [],
"last_scan": None,
"dept_filter": None, # None = all, "ELEC" or "GW" = dept-only
}
def run_scan(dept: str = None):
"""
Full scan: fetch switch IPs from NocoDB → SSH all → parse → store → export.
dept: None = all active switches, "ELEC" or "GW" = dept-filtered.
"""
global scan_state
if scan_state["running"]:
logger.warning("Scan already running, skipping.")
return
# Fetch switch list from NocoDB (or fallback)
switches = get_switch_ips(dept=dept)
if not switches:
logger.error("NocoDB returned no switches — aborting scan.")
scan_state["running"] = False
return
logger.error("No switches returned from NocoDB, aborting scan.")
return
scan_state.update({
"running": True,
"done": 0,
"total": len(switches),
"current_ip": None,
"current_hostname": None,
"ok": 0,
"fail": 0,
"errors": [],
"dept_filter": dept,
})
scan_id = log_scan_start()
clear_links() # Fresh start for links each scan
def on_progress(done, total, ip, result):
scan_state["done"] = done
scan_state["total"] = total
scan_state["current_ip"] = ip
if result["success"]:
scan_state["ok"] += 1
scan_state["current_hostname"] = result.get("hostname", ip)
upsert_switch(
chassis_id=result["chassis_id"],
hostname=result["hostname"],
mgmt_ip=result["mgmt_ip"],
description=result.get("description", ""),
)
for neighbor in result["neighbors"]:
if neighbor.get("chassis_id") and neighbor.get("system_name"):
upsert_switch(
chassis_id=neighbor["chassis_id"],
hostname=neighbor["system_name"],
mgmt_ip=neighbor.get("mgmt_ip", ""),
description=neighbor.get("system_desc", ""),
)
upsert_link(
chassis_a=result["chassis_id"],
port_a=neighbor["local_port"],
chassis_b=neighbor["chassis_id"],
port_b=neighbor["remote_port"],
)
else:
scan_state["fail"] += 1
scan_state["errors"].append({
"ip": ip,
"error": result.get("error", "Unknown error")
})
scan_all_switches(switches, progress_callback=on_progress)
try:
merge_duplicate_switches()
except Exception as e:
logger.error(f"Merge error: {e}")
try:
run_all_exports()
except Exception as e:
logger.error(f"Export error: {e}")
log_scan_finish(scan_id, scan_state["ok"], scan_state["fail"])
from datetime import datetime
scan_state["last_scan"] = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
scan_state["running"] = False
scan_state["current_ip"] = None
scan_state["current_hostname"] = None
scan_state["dept_filter"] = None
logger.info(f"Scan complete. OK: {scan_state['ok']}, Failed: {scan_state['fail']}")