feat: report the node kernel's modules and build configuration #7

Merged
christianmanivong merged 1 commits from feat/kernel-facts into master 2026-10-05 04:36:48 +00:00
4 changed files with 57 additions and 2 deletions
+2 -1
View File
@@ -34,7 +34,7 @@ from typing import Any
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
from napalm_device_types import FingerprintRule, HypervisorDriver, PortSpec from napalm_device_types import FingerprintRule, HypervisorDriver, KernelFactsMixin, PortSpec
from napalm.base.exceptions import ConnectionException from napalm.base.exceptions import ConnectionException
try: try:
@@ -76,6 +76,7 @@ class ProxmoxDriver(
ProxmoxVMProvisionMixin, ProxmoxVMProvisionMixin,
ProxmoxRoutingMixin, ProxmoxRoutingMixin,
ProxmoxSystemMixin, ProxmoxSystemMixin,
KernelFactsMixin,
HypervisorDriver, HypervisorDriver,
): ):
"""NAPALM driver for Proxmox VE nodes.""" """NAPALM driver for Proxmox VE nodes."""
+8
View File
@@ -221,6 +221,14 @@ class ProxmoxSystemMixin:
return result return result
# ------------------------------------------------------------------ #
# Kernel facts (KernelFactsMixin supplies get_kernel_facts)
# ------------------------------------------------------------------ #
def _run_kernel_facts_command(self, command: str) -> str:
"""The transport for ``KernelFactsMixin.get_kernel_facts``: the exec path."""
return self._exec_ssh_command(command)
# ------------------------------------------------------------------ # # ------------------------------------------------------------------ #
# Packages (Debian APT) # Packages (Debian APT)
# ------------------------------------------------------------------ # # ------------------------------------------------------------------ #
+1 -1
View File
@@ -25,7 +25,7 @@ classifiers = [
requires-python = ">=3.9" requires-python = ">=3.9"
dependencies = [ dependencies = [
"napalm>=5.0.0", "napalm>=5.0.0",
"napalm_device_types>=2.0.0", "napalm_device_types>=2.1.0",
"paramiko>=5.0.0", # CVE-2026-44405; imported directly for SSH fallback (driver.py) "paramiko>=5.0.0", # CVE-2026-44405; imported directly for SSH fallback (driver.py)
"proxmoxer>=2.0.0", "proxmoxer>=2.0.0",
"netaddr>=0.9.0", "netaddr>=0.9.0",
+46
View File
@@ -0,0 +1,46 @@
"""`get_kernel_facts`: what the node's kernel has built and loaded.
A Proxmox node runs its own kernel under every guest, which makes it the host
where a kernel CVE's preconditions matter most. The command and its parse are
napalm-device-types'; the driver only carries the command over its exec path.
"""
from __future__ import annotations
import base64
import gzip
from unittest.mock import patch
import pytest
from napalm_device_types import KernelFactsMixin
from napalm_device_types.kernel import KERNEL_FACTS_COMMAND
from napalm_proxmox.driver import ProxmoxDriver
REPORT = (
"[release]\n6.8.12-4-pve\n[loaded]\nkvm_intel\n[builtin]\nkernel/net/ipv4/tcp_cubic.ko\n"
"[available]\nkernel/net/tipc/tipc.ko\n[config]\nCONFIG_TIPC=m\n"
)
WIRE = "KFACTS_BEGIN\n" + base64.encodebytes(gzip.compress(REPORT.encode())).decode() + "KFACTS_END"
def test_the_driver_declares_the_contract():
assert issubclass(ProxmoxDriver, KernelFactsMixin)
def test_it_runs_the_shared_command(driver):
with patch.object(driver, "_exec_ssh_command", return_value=WIRE) as exec_:
facts = driver.get_kernel_facts()
exec_.assert_called_once_with(KERNEL_FACTS_COMMAND)
assert facts["release"] == "6.8.12-4-pve"
assert facts["loaded"] == ["kvm_intel"]
assert facts["builtin"] == ["tcp_cubic"]
assert facts["available"] == ["tipc"]
assert facts["config"] == {"CONFIG_TIPC": "m"}
def test_output_without_a_report_raises(driver):
with patch.object(driver, "_exec_ssh_command", return_value=""):
with pytest.raises(ValueError):
driver.get_kernel_facts()