fix: fix_snmp SSH fallback when driver is connected via REST API
The REST API has no writable config endpoint for SNMP communities. When _action_fix_snmp() is called on a REST-connected driver, it now opens a temporary Netmiko SSH session (standard KEX, then legacy KEX fallback), runs the config-mode CLI commands, and restores the REST transport state on exit. SSH errors are surfaced verbatim instead of being silently swallowed. Also: - _save_config: guard against REST transport (was calling self._device directly → NoneType AttributeError when transport == "api") - _action_fix_snmp: extracted CLI logic into _action_fix_snmp_cli() so it is reused for both the SSH-fallback and native SSH/Telnet paths - api_client: add get_snmp_communities() + configure_snmp_community() for future use; improve GET error logging (warn vs debug on non-404) - _try_api: probe timeout capped at 15 s; SSL retry loop for self-signed certificates; improved log levels - _api_set_interface / _cli_set_interface: handle enabled/description fields that were previously ignored - probe(): broaden accepted status codes (301/302/303/403) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
aa55eb2bb9
commit
0cab237b1d
@@ -79,7 +79,9 @@ class ProcurveApiClient:
|
||||
# 401 = API exists but not authenticated (most common)
|
||||
# 200 = API exists and accessible without auth (unusual)
|
||||
# 405 = wrong HTTP verb but API is there
|
||||
if resp.status_code in (200, 401, 405):
|
||||
# 302/301/303 = redirect to login page (older firmware)
|
||||
# 403 = API exists but access forbidden
|
||||
if resp.status_code in (200, 301, 302, 303, 401, 403, 405):
|
||||
logger.debug(
|
||||
"API detected at %s (HTTP %s, %s %s)",
|
||||
hostname, resp.status_code, proto, ver,
|
||||
@@ -169,9 +171,12 @@ class ProcurveApiClient:
|
||||
resp = self._session.get(url, timeout=self.timeout)
|
||||
if resp.ok:
|
||||
return resp.json()
|
||||
logger.debug("GET %s returned HTTP %s", url, resp.status_code)
|
||||
if resp.status_code == 404:
|
||||
logger.debug("GET %s returned HTTP 404", url)
|
||||
else:
|
||||
logger.warning("GET %s returned HTTP %s: %s", url, resp.status_code, resp.text[:200])
|
||||
except Exception as exc:
|
||||
logger.debug("GET %s error: %s", url, exc)
|
||||
logger.warning("GET %s error: %s", url, exc)
|
||||
return {}
|
||||
|
||||
def post(self, endpoint: str, **kwargs: Any) -> requests.Response:
|
||||
@@ -481,6 +486,36 @@ class ProcurveApiClient:
|
||||
f"delete_vlan({vlan_id}): REST API returned HTTP {resp.status_code}"
|
||||
)
|
||||
|
||||
def get_snmp_communities(self) -> Dict[str, Dict]:
|
||||
"""Return configured SNMP communities from REST API."""
|
||||
data = self.get("snmp-server/community")
|
||||
result: Dict[str, Dict] = {}
|
||||
for entry in data.get("snmp_community_element", []):
|
||||
name = entry.get("community_name", "")
|
||||
if name:
|
||||
result[name] = {"access_type": entry.get("access_type", "")}
|
||||
return result
|
||||
|
||||
def configure_snmp_community(self, community: str = "public") -> Tuple[bool, str]:
|
||||
"""Ensure an SNMP read-only community exists via REST resource endpoint.
|
||||
|
||||
Returns (success, message).
|
||||
"""
|
||||
existing = self.get_snmp_communities()
|
||||
if community in existing:
|
||||
return True, f"SNMP community '{community}' already exists."
|
||||
|
||||
resp = self.post("snmp-server/community", json={
|
||||
"community_name": community,
|
||||
"access_type": "MIB2",
|
||||
})
|
||||
if resp.ok:
|
||||
return True, f"SNMP community '{community}' configured via REST API."
|
||||
if resp.status_code == 409:
|
||||
return True, f"SNMP community '{community}' already exists."
|
||||
|
||||
return False, f"REST API returned HTTP {resp.status_code}: {resp.text[:200]}"
|
||||
|
||||
def ping(
|
||||
self,
|
||||
destination: str,
|
||||
|
||||
Reference in New Issue
Block a user