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:
1 parent
e9ad7951cd
commit
5b24675a12
9 files changed
+86
-7
No files matched your search
@@ -7,5 +7,5 @@ takes the first driver class of this package. ``IpmiDriver`` lives in
|
||||
|
||||
from napalm_redfish.driver import RedfishDriver
|
||||
|
||||
__version__ = "0.4.0"
|
||||
__version__ = "0.5.0"
|
||||
__all__ = ["RedfishDriver"]
|
||||
@@ -23,7 +23,7 @@ import logging
|
||||
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_redfish.driver import BMC_HTTP_FINGERPRINT, BMC_PORT_SPECS, RedfishDriver
|
||||
from napalm_redfish.ipmi import IpmiDriver
|
||||
@@ -35,7 +35,7 @@ _JsonDict = dict[str, Any]
|
||||
_IPMI_PORT = 623
|
||||
|
||||
|
||||
class BmcDriver(HealthMetricsMixin, ManagementControllerDriver):
|
||||
class BmcDriver(HealthMetricsMixin, LicenseMixin, ManagementControllerDriver):
|
||||
"""One BMC over Redfish and IPMI: Redfish first, IPMI where Redfish does not answer."""
|
||||
|
||||
platform = "bmc"
|
||||
@@ -137,6 +137,10 @@ class BmcDriver(HealthMetricsMixin, ManagementControllerDriver):
|
||||
def get_snmp_information(self) -> _JsonDict:
|
||||
return self._call("get_snmp_information")
|
||||
|
||||
def get_licenses(self) -> list[_JsonDict]:
|
||||
"""Redfish's license facts, also while IPMI reads the BMC: IPMI knows none."""
|
||||
return self._redfish.get_licenses()
|
||||
|
||||
def get_device_warnings(self) -> list[_JsonDict]:
|
||||
"""Redfish's signals; for a BMC that refuses Redfish, with ``ipmi_fallback`` set, as
|
||||
this driver reads it over IPMI meanwhile."""
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in new issue
Block a user