Files
napalm-amt/napalm_amt/parsers/power.py
T
christianmanivong 0052ba7884 feat: Intel AMT driver over WS-Management (0.1.0)
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.
2026-10-18 14:55:32 +02:00

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 []