Initial commit — LLDP network mapper
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+122
@@ -0,0 +1,122 @@
|
||||
# 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']}")
|
||||
Reference in New Issue
Block a user