Files
napalm-amt/napalm_amt/parsers/system.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

93 lines
3.4 KiB
Python

"""The managed machine's identity and AMT's firmware.
``CIM_Chassis`` carries the system's SMBIOS identity (manufacturer, product, serial), and
``CIM_Card`` the baseboard's. ``CIM_ComputerSystemPackage.PlatformGUID`` is the SMBIOS
system UUID, the same that the operating system on the machine reads.
"""
from __future__ import annotations
import base64
import binascii
import re
from typing import Any
from napalm_amt.parsers.common import meaningful, text
_Instance = dict[str, Any]
_HEX32 = re.compile(r"^[0-9a-fA-F]{32}$")
def _uuid_from_hex(raw: str) -> str:
"""The SMBIOS UUID from its 16 bytes as stored: the first three groups little-endian,
as dmidecode and Intel's console decode them. Empty for all-zero or all-FF bytes, which
SMBIOS defines as "not present" and "not set"."""
if not _HEX32.match(raw or ""):
return ""
raw = raw.lower()
if raw in ("0" * 32, "f" * 32):
return ""
b = bytes.fromhex(raw)
swapped = b[3::-1] + b[5:3:-1] + b[7:5:-1] + b[8:]
h = swapped.hex()
return f"{h[:8]}-{h[8:12]}-{h[12:16]}-{h[16:20]}-{h[20:]}"
def parse_platform_guid(guid: str) -> str:
"""``CIM_ComputerSystemPackage.PlatformGUID`` (32 hex digits) as the SMBIOS UUID."""
return _uuid_from_hex(text(guid))
def uuid_from_base64(value: str) -> str:
"""``AMT_SetupAndConfigurationService.GetUuid``'s base64 UUID as the SMBIOS UUID."""
try:
raw = base64.b64decode(text(value), validate=True)
except (binascii.Error, ValueError):
return ""
return _uuid_from_hex(raw.hex())
def parse_software_identity(identities: list[_Instance]) -> dict[str, str]:
"""AMT's version with its build number (``12.0.67.1579``, as AMT's ``Server`` header
and Intel's advisories write it) and the ME firmware (``Flash``) version."""
versions = {text(i.get("InstanceID")): text(i.get("VersionString")) for i in identities}
amt = versions.get("AMT", "")
build = versions.get("Build Number", "")
return {
"amt": f"{amt}.{build}" if amt and build else amt,
"flash": versions.get("Flash", ""),
}
def parse_system_identity(chassis: list[_Instance], cards: list[_Instance]) -> dict[str, str]:
"""Manufacturer, model and serial of the machine, from the chassis and, where it holds
only placeholders, the baseboard; plus the baseboard's own model and serial."""
system = chassis[0] if chassis else {}
board = cards[0] if cards else {}
def pick(key: str) -> str:
return meaningful(system.get(key)) or meaningful(board.get(key))
return {
"manufacturer": pick("Manufacturer"),
"model": pick("Model"),
"serial_number": pick("SerialNumber"),
"board_model": meaningful(board.get("Model")),
"board_serial": meaningful(board.get("SerialNumber")),
}
def parse_firmware(identity: dict[str, str], bios: list[_Instance]) -> list[dict[str, Any]]:
"""AMT and the BIOS. Neither is updated through AMT, so ``updateable`` is unknown."""
primary = next(
(b for b in bios if text(b.get("PrimaryBIOS")).lower() == "true"), bios[0] if bios else {}
)
firmware = []
if identity.get("amt"):
firmware.append({"name": "Intel AMT", "version": identity["amt"], "updateable": None})
if text(primary.get("Version")):
firmware.append(
{"name": "BIOS", "version": text(primary.get("Version")), "updateable": None}
)
return firmware