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). Mapping and
Iterable come from collections.abc, imports are sorted. What ruff cannot
fix comes in the next commit.
This commit is contained in:
Christian Manivong committed 2026-10-23 08:39:45 +02:00
1 parent 5b24675a12
commit 09029ca0a4
29 files changed
+985 -456

No files matched your search

+144 -87
View File
@@ -190,8 +190,9 @@ class RedfishDriver(HealthMetricsMixin, LicenseMixin, ManagementControllerDriver
except RedfishLicenseError as exc:
self._license_refused = exc
except RedfishError as exc:
raise ConnectionException(f"Cannot connect to Redfish at {self.hostname}: {exc}") \
from exc
raise ConnectionException(
f"Cannot connect to Redfish at {self.hostname}: {exc}"
) from exc
def close(self) -> None:
self.transport.close()
@@ -271,26 +272,37 @@ class RedfishDriver(HealthMetricsMixin, LicenseMixin, ManagementControllerDriver
return parse_network_protocol(raw or {})
def _manager_interfaces(self) -> list[_JsonDict]:
return [parse_ethernet_interface(raw)
for raw in self._members_of(self._manager_raw(), "EthernetInterfaces")]
return [
parse_ethernet_interface(raw)
for raw in self._members_of(self._manager_raw(), "EthernetInterfaces")
]
# --- NAPALM getters ----------------------------------------------------------------
def get_facts(self) -> _JsonDict:
if self._license_refused is not None:
return {"uptime": -1.0, "vendor": "", "model": "", "hostname": self.hostname,
"fqdn": self.hostname, "os_version": "", "serial_number": "",
"interface_list": []}
return {
"uptime": -1.0,
"vendor": "",
"model": "",
"hostname": self.hostname,
"fqdn": self.hostname,
"os_version": "",
"serial_number": "",
"interface_list": [],
}
system = parse_system(self._system_raw())
manager = parse_manager(self._manager_raw())
interfaces = self._manager_interfaces()
protocol = self._network_protocol()
hostname = (protocol["hostname"]
or next((i["hostname"] for i in interfaces if i["hostname"]), None)
or self.hostname)
fqdn = (protocol["fqdn"]
or next((i["fqdn"] for i in interfaces if i["fqdn"]), None)
or hostname)
hostname = (
protocol["hostname"]
or next((i["hostname"] for i in interfaces if i["hostname"]), None)
or self.hostname
)
fqdn = (
protocol["fqdn"] or next((i["fqdn"] for i in interfaces if i["fqdn"]), None) or hostname
)
return {
"uptime": -1.0, # Redfish has no uptime
"vendor": system["manufacturer"] or "",
@@ -327,11 +339,13 @@ class RedfishDriver(HealthMetricsMixin, LicenseMixin, ManagementControllerDriver
fans, temperature = legacy["fans"], legacy["temperature"]
elif thermal:
subsystem = parse_thermal_subsystem(self.transport.get(thermal["path"]))
parsed_fans = [parse_fan(raw) for raw in self.transport.members(
subsystem["fans_path"])] if subsystem["fans_path"] else []
parsed_fans = (
[parse_fan(raw) for raw in self.transport.members(subsystem["fans_path"])]
if subsystem["fans_path"]
else []
)
if sources["sensors"]:
sensors = [parse_sensor(raw)
for raw in self.transport.members(sources["sensors"])]
sensors = [parse_sensor(raw) for raw in self.transport.members(sources["sensors"])]
temperature = build_environment(sensors=sensors)["temperature"]
elif subsystem["thermal_metrics_path"]:
metrics = self.transport.get_optional(subsystem["thermal_metrics_path"])
@@ -342,12 +356,13 @@ class RedfishDriver(HealthMetricsMixin, LicenseMixin, ManagementControllerDriver
if supply and supply["schema"] == "Power":
power = parse_power(self.transport.get(supply["path"]))["power"]
elif supply:
psus = [parse_power_supply(raw, self.transport.follow(raw, "Metrics"))
for raw in self._power_supplies_raw(supply["path"])]
psus = [
parse_power_supply(raw, self.transport.follow(raw, "Metrics"))
for raw in self._power_supplies_raw(supply["path"])
]
power = build_environment(power_supplies=psus)["power"]
return {"fans": fans, "temperature": temperature, "power": power,
"cpu": {}, "memory": {}}
return {"fans": fans, "temperature": temperature, "power": power, "cpu": {}, "memory": {}}
def _power_supplies_raw(self, subsystem_path: str) -> list[_JsonDict]:
subsystem = parse_power_subsystem(self.transport.get(subsystem_path))
@@ -358,8 +373,7 @@ class RedfishDriver(HealthMetricsMixin, LicenseMixin, ManagementControllerDriver
service = self._service("AccountService")
if service is None:
return {}
return to_napalm_users(parse_account(raw) for raw in self._members_of(service,
"Accounts"))
return to_napalm_users(parse_account(raw) for raw in self._members_of(service, "Accounts"))
def get_ntp_servers(self) -> _JsonDict:
return {server: {} for server in self._network_protocol()["ntp"]["servers"]}
@@ -387,15 +401,23 @@ class RedfishDriver(HealthMetricsMixin, LicenseMixin, ManagementControllerDriver
"""
refused = self._license_refused
if refused is not None:
return [{"code": "bmc_license_required",
"meta": {"licenses": refused.licenses, "message": refused.detail}}]
return [
{
"code": "bmc_license_required",
"meta": {"licenses": refused.licenses, "message": refused.detail},
}
]
self._probe_withheld()
if not self._withheld:
return []
licenses = sorted({name for names in self._withheld.values() for name in names.split()})
parts = [part for part in _WITHHELD_ORDER if part in self._withheld]
return [{"code": "bmc_license_partial",
"meta": {"licenses": " ".join(licenses), "parts": parts}}]
return [
{
"code": "bmc_license_partial",
"meta": {"licenses": " ".join(licenses), "parts": parts},
}
]
def get_licenses(self) -> list[_JsonDict]:
"""The licenses the BMC asks for and does not have (``status`` "missing").
@@ -407,8 +429,10 @@ class RedfishDriver(HealthMetricsMixin, LicenseMixin, ManagementControllerDriver
"""
refused = self._license_refused
if refused is not None:
return [{"name": name, "status": "missing", "detail": refused.detail}
for name in refused.licenses.split() or [""]]
return [
{"name": name, "status": "missing", "detail": refused.detail}
for name in refused.licenses.split() or [""]
]
self._probe_withheld()
withheld: dict[str, list[str]] = {}
for part in _WITHHELD_ORDER:
@@ -416,8 +440,10 @@ class RedfishDriver(HealthMetricsMixin, LicenseMixin, ManagementControllerDriver
continue
for name in self._withheld[part].split() or [""]:
withheld.setdefault(name, []).append(part)
return [{"name": name, "status": "missing", "withholds": parts}
for name, parts in withheld.items()]
return [
{"name": name, "status": "missing", "withholds": parts}
for name, parts in withheld.items()
]
def _withhold(self, part: str, exc: RedfishFeatureLicenseError) -> None:
LOG.info("%s: %s withheld by the BMC: %s", self.hostname, part, exc)
@@ -460,8 +486,7 @@ class RedfishDriver(HealthMetricsMixin, LicenseMixin, ManagementControllerDriver
def get_power_state(self) -> _JsonDict:
system = self.transport.get(self._system_path())
return {"state": parse_power_state(system) or "",
"actions": self._accepted_actions(system)}
return {"state": parse_power_state(system) or "", "actions": self._accepted_actions(system)}
def set_power_state(self, action: str) -> None:
if action not in POWER_ACTIONS:
@@ -475,7 +500,8 @@ class RedfishDriver(HealthMetricsMixin, LicenseMixin, ManagementControllerDriver
raise RuntimeError(
f"{self.hostname}: {action} not accepted while the server is "
f"{parse_power_state(system) or 'in an unknown state'} "
f"(accepted: {', '.join(accepted) or 'none'})")
f"(accepted: {', '.join(accepted) or 'none'})"
)
try:
self.transport.post(target, {"ResetType": action})
except RedfishError as exc:
@@ -490,11 +516,13 @@ class RedfishDriver(HealthMetricsMixin, LicenseMixin, ManagementControllerDriver
raise RuntimeError(f"{self.hostname}: the BMC offers no reset action")
body: _JsonDict = {}
if reset["allowed"] is not None:
reset_type = next((t for t in ("GracefulRestart", "ForceRestart")
if t in reset["allowed"]), None)
reset_type = next(
(t for t in ("GracefulRestart", "ForceRestart") if t in reset["allowed"]), None
)
if reset_type is None:
raise RuntimeError(f"{self.hostname}: BMC reset types "
f"{reset['allowed']} include no restart")
raise RuntimeError(
f"{self.hostname}: BMC reset types {reset['allowed']} include no restart"
)
body["ResetType"] = reset_type
try:
self.transport.post(reset["target"], body)
@@ -524,9 +552,15 @@ class RedfishDriver(HealthMetricsMixin, LicenseMixin, ManagementControllerDriver
cpu = parse_processor(raw)
if cpu["processor_type"] not in (None, "CPU"):
continue # GPUs, FPGAs, accelerators
cpus.append({"socket": cpu["socket"] or cpu["id"] or "", "model": cpu["model"] or "",
"cores": cpu["cores"], "threads": cpu["threads"],
"status": _status(cpu)})
cpus.append(
{
"socket": cpu["socket"] or cpu["id"] or "",
"model": cpu["model"] or "",
"cores": cpu["cores"],
"threads": cpu["threads"],
"status": _status(cpu),
}
)
return cpus
def _dimms(self, system: _JsonDict) -> list[_JsonDict]:
@@ -534,15 +568,17 @@ class RedfishDriver(HealthMetricsMixin, LicenseMixin, ManagementControllerDriver
for raw in self._members_of(system, "Memory"):
dimm = parse_memory(raw)
size = dimm["capacity_mib"]
dimms.append({
"slot": dimm["location"] or dimm["id"] or "",
"size_bytes": size * 1024 * 1024 if size else None,
"type": dimm["memory_device_type"] or "",
"speed_mts": dimm["speed_mhz"], # Redfish: MHz or MT/s "as appropriate"
"part_number": dimm["part_number"] or "",
"serial_number": dimm["serial_number"] or "",
"status": _status(dimm),
})
dimms.append(
{
"slot": dimm["location"] or dimm["id"] or "",
"size_bytes": size * 1024 * 1024 if size else None,
"type": dimm["memory_device_type"] or "",
"speed_mts": dimm["speed_mhz"], # Redfish: MHz or MT/s "as appropriate"
"part_number": dimm["part_number"] or "",
"serial_number": dimm["serial_number"] or "",
"status": _status(dimm),
}
)
return dimms
def _storage_drives(self, system: _JsonDict) -> list[_JsonDict] | None:
@@ -572,15 +608,18 @@ class RedfishDriver(HealthMetricsMixin, LicenseMixin, ManagementControllerDriver
parsed = []
for storage in self._members_of(system, "SimpleStorage"):
parsed.extend(parse_simple_storage(storage)["drives"])
return [{
"slot": d["name"] or d["id"] or "",
"model": d["model"] or "",
"serial_number": d["serial_number"] or "",
"size_bytes": d["capacity_bytes"],
"media": d["media_type"] or "",
"protocol": d["protocol"] or "",
"status": _status(d),
} for d in parsed]
return [
{
"slot": d["name"] or d["id"] or "",
"model": d["model"] or "",
"serial_number": d["serial_number"] or "",
"size_bytes": d["capacity_bytes"],
"media": d["media_type"] or "",
"protocol": d["protocol"] or "",
"status": _status(d),
}
for d in parsed
]
def _psus(self) -> list[_JsonDict]:
source = environment_sources(self._chassis_raw())["power"]
@@ -595,13 +634,15 @@ class RedfishDriver(HealthMetricsMixin, LicenseMixin, ManagementControllerDriver
if not isinstance(raw, dict):
continue
psu = parse_power_supply(raw)
psus.append({
"slot": psu["name"],
"model": psu["model"] or "",
"serial_number": psu["serial_number"] or "",
"capacity_watts": int(psu["capacity"]) if psu["capacity"] >= 0 else None,
"status": _status(psu),
})
psus.append(
{
"slot": psu["name"],
"model": psu["model"] or "",
"serial_number": psu["serial_number"] or "",
"capacity_watts": int(psu["capacity"]) if psu["capacity"] >= 0 else None,
"status": _status(psu),
}
)
return psus
def _nics(self, system: _JsonDict) -> list[_JsonDict]:
@@ -610,13 +651,16 @@ class RedfishDriver(HealthMetricsMixin, LicenseMixin, ManagementControllerDriver
nic = parse_ethernet_interface(raw)
speed = nic["interface"]["speed"]
link_up = {"LinkUp": True, "LinkDown": False, "NoLink": False}.get(
nic["link_status"] or "")
nics.append({
"name": nic["name"] or "",
"mac_address": nic["interface"]["mac_address"].lower(),
"speed_mbps": int(speed) if speed >= 0 else None,
"link_up": link_up,
})
nic["link_status"] or ""
)
nics.append(
{
"name": nic["name"] or "",
"mac_address": nic["interface"]["mac_address"].lower(),
"speed_mbps": int(speed) if speed >= 0 else None,
"link_up": link_up,
}
)
return nics
# --- role: event log, firmware, protocols --------------------------------------------
@@ -640,20 +684,24 @@ class RedfishDriver(HealthMetricsMixin, LicenseMixin, ManagementControllerDriver
try:
raw_entries = self.transport.members(service["entries_path"])
except RedfishNotFound:
LOG.warning("%s: log %s has no entries collection", self.hostname,
service["id"])
LOG.warning(
"%s: log %s has no entries collection", self.hostname, service["id"]
)
continue
for raw in raw_entries:
entry = parse_log_entry(raw)
severity = entry["severity"]
entries.append({
"id": odata_id(raw) or f"{service['id']}/{entry['id']}",
"timestamp": _timestamp(entry["created"]),
"severity": severity if severity in _SEVERITIES else "OK",
"message": entry["message"] or "",
})
entries.sort(key=lambda e: (e["timestamp"] is not None, e["timestamp"] or 0.0),
reverse=True)
entries.append(
{
"id": odata_id(raw) or f"{service['id']}/{entry['id']}",
"timestamp": _timestamp(entry["created"]),
"severity": severity if severity in _SEVERITIES else "OK",
"message": entry["message"] or "",
}
)
entries.sort(
key=lambda e: (e["timestamp"] is not None, e["timestamp"] or 0.0), reverse=True
)
return entries[:limit]
def _update_service(self) -> _JsonDict | None:
@@ -673,11 +721,20 @@ class RedfishDriver(HealthMetricsMixin, LicenseMixin, ManagementControllerDriver
return []
bmc = parse_manager(self._manager_raw())["firmware_version"]
bios = self._system_raw().get("BiosVersion")
return [{"name": name, "version": version, "updateable": None}
for name, version in (("BMC", bmc), ("BIOS", bios)) if version]
return [
{"name": name, "version": version, "updateable": None}
for name, version in (("BMC", bmc), ("BIOS", bios))
if version
]
items = [parse_firmware(raw) for raw in self._members_of(service, "FirmwareInventory")]
return [{"name": fw["name"] or fw["id"] or "", "version": fw["version"] or "",
"updateable": fw["updateable"]} for fw in items]
return [
{
"name": fw["name"] or fw["id"] or "",
"version": fw["version"] or "",
"updateable": fw["updateable"],
}
for fw in items
]
def get_management_protocols(self) -> _JsonDict:
"""What the BMC listens on. A protocol the BMC does not report counts as off."""