Add SEC/AMI scans, scan abort, connection test, runtime credentials, serial tracking
Also fixes several LLDP parsing edge cases (blank-field regex bleed, endpoint-device filtering, HP ProCurve chassis ID format, Aruba port naming), corrects link dedup to prefer self-reported port names over neighbor guesses, and repoints NetBox sync at the new instance where devices are hand-curated rather than auto-created. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Bp1HutYUh9fmTFgwB4qBLz
This commit is contained in:
+34
-1
@@ -1,5 +1,6 @@
|
||||
# scanner.py - Orchestrates the full scan pipeline
|
||||
import logging
|
||||
import threading
|
||||
from nocodb_client import get_switches
|
||||
from ssh_client import scan_all_switches
|
||||
from db import (
|
||||
@@ -24,8 +25,19 @@ scan_state = {
|
||||
"log_lines": [],
|
||||
"last_scan": None,
|
||||
"dept_filter": None, # None = all, "ELEC" or "GW" = dept-only
|
||||
"aborted": False,
|
||||
}
|
||||
|
||||
_abort_event = threading.Event()
|
||||
|
||||
|
||||
def request_abort():
|
||||
"""Signal the in-progress scan to stop after currently in-flight switches finish."""
|
||||
if scan_state["running"]:
|
||||
_abort_event.set()
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def run_scan(dept: str = None, workers: int = 5, login_delay: int = 3):
|
||||
"""
|
||||
@@ -38,6 +50,8 @@ def run_scan(dept: str = None, workers: int = 5, login_delay: int = 3):
|
||||
logger.warning("Scan already running, skipping.")
|
||||
return
|
||||
|
||||
_abort_event.clear()
|
||||
|
||||
# Fetch switch list from NocoDB (or fallback)
|
||||
switches = get_switches(dept=dept)
|
||||
|
||||
@@ -59,6 +73,7 @@ def run_scan(dept: str = None, workers: int = 5, login_delay: int = 3):
|
||||
"errors": [],
|
||||
"log_lines": [],
|
||||
"dept_filter": dept,
|
||||
"aborted": False,
|
||||
})
|
||||
|
||||
scan_id = log_scan_start()
|
||||
@@ -89,6 +104,7 @@ def run_scan(dept: str = None, workers: int = 5, login_delay: int = 3):
|
||||
description=result.get("description", ""),
|
||||
firmware=result.get("firmware", ""),
|
||||
vendor=result.get("vendor", ""),
|
||||
serial=result.get("serial", ""),
|
||||
)
|
||||
|
||||
for neighbor in result["neighbors"]:
|
||||
@@ -119,7 +135,16 @@ def run_scan(dept: str = None, workers: int = 5, login_delay: int = 3):
|
||||
"text": f"✗ {ip} — {error_short}",
|
||||
})
|
||||
|
||||
scan_all_switches(switches, progress_callback=on_progress, max_workers=workers, login_delay=login_delay)
|
||||
scan_all_switches(switches, progress_callback=on_progress, max_workers=workers,
|
||||
login_delay=login_delay, abort_event=_abort_event)
|
||||
|
||||
if _abort_event.is_set():
|
||||
scan_state["aborted"] = True
|
||||
from datetime import datetime
|
||||
scan_state["log_lines"].append({
|
||||
"ts": datetime.now().strftime("%H:%M:%S"), "ok": False,
|
||||
"text": "⚠ Scan aborted by user",
|
||||
})
|
||||
|
||||
try:
|
||||
merge_duplicate_switches()
|
||||
@@ -141,3 +166,11 @@ def run_scan(dept: str = None, workers: int = 5, login_delay: int = 3):
|
||||
scan_state["dept_filter"] = None
|
||||
|
||||
logger.info(f"Scan complete. OK: {scan_state['ok']}, Failed: {scan_state['fail']}")
|
||||
|
||||
|
||||
def test_single_switch(ip: str, manufacturer: str = "", login_delay: int = 1) -> dict:
|
||||
"""Test SSH connectivity/LLDP query against a single switch, independent of the main scan.
|
||||
Does not touch scan_state or the database — just returns the raw connect_and_query result."""
|
||||
entry = {"ip": ip, "manufacturer": manufacturer}
|
||||
results = scan_all_switches([entry], max_workers=1, login_delay=login_delay)
|
||||
return results[0] if results else {"success": False, "ip": ip, "error": "No result"}
|
||||
|
||||
Reference in New Issue
Block a user