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

A Proxmox node runs its own kernel under every guest, which makes it the host
where a kernel CVE's preconditions matter most. ProxmoxDriver mixes in
KernelFactsMixin from napalm-device-types and supplies only the transport,
the existing exec path.

Requires napalm-device-types 2.1.0.
This commit is contained in:
Christian Manivong
2026-10-05 06:17:30 +02:00
parent ccb9585f4e
commit 52074620ef
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__)
from napalm_device_types import FingerprintRule, HypervisorDriver, PortSpec
from napalm_device_types import FingerprintRule, HypervisorDriver, KernelFactsMixin, PortSpec
from napalm.base.exceptions import ConnectionException
try:
@@ -76,6 +76,7 @@ class ProxmoxDriver(
ProxmoxVMProvisionMixin,
ProxmoxRoutingMixin,
ProxmoxSystemMixin,
KernelFactsMixin,
HypervisorDriver,
):
"""NAPALM driver for Proxmox VE nodes."""
+8
View File
@@ -221,6 +221,14 @@ class ProxmoxSystemMixin:
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)
# ------------------------------------------------------------------ #
+1 -1
View File
@@ -25,7 +25,7 @@ classifiers = [
requires-python = ">=3.9"
dependencies = [
"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)
"proxmoxer>=2.0.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()