style: ruff format and the lint rules of the driver standard

Mechanical: ruff format, and ruff check --fix with the rule set every NAPALM
driver uses (E, F, I, UP, B at line length 100, Python 3.10). What ruff
cannot fix comes in the next commit.
This commit is contained in:
Christian Manivong committed 2026-10-23 08:32:50 +02:00
1 parent 739283e76b
commit c2a788fa7a
6 files changed
+307 -278

No files matched your search

+65 -59
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# Licensed under the Apache License, Version 2.0
"""REST API client for HPE/Aruba ProCurve switches (ArubaOS REST interface).
@@ -8,13 +7,12 @@ Used automatically by ProcurveDriver when the REST API is reachable.
"""
import logging
from typing import Any, Dict, Optional, Tuple
from typing import Any
import requests
import urllib3
from napalm.base import helpers as napalm_helpers
from napalm.base.exceptions import ConnectionException, ConnectAuthError
from napalm.base.exceptions import ConnectAuthError, ConnectionException
from napalm_device_types import add_lag_interfaces
logger = logging.getLogger("napalm_procurve.api")
@@ -37,7 +35,7 @@ class ProcurveApiClient:
password: str,
timeout: int = 60,
ssl_verify: bool = False,
api_version: Optional[str] = None,
api_version: str | None = None,
) -> None:
self.hostname = hostname
self.username = username
@@ -47,7 +45,7 @@ class ProcurveApiClient:
self._api_version = api_version # None = auto-detect
self._proto: str = "https"
self._base_url: str = ""
self._session: Optional[requests.Session] = None
self._session: requests.Session | None = None
# ------------------------------------------------------------------
# Detection
@@ -59,7 +57,7 @@ class ProcurveApiClient:
hostname: str,
timeout: int = 5,
ssl_verify: bool = False,
) -> Tuple[Optional[str], Optional[str]]:
) -> tuple[str | None, str | None]:
"""Probe the device for an ArubaOS REST API.
Returns ``(api_version, proto)`` on success, ``(None, None)`` if the
@@ -86,7 +84,10 @@ class ProcurveApiClient:
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,
hostname,
resp.status_code,
proto,
ver,
)
return ver, proto
except requests.exceptions.SSLError:
@@ -115,9 +116,7 @@ class ProcurveApiClient:
self._session = requests.Session()
self._session.verify = self.ssl_verify
self._session.headers.update(
{"Content-Type": "application/json", "Connection": "close"}
)
self._session.headers.update({"Content-Type": "application/json", "Connection": "close"})
url = self._base_url + "login-sessions"
resp = self._session.post(
@@ -148,9 +147,7 @@ class ProcurveApiClient:
"""Log out from the REST API session."""
if self._session and self._base_url:
try:
self._session.delete(
self._base_url + "login-sessions", timeout=self.timeout
)
self._session.delete(self._base_url + "login-sessions", timeout=self.timeout)
except Exception:
pass
if self._session:
@@ -162,9 +159,7 @@ class ProcurveApiClient:
if not self._session:
return False
try:
resp = self._session.get(
self._base_url + "system/status", timeout=5
)
resp = self._session.get(self._base_url + "system/status", timeout=5)
return resp.status_code == 200
except Exception:
return False
@@ -173,7 +168,7 @@ class ProcurveApiClient:
# HTTP helpers
# ------------------------------------------------------------------
def get(self, endpoint: str) -> Dict[str, Any]:
def get(self, endpoint: str) -> dict[str, Any]:
"""GET ``{base_url}{endpoint}`` and return parsed JSON (or empty dict)."""
url = self._base_url + endpoint
try:
@@ -183,7 +178,9 @@ class ProcurveApiClient:
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])
logger.warning(
"GET %s returned HTTP %s: %s", url, resp.status_code, resp.text[:200]
)
except Exception as exc:
logger.warning("GET %s error: %s", url, exc)
return {}
@@ -204,7 +201,7 @@ class ProcurveApiClient:
# NAPALM data getters (API-backed)
# ------------------------------------------------------------------
def get_facts(self) -> Dict:
def get_facts(self) -> dict:
"""Return NAPALM facts from the REST API."""
system = self.get("system/status")
if not system:
@@ -230,6 +227,7 @@ class ProcurveApiClient:
model_raw = system.get("product_model", "")
# Extract J-code part numbers (e.g. J9777A) from the model string
import re as _re
_PN_RE = _re.compile(r"\b(J\d{4}[A-Z]{1,2})\b")
pn_match = _PN_RE.search(model_raw)
part_number = pn_match.group(1) if pn_match else ""
@@ -253,14 +251,14 @@ class ProcurveApiClient:
"interface_list": iface_list,
}
def get_interfaces(self) -> Dict[str, Dict]:
def get_interfaces(self) -> dict[str, dict]:
"""Return NAPALM interfaces from the REST API."""
ports_data = self.get("ports")
stats_data = self.get("port-statistics")
output: Dict[str, Dict] = {}
trunk_groups: Dict[str, list] = {}
trunk_modes: Dict[str, str] = {}
output: dict[str, dict] = {}
trunk_groups: dict[str, list] = {}
trunk_modes: dict[str, str] = {}
port_elements = list(ports_data.get("port_element", []))
@@ -274,13 +272,15 @@ class ProcurveApiClient:
for p in blade.get("data_ports", []):
pid = p.get("port_name", "")
if pid and pid not in seen_ids:
port_elements.append({
"id": pid,
"name": "",
"is_port_up": p.get("operStatus") == "OPER_UP",
"is_port_enabled": p.get("adminStatus") == "ADMIN_UP",
"trunk_group": "",
})
port_elements.append(
{
"id": pid,
"name": "",
"is_port_up": p.get("operStatus") == "OPER_UP",
"is_port_enabled": p.get("adminStatus") == "ADMIN_UP",
"trunk_group": "",
}
)
seen_ids.add(pid)
except Exception:
pass
@@ -309,15 +309,18 @@ class ProcurveApiClient:
output[pid]["speed"] = float(stat.get("port_speed_mbps", 0))
# One row per LAG/trunk group alongside its member ports.
return add_lag_interfaces(output, {
group: "lacp" if trunk_modes.get(group) == "PTT_LACP" else "trunk"
for group in trunk_groups
})
return add_lag_interfaces(
output,
{
group: "lacp" if trunk_modes.get(group) == "PTT_LACP" else "trunk"
for group in trunk_groups
},
)
def get_interfaces_ip(self) -> Dict[str, Dict]:
def get_interfaces_ip(self) -> dict[str, dict]:
"""Return NAPALM interfaces IP from the REST API."""
vlans_data = self.get("vlans")
result: Dict[str, Dict] = {}
result: dict[str, dict] = {}
for vlan in vlans_data.get("vlan_element", []):
vlan_id = str(vlan.get("vlan_id", ""))
@@ -331,6 +334,7 @@ class ProcurveApiClient:
continue
try:
import netaddr
plen = netaddr.IPNetwork(f"{addr}/{mask}").prefixlen
except Exception:
plen = 24
@@ -343,12 +347,12 @@ class ProcurveApiClient:
# PoE
# ------------------------------------------------------------------
def get_poe_ports(self) -> Dict[str, Dict]:
def get_poe_ports(self) -> dict[str, dict]:
"""Return PoE configuration for all ports, keyed by port id."""
data = self.get("poe/ports")
return {p["port_id"]: p for p in data.get("port_poe", []) if p.get("port_id")}
def set_port_poe(self, port_id: str, payload: Dict[str, Any]) -> requests.Response:
def set_port_poe(self, port_id: str, payload: dict[str, Any]) -> requests.Response:
"""PUT PoE configuration changes for a single port."""
return self.put(f"ports/{port_id}/poe", json=payload)
@@ -390,9 +394,9 @@ class ProcurveApiClient:
)
return table
def get_lldp_neighbors(self) -> Dict[str, list]:
def get_lldp_neighbors(self) -> dict[str, list]:
"""Return NAPALM LLDP neighbors from the REST API."""
result: Dict[str, list] = {}
result: dict[str, list] = {}
data = self.get("lldp/remote-device")
for nbr in data.get("lldp_remote_device_element", []):
local_port = nbr.get("local_port", "")
@@ -404,9 +408,9 @@ class ProcurveApiClient:
)
return result
def get_lldp_neighbors_detail(self) -> Dict[str, list]:
def get_lldp_neighbors_detail(self) -> dict[str, list]:
"""Return NAPALM LLDP neighbor details from the REST API."""
result: Dict[str, list] = {}
result: dict[str, list] = {}
data = self.get("lldp/remote-device")
for nbr in data.get("lldp_remote_device_element", []):
local_port = nbr.get("local_port", "")
@@ -424,7 +428,7 @@ class ProcurveApiClient:
)
return result
def get_config(self) -> Dict[str, str]:
def get_config(self) -> dict[str, str]:
"""Return running and startup configuration via REST API.
AOS-Switch returns the config as plain text, not JSON.
@@ -447,17 +451,17 @@ class ProcurveApiClient:
logger.warning("GET %s error: %s", url, exc)
return ""
def get_ntp_servers(self) -> Dict[str, Dict]:
def get_ntp_servers(self) -> dict[str, dict]:
"""Return NTP servers from REST API."""
data = self.get("ntp/server")
servers: Dict[str, Dict] = {}
servers: dict[str, dict] = {}
for entry in data.get("ntp_server_element", []):
addr = entry.get("server_address", "")
if addr:
servers[addr] = {}
return servers
def get_vlans(self) -> Dict[int, Dict]:
def get_vlans(self) -> dict[int, dict]:
"""Return VLAN information with proper tagged/untagged separation.
Fetches ``/vlans`` for VLAN names and ``/vlans-ports`` for port membership.
@@ -491,7 +495,7 @@ class ProcurveApiClient:
ports_data = self.get("vlans-ports")
# Build VLAN skeleton from /vlans
result: Dict[int, Dict] = {}
result: dict[int, dict] = {}
for vlan in vlans_data.get("vlan_element", []):
try:
vid = int(vlan["vlan_id"])
@@ -522,8 +526,7 @@ class ProcurveApiClient:
# Untagged variants: POM_UNTAGGED, POM_NATIVE_UNTAGGED, "Untagged"
is_untagged = any(
kw in mode_low
for kw in ("untagged", "native_untagged", "pom_untagged")
kw in mode_low for kw in ("untagged", "native_untagged", "pom_untagged")
) and "tagged" not in mode_low.replace("untagged", "")
# More precise: contains "untagged" anywhere → untagged
@@ -576,17 +579,17 @@ class ProcurveApiClient:
f"delete_vlan({vlan_id}): REST API returned HTTP {resp.status_code}"
)
def get_snmp_communities(self) -> Dict[str, Dict]:
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] = {}
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]:
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).
@@ -595,10 +598,13 @@ class ProcurveApiClient:
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",
})
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:
@@ -615,9 +621,9 @@ class ProcurveApiClient:
size: int = 100,
count: int = 5,
vrf: str = "",
) -> Dict:
) -> dict:
"""Execute ping via REST API (if supported) or return not-implemented."""
payload: Dict = {"destination": destination, "count": count, "timeout": timeout}
payload: dict = {"destination": destination, "count": count, "timeout": timeout}
if source:
payload["source"] = source