feat: report the node kernel's modules and build configuration #7
@@ -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."""
|
||||
|
||||
@@ -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
@@ -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",
|
||||
|
||||
@@ -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()
|
||||
Reference in New Issue
Block a user