Limit scan concurrency: serialise logins, cap at 5 sessions
Only one SSH handshake/auth runs at a time via a module-level semaphore to avoid RADIUS lockout. Up to 5 sessions can remain open concurrently (down from 10) while commands run in parallel after login. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+10
-2
@@ -1,6 +1,7 @@
|
|||||||
# ssh_client.py - SSH connections to FS, HP/Aruba ProCurve, and Dell switches
|
# ssh_client.py - SSH connections to FS, HP/Aruba ProCurve, and Dell switches
|
||||||
import re
|
import re
|
||||||
import logging
|
import logging
|
||||||
|
import threading
|
||||||
from concurrent.futures import ThreadPoolExecutor, as_completed
|
from concurrent.futures import ThreadPoolExecutor, as_completed
|
||||||
from netmiko import ConnectHandler, NetmikoTimeoutException, NetmikoAuthenticationException
|
from netmiko import ConnectHandler, NetmikoTimeoutException, NetmikoAuthenticationException
|
||||||
from config import SSH_USERNAME, SSH_PASSWORD, SSH_PORT, SSH_TIMEOUT, DEVICE_TYPE
|
from config import SSH_USERNAME, SSH_PASSWORD, SSH_PORT, SSH_TIMEOUT, DEVICE_TYPE
|
||||||
@@ -10,6 +11,9 @@ from parser import (parse_lldp_neighbors, parse_mgmt_ip_from_interfaces,
|
|||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
# Serialise SSH logins — only one handshake/auth at a time to avoid RADIUS lockout
|
||||||
|
_login_lock = threading.Semaphore(1)
|
||||||
|
|
||||||
|
|
||||||
def connect_and_query(ip):
|
def connect_and_query(ip):
|
||||||
device = {
|
device = {
|
||||||
@@ -36,9 +40,11 @@ def connect_and_query(ip):
|
|||||||
"ecdsa-sha2-nistp521", "ssh-ed25519",
|
"ecdsa-sha2-nistp521", "ssh-ed25519",
|
||||||
)
|
)
|
||||||
|
|
||||||
with ConnectHandler(**device) as conn:
|
with _login_lock:
|
||||||
|
conn = ConnectHandler(**device)
|
||||||
_pt.Transport._preferred_keys = _orig_preferred_keys
|
_pt.Transport._preferred_keys = _orig_preferred_keys
|
||||||
|
|
||||||
|
try:
|
||||||
hostname = conn.find_prompt().replace('#', '').replace('>', '').strip()
|
hostname = conn.find_prompt().replace('#', '').replace('>', '').strip()
|
||||||
version_output = conn.send_command("show version", read_timeout=30)
|
version_output = conn.send_command("show version", read_timeout=30)
|
||||||
vendor = _detect_vendor(version_output)
|
vendor = _detect_vendor(version_output)
|
||||||
@@ -47,6 +53,8 @@ def connect_and_query(ip):
|
|||||||
chassis_id, mgmt_ip, model, firmware, neighbors = _query_aruba(conn, version_output, ip)
|
chassis_id, mgmt_ip, model, firmware, neighbors = _query_aruba(conn, version_output, ip)
|
||||||
else:
|
else:
|
||||||
chassis_id, mgmt_ip, model, firmware, neighbors = _query_fs(conn, version_output, ip)
|
chassis_id, mgmt_ip, model, firmware, neighbors = _query_fs(conn, version_output, ip)
|
||||||
|
finally:
|
||||||
|
conn.disconnect()
|
||||||
|
|
||||||
logger.info(f" {hostname} ({ip}) [{vendor}]: {len(neighbors)} neighbors")
|
logger.info(f" {hostname} ({ip}) [{vendor}]: {len(neighbors)} neighbors")
|
||||||
|
|
||||||
@@ -167,7 +175,7 @@ def _aruba_firmware(version_output):
|
|||||||
|
|
||||||
# ── Scan orchestration ────────────────────────────────────────────────────────
|
# ── Scan orchestration ────────────────────────────────────────────────────────
|
||||||
|
|
||||||
def scan_all_switches(ip_list, progress_callback=None, max_workers=10):
|
def scan_all_switches(ip_list, progress_callback=None, max_workers=5):
|
||||||
results = []
|
results = []
|
||||||
total = len(ip_list)
|
total = len(ip_list)
|
||||||
done = 0
|
done = 0
|
||||||
|
|||||||
Reference in New Issue
Block a user