feat: SNMP support — get_snmp_config(), fix_snmp action, get_device_warnings()

get_snmp_config() uses single 'show snmp-server' command with read_timeout=10
to avoid blocking the poll. fix_snmp configures 'snmp-server community public
manager restricted'. get_device_warnings() added (was missing, caused AttributeError).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Christian Manivong
2026-06-01 13:09:51 +02:00
co-authored by Claude Sonnet 4.6
parent e7d054ea2a
commit 5f780190c7
+95
View File
@@ -994,6 +994,101 @@ class ProcurveDriver(SwitchDriver):
"""Return True if a candidate config is loaded.""" """Return True if a candidate config is loaded."""
return self._candidate_config is not None return self._candidate_config is not None
# ── SNMP / Health ──────────────────────────────────────────────────────────
def get_device_warnings(self) -> list:
"""Return device warnings. SNMP detection handled by poll task."""
return []
def get_snmp_config(self):
"""Return SNMP config if a community is configured on the switch."""
try:
from napalm_device_types.models import SNMPConfigDict
except ImportError:
return None
# Use a single command with short read_timeout to avoid blocking the poll.
try:
out = self._device.send_command(
"show snmp-server",
expect_string=rf"{re.escape(self._device.base_prompt)}[>#]",
read_timeout=10,
).strip() if self._device else ""
except Exception:
return None
if not out:
return None
# Parse communities from output
community = None
for line in out.splitlines():
m = re.match(r"\s+(\S+)\s+(Manager|Operator)\s+(Restricted|Unrestricted)", line, re.I)
if m:
comm = m.group(1)
access = m.group(3).lower()
if access == "restricted": # read-only — prefer this
community = comm
break
elif community is None:
community = comm # fallback to first found
if not community:
return None
return SNMPConfigDict(running=True, community=community, port=161, version="2c")
def run_device_action(self, action: str) -> Dict:
"""Execute a named action on the switch."""
if action == "fix_snmp":
return self._action_fix_snmp()
raise NotImplementedError(f"Unknown action: {action!r}")
def _detect_management_vlan(self) -> Optional[int]:
"""Find the VLAN that carries this switch's management IP."""
try:
out = self._send_command("show ip")
for line in out.splitlines():
# " VLAN8 | 172.22.8.x ..." or similar
m = re.search(r"VLAN\s*(\d+)[^|]*\|\s*" + re.escape(self.hostname), line, re.I)
if m:
return int(m.group(1))
# Alternative: "VLAN 8 172.22.8.x"
m2 = re.search(r"\bVLAN\s+(\d+)\b.*\b" + re.escape(self.hostname) + r"\b", line, re.I)
if m2:
return int(m2.group(1))
except Exception:
pass
return None
def _action_fix_snmp(self) -> Dict:
"""Enable SNMP with community 'public' (read-only) on the ProCurve switch.
ProCurve/Aruba syntax:
snmp-server community "public" manager restricted
"""
lines: list = []
self._enter_config_mode()
# Enable SNMP with read-only community 'public'
# ProCurve: manager = standard access level, restricted = read-only
self._send_command('snmp-server community "public" manager restricted')
lines.append("[config] SNMP community 'public' (manager restricted / read-only) configured.")
self._exit_config_mode()
self._save_config()
lines.append("[config] Configuration saved.")
# Verify
out = self._send_command("show snmp-server")
success = "public" in out
if success:
lines.append("[ok] SNMP is active with community 'public'.")
else:
lines.append(f"[warn] Verification — community not found: {out[:200]}")
return {"success": success, "output": "\n".join(lines)}
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# Helpers # Helpers