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:
1 parent
739283e76b
commit
c2a788fa7a
6 files changed
+307
-278
No files matched your search
+72
-73
@@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Licensed under the Apache License, Version 2.0
|
||||
|
||||
"""CLI output parsers for HPE/Aruba ProCurve switches.
|
||||
@@ -9,13 +8,12 @@ backends.
|
||||
"""
|
||||
|
||||
import re
|
||||
from typing import Dict, List, Optional
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# MAC address helpers
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def procurve_mac_to_std(mac: str) -> str:
|
||||
"""Convert ProCurve MAC format ``a1b2c3-d4e5f6`` to ``a1:b2:c3:d4:e5:f6``."""
|
||||
clean = mac.replace("-", "").replace(":", "").replace(".", "").lower()
|
||||
@@ -38,7 +36,8 @@ def _kv(output: str, key: str) -> str:
|
||||
# show system-information
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def parse_system_info(output: str) -> Dict:
|
||||
|
||||
def parse_system_info(output: str) -> dict:
|
||||
"""Parse ``show system-information`` output.
|
||||
|
||||
Returns a dict with keys: hostname, os_version, serial_number, model,
|
||||
@@ -63,10 +62,8 @@ def parse_system_info(output: str) -> Dict:
|
||||
# between them, e.g.:
|
||||
# " Software revision : R.11.27 Base MAC Addr : a1b2c3-d4e5f6"
|
||||
# We capture all k:v pairs on every line.
|
||||
_KV_RE = re.compile(
|
||||
r"([A-Za-z][A-Za-z0-9 ()/#-]{1,40}?)\s*:\s*(\S.*?)(?=\s{3,}[A-Za-z]|$)"
|
||||
)
|
||||
kv_map: Dict[str, str] = {}
|
||||
_KV_RE = re.compile(r"([A-Za-z][A-Za-z0-9 ()/#-]{1,40}?)\s*:\s*(\S.*?)(?=\s{3,}[A-Za-z]|$)")
|
||||
kv_map: dict[str, str] = {}
|
||||
for raw_line in output.splitlines():
|
||||
for m in _KV_RE.finditer(raw_line):
|
||||
k = m.group(1).strip()
|
||||
@@ -163,6 +160,7 @@ def _parse_procurve_uptime(uptime_str: str) -> float:
|
||||
# show version
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def parse_version(output: str) -> str:
|
||||
"""Extract firmware version from ``show version`` or ``show system-information``.
|
||||
|
||||
@@ -234,24 +232,24 @@ def parse_model_from_version(output: str) -> tuple[str, str]:
|
||||
# 1 100/1000T | Yes Down Auto Unknown off 0
|
||||
|
||||
_INTF_BRIEF_RE = re.compile(
|
||||
r"^\s*([^\s-]+)" # Port
|
||||
r"(?:-(Trk\d+))?\s+" # Trunk group of a member port ("3-Trk3")
|
||||
r"(\S+)\s+\|" # Type |
|
||||
r"^\s*([^\s-]+)" # Port
|
||||
r"(?:-(Trk\d+))?\s+" # Trunk group of a member port ("3-Trk3")
|
||||
r"(\S+)\s+\|" # Type |
|
||||
r"\s+(?:(?:Yes|No)\s+)?" # Intrusion Alert, on firmware that has the column
|
||||
r"(Yes|No)\s+" # Enabled
|
||||
r"(Up|Down)\s+" # Link
|
||||
r"(\S+)\s+(\S+)", # Mode and MDI, in either order depending on firmware
|
||||
r"(Yes|No)\s+" # Enabled
|
||||
r"(Up|Down)\s+" # Link
|
||||
r"(\S+)\s+(\S+)", # Mode and MDI, in either order depending on firmware
|
||||
re.IGNORECASE,
|
||||
)
|
||||
|
||||
|
||||
def parse_interfaces_brief(output: str) -> Dict[str, Dict]:
|
||||
def parse_interfaces_brief(output: str) -> dict[str, dict]:
|
||||
"""Parse ``show interfaces brief`` tabular output.
|
||||
|
||||
Returns dict keyed by port name with sub-keys:
|
||||
is_up, is_enabled, speed, duplex, description.
|
||||
"""
|
||||
interfaces: Dict[str, Dict] = {}
|
||||
interfaces: dict[str, dict] = {}
|
||||
for line in output.splitlines():
|
||||
m = _INTF_BRIEF_RE.match(line)
|
||||
if not m:
|
||||
@@ -260,9 +258,7 @@ def parse_interfaces_brief(output: str) -> Dict[str, Dict]:
|
||||
speed = 0.0
|
||||
duplex = ""
|
||||
# Mode examples: "1000FDx", "100HDx", "Unknown"; MDI is "Auto", "MDI", "MDIX", "NA"
|
||||
sm = next(
|
||||
filter(None, (re.match(r"(\d+)(FDx|HDx)?", t, re.I) for t in mode_or_mdi)), None
|
||||
)
|
||||
sm = next(filter(None, (re.match(r"(\d+)(FDx|HDx)?", t, re.I) for t in mode_or_mdi)), None)
|
||||
if sm:
|
||||
speed = float(sm.group(1))
|
||||
duplex = "full" if (sm.group(2) or "").lower() == "fdx" else "half"
|
||||
@@ -286,7 +282,8 @@ def parse_interfaces_brief(output: str) -> Dict[str, Dict]:
|
||||
# show interfaces (per-port detail for MAC / description)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def parse_interface_detail(output: str, port: str) -> Dict:
|
||||
|
||||
def parse_interface_detail(output: str, port: str) -> dict:
|
||||
"""Parse ``show interfaces <port>`` for MAC address and description."""
|
||||
mac = procurve_mac_to_std(_kv(output, "MAC address"))
|
||||
name = _kv(output, "Name") or _kv(output, "Description")
|
||||
@@ -297,7 +294,8 @@ def parse_interface_detail(output: str, port: str) -> Dict:
|
||||
# show ip / show ip interface
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def parse_ip_interfaces(output: str) -> Dict[str, Dict]:
|
||||
|
||||
def parse_ip_interfaces(output: str) -> dict[str, dict]:
|
||||
"""Parse ``show ip`` or ``show ip interface brief`` output.
|
||||
|
||||
Returns NAPALM-compatible dict::
|
||||
@@ -314,7 +312,7 @@ def parse_ip_interfaces(output: str) -> Dict[str, Dict]:
|
||||
IP Configuration for VLAN 99:
|
||||
...
|
||||
"""
|
||||
result: Dict[str, Dict] = {}
|
||||
result: dict[str, dict] = {}
|
||||
current_vlan = ""
|
||||
current_ip = ""
|
||||
|
||||
@@ -343,6 +341,7 @@ def parse_ip_interfaces(output: str) -> Dict[str, Dict]:
|
||||
mask = line.split(":", 1)[-1].strip()
|
||||
try:
|
||||
import netaddr
|
||||
|
||||
prefix_len = netaddr.IPNetwork(f"{current_ip}/{mask}").prefixlen
|
||||
except Exception:
|
||||
prefix_len = 24
|
||||
@@ -362,23 +361,21 @@ def parse_ip_interfaces(output: str) -> Dict[str, Dict]:
|
||||
_ARP_RE = re.compile(
|
||||
r"(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})" # IP
|
||||
r"\s+"
|
||||
r"([0-9a-fA-F]{6}-[0-9a-fA-F]{6}|" # MAC ProCurve hex-dash format
|
||||
r"[0-9a-fA-F:]{17})" # or standard colon format
|
||||
r"\s+(\S+)" # Type
|
||||
r"(?:\s+(\S+))?", # Port (optional)
|
||||
r"([0-9a-fA-F]{6}-[0-9a-fA-F]{6}|" # MAC ProCurve hex-dash format
|
||||
r"[0-9a-fA-F:]{17})" # or standard colon format
|
||||
r"\s+(\S+)" # Type
|
||||
r"(?:\s+(\S+))?", # Port (optional)
|
||||
)
|
||||
|
||||
|
||||
def parse_arp_table(output: str) -> List[Dict]:
|
||||
def parse_arp_table(output: str) -> list[dict]:
|
||||
"""Parse ``show arp`` output into NAPALM ARP table format."""
|
||||
table = []
|
||||
for line in output.splitlines():
|
||||
m = _ARP_RE.search(line)
|
||||
if not m:
|
||||
continue
|
||||
ip, mac, entry_type, port = (
|
||||
m.group(1), m.group(2), m.group(3), m.group(4) or ""
|
||||
)
|
||||
ip, mac, entry_type, port = (m.group(1), m.group(2), m.group(3), m.group(4) or "")
|
||||
table.append(
|
||||
{
|
||||
"interface": port,
|
||||
@@ -396,13 +393,13 @@ def parse_arp_table(output: str) -> List[Dict]:
|
||||
|
||||
_MAC_RE = re.compile(
|
||||
r"([0-9a-fA-F]{6}-[0-9a-fA-F]{6}|[0-9a-fA-F:]{17})" # MAC
|
||||
r"\s+(\S+)" # Port
|
||||
r"\s+(\d+)" # VLAN
|
||||
r"\s+(\w+)", # Type
|
||||
r"\s+(\S+)" # Port
|
||||
r"\s+(\d+)" # VLAN
|
||||
r"\s+(\w+)", # Type
|
||||
)
|
||||
|
||||
|
||||
def parse_mac_table(output: str) -> List[Dict]:
|
||||
def parse_mac_table(output: str) -> list[dict]:
|
||||
"""Parse ``show mac-address`` into NAPALM MAC table format."""
|
||||
table = []
|
||||
for line in output.splitlines():
|
||||
@@ -428,7 +425,8 @@ def parse_mac_table(output: str) -> List[Dict]:
|
||||
# show lldp info remote-device
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def parse_lldp_neighbors(output: str) -> Dict[str, List[Dict]]:
|
||||
|
||||
def parse_lldp_neighbors(output: str) -> dict[str, list[dict]]:
|
||||
"""Parse ``show lldp info remote-device`` into NAPALM neighbors dict.
|
||||
|
||||
Output format::
|
||||
@@ -437,7 +435,7 @@ def parse_lldp_neighbors(output: str) -> Dict[str, List[Dict]]:
|
||||
--------- + ------------------- ------------ ------------ ...
|
||||
1 | a1 b2 c3 d4 e5 f6 Gi 0/1 otherswitch ...
|
||||
"""
|
||||
neighbors: Dict[str, List[Dict]] = {}
|
||||
neighbors: dict[str, list[dict]] = {}
|
||||
in_table = False
|
||||
|
||||
for line in output.splitlines():
|
||||
@@ -472,21 +470,19 @@ def parse_lldp_neighbors(output: str) -> Dict[str, List[Dict]]:
|
||||
if re.match(r"^[0-9a-fA-F]{12}$", chassis):
|
||||
chassis = ":".join(chassis[i : i + 2] for i in range(0, 12, 2))
|
||||
|
||||
neighbors.setdefault(local_port, []).append(
|
||||
{"hostname": sys_name, "port": port_id}
|
||||
)
|
||||
neighbors.setdefault(local_port, []).append({"hostname": sys_name, "port": port_id})
|
||||
|
||||
return neighbors
|
||||
|
||||
|
||||
def parse_lldp_neighbors_detail(output: str) -> Dict[str, List[Dict]]:
|
||||
def parse_lldp_neighbors_detail(output: str) -> dict[str, list[dict]]:
|
||||
"""Parse ``show lldp info remote-device detail`` output.
|
||||
|
||||
Returns NAPALM lldp_neighbors_detail format.
|
||||
"""
|
||||
neighbors: Dict[str, List[Dict]] = {}
|
||||
neighbors: dict[str, list[dict]] = {}
|
||||
current_port = ""
|
||||
entry: Dict = {}
|
||||
entry: dict = {}
|
||||
|
||||
def _save():
|
||||
if current_port and entry:
|
||||
@@ -516,9 +512,11 @@ def parse_lldp_neighbors_detail(output: str) -> Dict[str, List[Dict]]:
|
||||
low = line.lower()
|
||||
if "chassis id" in low:
|
||||
val = line.split(":", 1)[-1].strip()
|
||||
entry["remote_chassis_id"] = procurve_mac_to_std(
|
||||
re.sub(r"\s+", "", val)
|
||||
) if re.search(r"[0-9a-fA-F ]{14,}", val) else val
|
||||
entry["remote_chassis_id"] = (
|
||||
procurve_mac_to_std(re.sub(r"\s+", "", val))
|
||||
if re.search(r"[0-9a-fA-F ]{14,}", val)
|
||||
else val
|
||||
)
|
||||
elif "port id" in low and "descr" not in low:
|
||||
entry["remote_port"] = line.split(":", 1)[-1].strip()
|
||||
elif "port description" in low:
|
||||
@@ -528,9 +526,7 @@ def parse_lldp_neighbors_detail(output: str) -> Dict[str, List[Dict]]:
|
||||
elif "system description" in low:
|
||||
entry["remote_system_description"] = line.split(":", 1)[-1].strip()
|
||||
elif "system capabilities" in low and "enabled" not in low:
|
||||
entry["remote_system_capab"] = _parse_capabilities(
|
||||
line.split(":", 1)[-1].strip()
|
||||
)
|
||||
entry["remote_system_capab"] = _parse_capabilities(line.split(":", 1)[-1].strip())
|
||||
elif "enabled capabilities" in low or ("system capabilities" in low and "enabled" in low):
|
||||
entry["remote_system_enable_capab"] = _parse_capabilities(
|
||||
line.split(":", 1)[-1].strip()
|
||||
@@ -540,7 +536,7 @@ def parse_lldp_neighbors_detail(output: str) -> Dict[str, List[Dict]]:
|
||||
return neighbors
|
||||
|
||||
|
||||
def _parse_capabilities(cap_str: str) -> List[str]:
|
||||
def _parse_capabilities(cap_str: str) -> list[str]:
|
||||
"""Convert ProCurve capability abbreviations to NAPALM strings."""
|
||||
_MAP = {
|
||||
"B": "bridge",
|
||||
@@ -566,12 +562,13 @@ def _parse_capabilities(cap_str: str) -> List[str]:
|
||||
# show ntp associations / show timesync
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def parse_ntp_servers(output: str) -> Dict[str, Dict]:
|
||||
|
||||
def parse_ntp_servers(output: str) -> dict[str, dict]:
|
||||
"""Parse ``show ntp associations`` or ``show ntp status`` output.
|
||||
|
||||
Returns dict of {server_ip: {}} as per NAPALM get_ntp_servers.
|
||||
"""
|
||||
servers: Dict[str, Dict] = {}
|
||||
servers: dict[str, dict] = {}
|
||||
for line in output.splitlines():
|
||||
# " 10.0.0.1 sys.peer 2 ..." or "NTP server: 10.0.0.1"
|
||||
m = re.search(r"(\d{1,3}(?:\.\d{1,3}){3})", line)
|
||||
@@ -588,9 +585,10 @@ def parse_ntp_servers(output: str) -> Dict[str, Dict]:
|
||||
# show environment
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def parse_environment(output: str) -> Dict:
|
||||
|
||||
def parse_environment(output: str) -> dict:
|
||||
"""Parse ``show environment`` output into NAPALM environment dict."""
|
||||
env: Dict = {
|
||||
env: dict = {
|
||||
"fans": {},
|
||||
"temperature": {},
|
||||
"power": {},
|
||||
@@ -629,9 +627,7 @@ def parse_environment(output: str) -> Dict:
|
||||
}
|
||||
|
||||
# Fan status
|
||||
for m in re.finditer(
|
||||
r"Fan\s*(\d+)\s*[:\s]+(\S+)", output, re.I
|
||||
):
|
||||
for m in re.finditer(r"Fan\s*(\d+)\s*[:\s]+(\S+)", output, re.I):
|
||||
fan_id = f"fan_{m.group(1)}"
|
||||
status = m.group(2).lower()
|
||||
env["fans"][fan_id] = {"status": status in ("ok", "good", "normal", "operating")}
|
||||
@@ -643,12 +639,13 @@ def parse_environment(output: str) -> Dict:
|
||||
# show local-users
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def parse_users(output: str) -> Dict[str, Dict]:
|
||||
|
||||
def parse_users(output: str) -> dict[str, dict]:
|
||||
"""Parse ``show local-users`` or ``show users`` output.
|
||||
|
||||
Returns NAPALM users dict.
|
||||
"""
|
||||
users: Dict[str, Dict] = {}
|
||||
users: dict[str, dict] = {}
|
||||
for line in output.splitlines():
|
||||
# " manager Operator Enabled ..." or " manager Manager ..."
|
||||
m = re.match(
|
||||
@@ -678,9 +675,10 @@ def parse_users(output: str) -> Dict[str, Dict]:
|
||||
# show snmp-server / show snmp
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def parse_snmp_info(output: str) -> Dict:
|
||||
|
||||
def parse_snmp_info(output: str) -> dict:
|
||||
"""Parse ``show snmp-server`` output into NAPALM snmp_information dict."""
|
||||
communities: Dict[str, Dict] = {}
|
||||
communities: dict[str, dict] = {}
|
||||
contact = _kv(output, "System Contact") or _kv(output, "Contact")
|
||||
location = _kv(output, "System Location") or _kv(output, "Location")
|
||||
chassis_id = ""
|
||||
@@ -708,7 +706,8 @@ def parse_snmp_info(output: str) -> Dict:
|
||||
# VLAN helpers
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def expand_procurve_ports(port_str: str) -> List[str]:
|
||||
|
||||
def expand_procurve_ports(port_str: str) -> list[str]:
|
||||
"""Expand a ProCurve port-list string into a flat list of port names.
|
||||
|
||||
Handles:
|
||||
@@ -717,7 +716,7 @@ def expand_procurve_ports(port_str: str) -> List[str]:
|
||||
- Trunk ports: ``Trk1,Trk2`` → ``["Trk1","Trk2"]``
|
||||
- Mixed: ``1-3,Trk1,A1`` → ``["1","2","3","Trk1","A1"]``
|
||||
"""
|
||||
ports: List[str] = []
|
||||
ports: list[str] = []
|
||||
for token in port_str.split(","):
|
||||
token = token.strip()
|
||||
if not token:
|
||||
@@ -746,7 +745,7 @@ def expand_procurve_ports(port_str: str) -> List[str]:
|
||||
return ports
|
||||
|
||||
|
||||
def parse_vlans_from_running_config(config_text: str) -> Dict[int, Dict]:
|
||||
def parse_vlans_from_running_config(config_text: str) -> dict[int, dict]:
|
||||
"""Parse VLAN blocks from ProCurve ``show running-config`` output.
|
||||
|
||||
Extracts ``name``, ``tagged``, and ``untagged`` port lists per VLAN.
|
||||
@@ -777,8 +776,8 @@ def parse_vlans_from_running_config(config_text: str) -> Dict[int, Dict]:
|
||||
"untagged": []},
|
||||
}
|
||||
"""
|
||||
vlans: Dict[int, Dict] = {}
|
||||
current_id: Optional[int] = None
|
||||
vlans: dict[int, dict] = {}
|
||||
current_id: int | None = None
|
||||
|
||||
for line in config_text.splitlines():
|
||||
stripped = line.strip()
|
||||
@@ -818,7 +817,7 @@ def parse_vlans_from_running_config(config_text: str) -> Dict[int, Dict]:
|
||||
|
||||
# Populate "interfaces" as union of tagged + untagged (NAPALM standard key)
|
||||
for vdata in vlans.values():
|
||||
seen: List[str] = []
|
||||
seen: list[str] = []
|
||||
for p in vdata["untagged"] + vdata["tagged"]:
|
||||
if p not in seen:
|
||||
seen.append(p)
|
||||
@@ -827,7 +826,7 @@ def parse_vlans_from_running_config(config_text: str) -> Dict[int, Dict]:
|
||||
return vlans
|
||||
|
||||
|
||||
def parse_vlans_brief(output: str) -> Dict[int, str]:
|
||||
def parse_vlans_brief(output: str) -> dict[int, str]:
|
||||
"""Parse ``show vlans`` brief table for VLAN IDs and names only.
|
||||
|
||||
Used as a discovery step when running-config is unavailable.
|
||||
@@ -841,7 +840,7 @@ def parse_vlans_brief(output: str) -> Dict[int, str]:
|
||||
|
||||
Returns ``{vlan_id: name}`` dict.
|
||||
"""
|
||||
vlans: Dict[int, str] = {}
|
||||
vlans: dict[int, str] = {}
|
||||
in_table = False
|
||||
|
||||
for line in output.splitlines():
|
||||
@@ -857,7 +856,7 @@ def parse_vlans_brief(output: str) -> Dict[int, str]:
|
||||
return vlans
|
||||
|
||||
|
||||
def parse_vlan_ports_detail(output: str) -> Dict[str, List[str]]:
|
||||
def parse_vlan_ports_detail(output: str) -> dict[str, list[str]]:
|
||||
"""Parse ``show vlans <id>`` per-VLAN port detail output.
|
||||
|
||||
Returns ``{"tagged": [...], "untagged": [...]}`` for ONE vlan.
|
||||
@@ -870,8 +869,8 @@ def parse_vlan_ports_detail(output: str) -> Dict[str, List[str]]:
|
||||
2 Tagged Learn Up
|
||||
Trk1 Tagged Learn Up
|
||||
"""
|
||||
tagged: List[str] = []
|
||||
untagged: List[str] = []
|
||||
tagged: list[str] = []
|
||||
untagged: list[str] = []
|
||||
in_table = False
|
||||
|
||||
for line in output.splitlines():
|
||||
@@ -892,7 +891,7 @@ def parse_vlan_ports_detail(output: str) -> Dict[str, List[str]]:
|
||||
return {"tagged": tagged, "untagged": untagged}
|
||||
|
||||
|
||||
def derive_port_pvids(vlans: Dict[int, Dict]) -> Dict[str, int]:
|
||||
def derive_port_pvids(vlans: dict[int, dict]) -> dict[str, int]:
|
||||
"""Derive PVID (native/access VLAN) for each port from the ``get_vlans`` result.
|
||||
|
||||
On ProCurve a port is untagged in exactly ONE VLAN — that is its PVID.
|
||||
@@ -910,7 +909,7 @@ def derive_port_pvids(vlans: Dict[int, Dict]) -> Dict[str, int]:
|
||||
})
|
||||
# → {"1": 1, "2": 1, "3": 1, "5": 1}
|
||||
"""
|
||||
pvids: Dict[str, int] = {}
|
||||
pvids: dict[str, int] = {}
|
||||
|
||||
# First pass: assign PVID from untagged membership
|
||||
for vid, vdata in sorted(vlans.items()):
|
||||
|
||||
Reference in new issue
Block a user