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

174 lines
5.9 KiB
Python

"""AMT's hardware event log: the base64 records of ``AMT_MessageLog.GetRecords``.
Each record is a 21-byte platform event (ASF/PET): a little-endian Unix timestamp, then
device address, sensor type, event type, offset, source type, severity, sensor number,
entity, entity instance and eight bytes of event data. The texts follow Intel's decoder in
go-wsman-messages, which knows the events AMT itself writes: failed logins, BIOS progress
and errors, watchdog and boot failures.
"""
from __future__ import annotations
import base64
import binascii
from typing import Any
_RECORD_LENGTH = 21
#: Entity codes (IPMI entity IDs) -> name.
_ENTITIES = {
0: "Unspecified",
1: "Other",
2: "Unknown",
3: "Processor",
4: "Disk",
5: "Peripheral",
6: "System management module",
7: "System board",
8: "Memory module",
9: "Processor module",
10: "Power supply",
11: "Add in card",
12: "Front panel board",
13: "Back panel board",
14: "Power system board",
15: "Drive backplane",
16: "System internal expansion board",
17: "Other system board",
18: "Processor board",
19: "Power unit",
20: "Power module",
21: "Power management board",
22: "Chassis back panel board",
23: "System chassis",
24: "Sub chassis",
25: "Other chassis board",
26: "Disk drive bay",
27: "Peripheral bay",
28: "Device bay",
29: "Fan cooling",
30: "Cooling unit",
31: "Cable interconnect",
32: "Memory device",
33: "System management software",
34: "BIOS",
35: "Intel(r) ME",
36: "System bus",
37: "Group",
38: "Intel(r) ME",
39: "External environment",
40: "Battery",
41: "Processing blade",
42: "Connectivity switch",
43: "Processor/memory module",
44: "I/O module",
45: "Processor I/O module",
46: "Management controller firmware",
47: "IPMI channel",
48: "PCI bus",
49: "PCI express bus",
50: "SCSI bus",
51: "SATA/SAS bus",
52: "Processor front side bus",
}
_FIRMWARE_ERRORS = {
0: "Unspecified.",
1: "No system memory is physically installed in the system.",
2: "No usable system memory, all installed memory has experienced an unrecoverable failure.",
3: "Unrecoverable hard-disk/ATAPI/IDE device failure.",
4: "Unrecoverable system-board failure.",
5: "Unrecoverable diskette subsystem failure.",
6: "Unrecoverable hard-disk controller failure.",
7: "Unrecoverable PS/2 or USB keyboard failure.",
8: "Removable boot media not found.",
9: "Unrecoverable video controller failure.",
10: "No video device detected.",
11: "Firmware (BIOS) ROM corruption detected.",
12: "CPU voltage mismatch (processors that share same supply have mismatched voltage "
"requirements)",
13: "CPU speed matching failure",
}
_FIRMWARE_PROGRESS = {
0: "Unspecified.",
1: "Memory initialization.",
2: "Starting hard-disk initialization and test",
3: "Secondary processor(s) initialization",
4: "User authentication",
5: "User-initiated system setup",
6: "USB resource configuration",
7: "PCI resource configuration",
8: "Option ROM initialization",
9: "Video initialization",
10: "Cache initialization",
11: "SM Bus initialization",
12: "Keyboard controller initialization",
13: "Embedded controller/management controller initialization",
14: "Docking station attachment",
15: "Enabling docking station",
16: "Docking station ejection",
17: "Disabling docking station",
18: "Calling operating system wake-up vector",
19: "Starting operating system boot process",
20: "Baseboard or motherboard initialization",
21: "reserved",
22: "Floppy initialization",
23: "Keyboard test",
24: "Pointing device test",
25: "Primary processor initialization",
}
_WATCHDOG_STATES = {1: "Not Started", 2: "Stopped", 4: "Running", 8: "Expired", 16: "Suspended"}
_FIXED = {
30: "No bootable media",
32: "Operating system lockup or power interrupt",
35: "System boot failure",
37: "System firmware started (at least one CPU is properly executing).",
}
def _severity(code: int) -> str:
"""PET severity: 16 critical, 32 non-recoverable; 8 non-critical; the rest is fine."""
if code >= 16:
return "Critical"
if code >= 8:
return "Warning"
return "OK"
def _description(sensor_type: int, offset: int, data: bytes) -> str:
if sensor_type == 6:
return (
f"Authentication failed {data[1] + (data[2] << 8)} times. "
"The system may be under attack."
)
if sensor_type == 15:
if data[0] == 235:
return "Invalid Data"
table = _FIRMWARE_ERRORS if offset == 0 else _FIRMWARE_PROGRESS
return table.get(data[1], f"code {data[1]}")
if sensor_type == 18:
if data[0] == 170:
agent = f"{data[4]:x}{data[3]:x}{data[2]:x}{data[1]:x}-{data[6]:x}{data[5]:x}"
state = _WATCHDOG_STATES.get(data[7], str(data[7]))
return f"Agent watchdog {agent}-... changed to {state}"
return "Unknown event data field"
return _FIXED.get(sensor_type, f"sensor type {sensor_type}")
def parse_event_record(record: str, entry_id: str) -> dict[str, Any] | None:
"""One base64 record as a role event log entry; None for a record that is not one."""
try:
raw = base64.b64decode(record, validate=True)
except (binascii.Error, ValueError):
return None
if len(raw) < _RECORD_LENGTH:
return None
timestamp = int.from_bytes(raw[0:4], "little")
sensor_type, offset, severity, entity = raw[5], raw[7], raw[9], raw[11]
entity_name = _ENTITIES.get(entity, f"Entity {entity}")
return {
"id": entry_id,
"timestamp": float(timestamp) if timestamp else None,
"severity": _severity(severity),
"message": f"{entity_name}: {_description(sensor_type, offset, raw[13:21])}",
}