From c98dd928e6f60e1098422dc89d43e3f7f9f35c11 Mon Sep 17 00:00:00 2001 From: Christian Manivong Date: Tue, 13 Oct 2026 12:20:12 +0200 Subject: [PATCH] feat: get_system_identity for matching a host to its BMC (0.7.0) Mixes in napalm-device-types 4.9's SystemIdentityMixin: the host's SMBIOS system UUID, product serial and board serial, read as root on an exec channel, vendor placeholders empty. netOrk links a server's BMC to the host on these. --- CHANGELOG.md | 7 +++++++ napalm_linux/linux.py | 7 +++++++ pyproject.toml | 4 ++-- tests/test_linux.py | 33 +++++++++++++++++++++++++++++++++ 4 files changed, 49 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b4b2a1d..008ed9f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- `get_system_identity()` (napalm-device-types 4.9 `SystemIdentityMixin`): the host's + SMBIOS system UUID, product serial and board serial, read as root on an exec channel, + vendor placeholders empty. netOrk matches a server to its BMC on these. Requires + napalm-device-types 4.9.0 or later; version 0.7.0. + ### Changed - Requires napalm-device-types 4.7.1 or later; version 0.6.0. diff --git a/napalm_linux/linux.py b/napalm_linux/linux.py index 0c2c03d..1d9a5ae 100644 --- a/napalm_linux/linux.py +++ b/napalm_linux/linux.py @@ -43,6 +43,7 @@ from napalm_device_types import ( ListeningSocketsMixin, OpenSshServerMixin, OSDriver, + SystemIdentityMixin, SystemdServicesMixin, SystemdUnavailable, VirtualIpsMixin, @@ -245,6 +246,7 @@ class LinuxDriver( KernelFactsMixin, ListeningSocketsMixin, VirtualIpsMixin, + SystemIdentityMixin, OpenSshServerMixin, SystemdServicesMixin, HostStatusMixin, @@ -1131,6 +1133,11 @@ class LinuxDriver( """ return self.run_command(command, privileged=privileged, timeout=60).stdout + def _run_system_identity_command(self, command: str, *, privileged: bool) -> str: + """The transport for ``SystemIdentityMixin.get_system_identity``: an exec channel, + as root, since ``product_uuid`` and ``product_serial`` are root's.""" + return self.run_command(command, privileged=privileged, timeout=30).stdout + # ------------------------------------------------------------------ # OSDriver – package management # ------------------------------------------------------------------ diff --git a/pyproject.toml b/pyproject.toml index d17944a..cf48a46 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "napalm-linux" -version = "0.6.0" +version = "0.7.0" description = "NAPALM driver for generic Linux systems via SSH" readme = "README.md" requires-python = ">=3.9" @@ -37,7 +37,7 @@ classifiers = [ ] dependencies = [ "napalm>=4.0", - "napalm-device-types>=4.7.1", + "napalm-device-types>=4.9.0", "netmiko>=4.0.0", "paramiko>=5.0.0", # CVE-2026-44405 ] diff --git a/tests/test_linux.py b/tests/test_linux.py index 65c496d..4f0cbce 100644 --- a/tests/test_linux.py +++ b/tests/test_linux.py @@ -1725,6 +1725,39 @@ class TestGetListeningSockets: assert plain.startswith("sh -c '") +# --------------------------------------------------------------------------- +# get_system_identity (napalm-device-types SystemIdentityMixin) +# --------------------------------------------------------------------------- + + +class TestGetSystemIdentity: + """What ties a host to its BMC: SMBIOS UUID and serials, read as root on an exec + channel (product_uuid and product_serial are root's).""" + + def test_it_reads_the_shared_command_as_root_on_an_exec_channel(self, driver): + from napalm_device_types import SYSTEM_IDENTITY_COMMAND, CommandResult, SystemIdentityMixin + + assert isinstance(driver, SystemIdentityMixin) + calls = [] + + def run_command(command, *, privileged=False, timeout=60, stdin=None): + calls.append((command, privileged)) + out = "IDBEGIN\n31303043-534D-C40C-7A72-6F1300000000\n0123456789\nZM155S026022\nIDEND\n" + return CommandResult(out, "", 0) + + driver.run_command = run_command + + identity = driver.get_system_identity() + + assert identity == { + "system_uuid": "31303043-534d-c40c-7a72-6f1300000000", + "product_serial": "", # Supermicro's placeholder + "board_serial": "ZM155S026022", + } + assert calls == [(SYSTEM_IDENTITY_COMMAND, True)] + driver._device.send_command.assert_not_called() + + # --------------------------------------------------------------------------- # get_virtual_ips (napalm-device-types VirtualIpsMixin) # ---------------------------------------------------------------------------