40d4679a59
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
123 lines
3.7 KiB
Python
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']}")
|