An `amt` driver (napalm_amt.AmtDriver), a ManagementControllerDriver for the management controller of Intel vPro machines, with its own lean WS-Management client (requests + ElementTree, HTTP Digest, ports 16992/16993). It reads the machine's identity, hardware with the SMBIOS system UUID, firmware (AMT with build, BIOS), power state and the hardware event log, reports what AMT listens on (plain HTTP or TLS, KVM incl. port 5900, IDE-R), and switches the machine's power. AMT cannot be reset over WS-Management, so there is no reboot_host. Discovery recognises AMT by its ports and its Server header. Tested against Intel's published AMT 12 recordings (go-wsman-messages, Apache-2.0, see NOTICE). `python -m napalm_amt.record` records a real AMT read-only; the first test devices are two Lenovo ThinkCentre M910x.
56 lines
1.9 KiB
Python
56 lines
1.9 KiB
Python
"""The machine's power state and the power actions AMT accepts.
|
|
|
|
``CIM_AssociatedPowerManagementService.PowerState`` uses the CIM power state codes, and
|
|
``AvailableRequestedPowerStates`` lists the codes AMT accepts now. AMT lists the graceful
|
|
ones (12, 14) only where the host can honour them.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
|
|
from napalm_device_types.models import POWER_ACTIONS
|
|
|
|
from napalm_amt.parsers.common import as_list, to_int
|
|
|
|
_Instance = dict[str, Any]
|
|
|
|
#: Role power action -> the CIM power state AMT is asked for. 8 is "Power Off - Hard" and
|
|
#: 10 "Master Bus Reset" (a hard reset); Intel verified both, and 2 and 5, on AMT.
|
|
POWER_REQUESTS: dict[str, int] = {
|
|
"On": 2,
|
|
"GracefulShutdown": 12,
|
|
"ForceOff": 8,
|
|
"GracefulRestart": 14,
|
|
"ForceRestart": 10,
|
|
"PowerCycle": 5,
|
|
}
|
|
_ON = {2}
|
|
#: Off, but also asleep (3, 4) and hibernating (7): the OS is not running, and On wakes it.
|
|
_OFF = {3, 4, 6, 7, 8, 12, 13}
|
|
|
|
|
|
def parse_power_state(service: _Instance) -> dict[str, Any]:
|
|
"""``{"state": "On" | "Off" | "", "available": [CIM power state codes]}``."""
|
|
code = to_int(service.get("PowerState"))
|
|
state = "On" if code in _ON else "Off" if code in _OFF else ""
|
|
available = [
|
|
n
|
|
for n in (to_int(v) for v in as_list(service.get("AvailableRequestedPowerStates")))
|
|
if n is not None
|
|
]
|
|
return {"state": state, "available": available}
|
|
|
|
|
|
def accepted_actions(state: str, available: list[int]) -> list[str]:
|
|
"""The role actions that make sense in ``state`` and that AMT offers.
|
|
|
|
A machine that is off accepts On (AMT may leave 2 out of its list then). A machine that
|
|
is on accepts every other action whose state AMT lists.
|
|
"""
|
|
if state == "Off":
|
|
return ["On"] if not available or POWER_REQUESTS["On"] in available else []
|
|
if state == "On":
|
|
return [a for a in POWER_ACTIONS if a != "On" and POWER_REQUESTS[a] in available]
|
|
return []
|