feat: read-only IPMI driver; report a missing Redfish license (0.2.0)
A Supermicro X10DRL-i refuses Redfish without a license (SUM, DCMS or
OOB), even the service root. IPMI answers without one.
- New entry point `ipmi` (napalm_redfish.ipmi.IpmiDriver) on
ManagementControllerDriver, over pyghmi: facts and inventory from the
FRU (serial number, system UUID), the BMC's LAN interface, sensors
(temperatures, fans, power supplies), the power state, the event log
and the BMC firmware. Read-only: no power control, no BMC reset.
Tested against recordings of the X10 with serials, the UUID, MACs and
IPs replaced.
- RedfishDriver.open() no longer fails on a license refusal: get_facts
returns what is known without Redfish, get_device_warnings reports
bmc_license_required with the licenses the BMC names, every other
getter raises. netOrk then shows the reason instead of an offline BMC.
- IpmiDriver is not exported from the package: NAPALM's own
get_network_driver("redfish") takes the first driver class of
napalm_redfish.
This commit is contained in:
1 parent
bfbeb19255
commit
332e6090a7
17 files changed
+1194
-19
No files matched your search
+37
-8
@@ -490,14 +490,43 @@ def test_get_management_protocols_missing(driver, server):
|
||||
assert protocols["ssh"] == {"enabled": False, "port": None}
|
||||
|
||||
|
||||
def test_unlicensed_bmc_says_so(server):
|
||||
def _unlicensed(server):
|
||||
# A Supermicro X10 refuses Redfish entirely until a license is activated.
|
||||
x10 = Path(__file__).parent / "fixtures" / "supermicro-x10drl-i-3.91"
|
||||
body = json.loads((x10 / "service_root_403.json").read_text())
|
||||
server.override("GET", "/redfish/v1/", 403, body)
|
||||
server.override("GET", "/redfish/v1/", 403,
|
||||
json.loads((x10 / "service_root_403.json").read_text()))
|
||||
d = RedfishDriver("bmc.example.net", "netork", "s3cret", optional_args={"verify_ssl": False})
|
||||
with pytest.raises(ConnectionException) as exc:
|
||||
d.open()
|
||||
message = str(exc.value)
|
||||
assert "license" in message.lower()
|
||||
assert "SUM DCMS OOB" in message
|
||||
d.open()
|
||||
return d
|
||||
|
||||
|
||||
def test_unlicensed_bmc_still_opens(server):
|
||||
# Reachable, just refusing: the poll goes through and says why instead of
|
||||
# reporting the BMC offline.
|
||||
d = _unlicensed(server)
|
||||
assert d.is_alive() == {"is_alive": True}
|
||||
assert d.get_facts() == {
|
||||
"uptime": -1.0, "vendor": "", "model": "", "hostname": "bmc.example.net",
|
||||
"fqdn": "bmc.example.net", "os_version": "", "serial_number": "", "interface_list": [],
|
||||
}
|
||||
|
||||
|
||||
def test_unlicensed_bmc_reports_the_missing_license(server):
|
||||
(warning,) = _unlicensed(server).get_device_warnings()
|
||||
assert warning["code"] == "bmc_license_required"
|
||||
assert warning["meta"]["licenses"] == "SUM DCMS OOB"
|
||||
assert "Not licensed" in warning["meta"]["message"]
|
||||
# Raw signal only: presentation is netOrk's.
|
||||
assert set(warning) == {"code", "meta"}
|
||||
|
||||
|
||||
def test_unlicensed_bmc_refuses_everything_else(server):
|
||||
d = _unlicensed(server)
|
||||
with pytest.raises(ConnectionException, match="license"):
|
||||
d.get_interfaces()
|
||||
with pytest.raises(ConnectionException, match="license"):
|
||||
d.get_hardware_inventory()
|
||||
|
||||
|
||||
def test_licensed_bmc_has_no_warnings(driver):
|
||||
assert driver.get_device_warnings() == []
|
||||
Reference in new issue
Block a user