Files
napalm-redfish/tests/test_x10_redfish.py
T
Christian Manivong 5b24675a12 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.
2026-10-17 11:32:59 +02:00

103 lines
3.6 KiB
Python

"""RedfishDriver against a recording of a real Supermicro X10DRL-i (BMC 3.91, Redfish 1.0.1).
Recorded 2026-10-17 with an SFT-OOB-LIC license only: the service root and most resources
answer, but ``Systems/1/Storage`` and ``UpdateService`` want the DCMS license as well (403,
``OemLicenseNotPassed``). Serial numbers, UUIDs, MACs, addresses, the host name and the SNMP
communities are scrubbed. ``STATUS.json`` lists the paths that did not answer 200; their
files hold the error body the BMC sent.
"""
import json
import pytest
from napalm_device_types import models
from napalm_redfish import RedfishDriver
from tests.conftest import BASE_URL, FIXTURES, MockupServer
MOCKUP = "supermicro-x10drl-i-3.91/redfish"
REFUSED = json.loads((FIXTURES / MOCKUP / "STATUS.json").read_text())
@pytest.fixture
def x10(http):
server = MockupServer(http, MOCKUP)
for path, status in REFUSED.items():
body = json.loads((FIXTURES / MOCKUP / path[len("/redfish/v1/"):] / "index.json")
.read_text())
server.override("GET", path, status, body)
return server
@pytest.fixture
def driver(x10):
d = RedfishDriver(BASE_URL.removeprefix("https://"), "netork", "secret",
optional_args={"verify_ssl": False})
d.open()
yield d
d.close()
def test_the_facts_name_the_board_not_super_server(driver):
facts = driver.get_facts()
assert facts["vendor"] == "Supermicro"
# Systems/1 says "Super Server"; the board is its PartNumber and the chassis model.
assert facts["model"] == "X10DRL-i"
assert facts["os_version"] == "3.91"
assert facts["serial_number"] == "ZM000S000001"
def test_the_inventory_survives_the_storage_it_may_not_read(driver):
inv = driver.get_hardware_inventory()
assert set(inv) == set(models.HardwareInventoryDict.__annotations__)
assert inv["model"] == "X10DRL-i"
assert inv["system_uuid"] == "00000000-0000-0000-0000-00005e00534c"
assert len(inv["cpus"]) == 2
assert len(inv["memory"]) == 8
assert inv["drives"] == []
def test_the_firmware_comes_from_the_manager_and_the_system_without_updateservice(driver):
assert driver.get_firmware_inventory() == [
{"name": "BMC", "version": "3.91", "updateable": None},
{"name": "BIOS", "version": "3.1c", "updateable": None},
]
def test_the_missing_license_is_named_with_what_it_withholds(driver):
assert driver.get_device_warnings() == [{
"code": "bmc_license_partial",
"meta": {"licenses": "DCMS", "parts": ["drives", "firmware inventory"]},
}]
def test_the_power_actions_are_the_ones_the_bmc_advertises(driver):
assert driver.get_power_state() == {
"state": "On",
"actions": ["GracefulShutdown", "ForceOff", "GracefulRestart", "ForceRestart"],
}
def test_the_protocols_report_ipmi_over_lan_on(driver):
protocols = driver.get_management_protocols()
assert protocols["ipmi"] == {"enabled": True, "port": 623}
assert protocols["https"] == {"enabled": True, "port": 443}
def test_the_sensors_and_the_event_log_read(driver):
env = driver.get_environment()
assert len(env["temperature"]) == 19
assert len(env["fans"]) == 4
assert isinstance(driver.get_event_log(limit=5), list)
# ── licenses as facts (#1111) ───────────────────────────────────────────
def test_the_missing_license_is_a_fact_with_what_it_withholds(driver):
assert driver.get_licenses() == [{
"name": "DCMS",
"status": "missing",
"withholds": ["drives", "firmware inventory"],
}]