feat: report the licenses a BMC asks for as facts (0.5.0)

get_licenses (napalm-device-types LicenseMixin, 4.15) on redfish and bmc,
for NetOrk/netork#1111.

- A BMC that refuses Redfish altogether reports each license it names as
  missing, with its message.
- One that answers but withholds parts reports the license with withholds,
  e.g. DCMS withholding drives and the firmware inventory on an X10 with
  only SFT-OOB-LIC.
- ipmi knows no licenses.

get_device_warnings keeps its two codes until netOrk derives them.
This commit is contained in:
Christian Manivong committed 2026-10-17 11:32:59 +02:00
1 parent e9ad7951cd
commit 5b24675a12
9 files changed
+86 -7

No files matched your search

+24 -2
View File
@@ -30,7 +30,7 @@ from datetime import datetime, timezone
from typing import Any
from napalm.base.exceptions import ConnectionException
from napalm_device_types import HealthMetricsMixin, ManagementControllerDriver
from napalm_device_types import HealthMetricsMixin, LicenseMixin, ManagementControllerDriver
from napalm_device_types.base import FingerprintRule, PortSpec
from napalm_device_types.models import POWER_ACTIONS
@@ -134,7 +134,7 @@ def _status(item: _JsonDict) -> str:
return item.get("health") or ""
class RedfishDriver(HealthMetricsMixin, ManagementControllerDriver):
class RedfishDriver(HealthMetricsMixin, LicenseMixin, ManagementControllerDriver):
"""NAPALM driver for Redfish BMCs (Supermicro, iDRAC, iLO, XCC and others).
``HealthMetricsMixin``: the BMC's own load, memory and interfaces over SNMP (UCD-MIB),
@@ -397,6 +397,28 @@ class RedfishDriver(HealthMetricsMixin, ManagementControllerDriver):
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").
Refused altogether: one entry per license the service names, with its message.
Answering but withholding parts: one entry per license, with ``withholds``
naming the parts. A BMC that names nothing missing reports nothing: Redfish has
no standard way to list the licenses a BMC does have.
"""
refused = self._license_refused
if refused is not None:
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:
if part not in self._withheld:
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()]
def _withhold(self, part: str, exc: RedfishFeatureLicenseError) -> None:
LOG.info("%s: %s withheld by the BMC: %s", self.hostname, part, exc)
self._withheld[part] = exc.licenses