feat: SystemIdentityMixin reads a host's SMBIOS identity (4.9.0)
CI / test (3.10) (push) Successful in 27s
CI / test (3.11) (push) Successful in 25s
CI / test (3.12) (push) Successful in 27s
CI / test (3.13) (push) Successful in 27s

What ties a server to its BMC: the BMC reports the server's serial
number and system UUID, the host reads the same values from its DMI
tables. Both drivers that read DMI (napalm-linux, napalm-proxmox) had a
copy of the shell for it, and neither read the board serial.

SystemIdentityMixin.get_system_identity() reads product_uuid,
product_serial and board_serial in one framed command and returns them
with vendor placeholders empty: a Supermicro board's product serial is
0123456789 on every one of them, others read "To be filled by O.E.M."
or "Default string", and many cheap boards share the UUID
03000200-0400-0500-0006-000700080009. is_placeholder_identity() is the
same filter for a BMC driver's side. A driver supplies only
_run_system_identity_command(command, privileged=); the files are
root's.
This commit is contained in:
christianmanivong committed 2026-10-13 12:20:10 +02:00
1 parent a1da0337e2
commit fde6d717a8
6 files changed
+247 -1

No files matched your search

+10
View File
@@ -125,6 +125,12 @@ from napalm_device_types.systemd import (
parse_systemd_services,
)
from napalm_device_types.updates import UpdateMixin
from napalm_device_types.system_identity import (
SYSTEM_IDENTITY_COMMAND,
SystemIdentityMixin,
is_placeholder_identity,
parse_system_identity,
)
from napalm_device_types.virtual_ips import (
VIRTUAL_IPS_COMMAND,
VirtualIpsMixin,
@@ -205,6 +211,10 @@ __all__ = [
"UpdateMixin",
"VIRTUAL_IPS_COMMAND",
"VirtualIpsMixin",
"SystemIdentityMixin",
"SYSTEM_IDENTITY_COMMAND",
"is_placeholder_identity",
"parse_system_identity",
"OpenSshServerMixin",
"SSH_ALGORITHM_CLASSES",
"SshHardeningPlan",
+10
View File
@@ -1151,3 +1151,13 @@ class ManagementProtocolsDict(TypedDict):
snmp: ProtocolStateDict
kvm: ProtocolStateDict
virtual_media: ProtocolStateDict
class SystemIdentityDict(TypedDict):
"""A host's SMBIOS identity, what ties it to its BMC (:mod:`napalm_device_types.system_identity`).
Each value is empty when unknown or a vendor placeholder."""
system_uuid: str # lowercase
product_serial: str
board_serial: str
+115
View File
@@ -0,0 +1,115 @@
# -*- coding: utf-8 -*-
"""The SMBIOS identity of a host: system UUID, product serial, board serial.
What ties a server to its BMC. The BMC reports the server's serial number and
system UUID; the host reads the same values from its firmware tables. Both sides
read them the same way on every Linux host, so the command, its parse and the
placeholder filter live here once, and a driver only carries the command across.
**Placeholders are no identity.** Board vendors leave defaults in the DMI tables
that are the same on thousands of machines: a Supermicro board's product serial
is ``0123456789``, others read ``To be filled by O.E.M.`` or ``Default string``,
and a common UUID on cheap boards is ``03000200-0400-0500-0006-000700080009``.
Matching on those would join unrelated machines, so they read as empty.
**Root.** ``product_uuid`` and ``product_serial`` are readable by root only; without
it they come back empty, which is "not known", not a mismatch.
"""
from __future__ import annotations
import re
from typing import TYPE_CHECKING
from napalm_device_types.models import SystemIdentityDict
from napalm_device_types.terminal import strip_terminal_codes
_BEGIN = "IDBEGIN"
_END = "IDEND"
_DMI = "/sys/class/dmi/id"
#: Reads the three files in one round trip, framed so a banner or prompt around the
#: output cannot shift a field.
SYSTEM_IDENTITY_COMMAND = (
f"printf '{_BEGIN}\\n%s\\n%s\\n%s\\n{_END}\\n' "
f"\"$(cat {_DMI}/product_uuid 2>/dev/null)\" "
f"\"$(cat {_DMI}/product_serial 2>/dev/null)\" "
f"\"$(cat {_DMI}/board_serial 2>/dev/null)\""
)
#: Lowercased defaults board vendors leave in place.
_PLACEHOLDERS = frozenset({
"", "none", "n/a", "na", "not specified", "not applicable", "not available",
"to be filled by o.e.m.", "to be filled by oem", "default string", "default",
"system serial number", "base board serial number", "chassis serial number",
"serial number", "0123456789", "1234567890", "123456789", "o.e.m.", "oem",
"03000200-0400-0500-0006-000700080009",
})
#: A value of one repeated character ("0", "00000000", "FFFF-...") or of zeros and Fs.
_REPEATED = re.compile(r"^(.)\1*$")
def is_placeholder_identity(value: str | None) -> bool:
"""Whether *value* is a vendor default rather than this machine's identity."""
text = (value or "").strip().lower()
if text in _PLACEHOLDERS:
return True
compact = text.replace("-", "").replace(" ", "")
return bool(_REPEATED.match(compact))
def _clean(value: str) -> str:
value = value.strip()
return "" if is_placeholder_identity(value) else value
def parse_system_identity(output: str) -> SystemIdentityDict:
"""The identity from :data:`SYSTEM_IDENTITY_COMMAND`'s output, placeholders empty.
:raises ValueError: when the output carries no intact frame.
"""
lines = strip_terminal_codes(output).splitlines()
try:
start = lines.index(_BEGIN) + 1
end = lines.index(_END, start)
except ValueError:
raise ValueError("system identity: no intact IDBEGIN/IDEND frame") from None
fields = (lines[start:end] + ["", "", ""])[:3]
uuid, product_serial, board_serial = (_clean(f) for f in fields)
return {
"system_uuid": uuid.lower(),
"product_serial": product_serial,
"board_serial": board_serial,
}
class SystemIdentityMixin:
"""Adds :meth:`get_system_identity` to a driver that can run a command on a Linux host.
The template form (README, "Function classes"): command and parse are the same
everywhere and concrete here; a driver supplies only
:meth:`_run_system_identity_command` -- how a command reaches its host, and how it
gains root there. Mixed in by the drivers that can, so
``hasattr(driver, "get_system_identity")`` stays a truthful answer.
"""
if TYPE_CHECKING: # pragma: no cover - declared for type checkers only
def _run_system_identity_command(self, command: str, *, privileged: bool) -> str:
"""Run *command* on the host and return what it printed; as root when
*privileged* (``product_uuid`` and ``product_serial`` are root's)."""
...
def get_system_identity(self) -> SystemIdentityDict:
"""
Returns the host's SMBIOS identity, each value empty when unknown or a vendor
placeholder:
* system_uuid (str) - lowercase
* product_serial (str)
* board_serial (str)
:raises ValueError: if the output carried no intact reading.
"""
output = self._run_system_identity_command(SYSTEM_IDENTITY_COMMAND, privileged=True)
return parse_system_identity(output)