fix: raise when an update reader cannot read, and report host status
CI / test (3.10) (push) Successful in 26s
CI / test (3.11) (push) Successful in 25s
CI / test (3.12) (push) Successful in 26s
CI / test (3.10) (pull_request) Successful in 25s
CI / test (3.11) (pull_request) Successful in 24s
CI / test (3.12) (pull_request) Successful in 26s

netOrk reads an empty update list as "no updates" and closes every patch
clock on the host. A refused sudo, a failing pkg, syspatch or freebsd-update,
or a pkg database pkg cannot read used to come back as that empty list. Each
of these now raises. Only pkg's "is not installed" still means no packages.

BsdDriver takes on napalm-device-types' HostStatusMixin. On FreeBSD it
reports a host whose installed kernel differs from the running one as
needing a reboot (device-types 4.1). OpenBSD stays unknown.

Closes #4
This commit is contained in:
Christian Manivong
2026-10-08 12:08:44 +02:00
parent 1172b4d9d5
commit 25308bf747
6 changed files with 160 additions and 28 deletions
+29 -1
View File
@@ -17,6 +17,7 @@ from typing import Any, Optional
import paramiko
from napalm.base.exceptions import ConnectionClosedException, ConnectionException
from napalm_device_types import OSDriver
from napalm_device_types.host_status import HostStatusMixin
from napalm_device_types.channel import (
ByteStream,
CommandResult,
@@ -47,7 +48,7 @@ _SNMPD_CONF = (
_SNMP_TYPES = ("STRING:", "INTEGER:", "OID:", "Timeticks:", "Hex-STRING:", "IpAddress:")
class BsdDriver(OSDriver):
class BsdDriver(HostStatusMixin, OSDriver):
"""Base for the BSD drivers; a concrete one names the commands that differ."""
VENDOR = ""
@@ -163,11 +164,38 @@ class BsdDriver(OSDriver):
line, prefix = self._privileged(command, privileged)
return open_stream_on_transport(self._transport(), line, stdin_prefix=prefix)
def _read(
self,
command: str,
*,
privileged: bool = False,
timeout: float = 60,
ok: tuple[int, ...] = (0,),
) -> CommandResult:
"""Run a reader's command; raise when it did not read.
A reader that cannot read raises rather than return "nothing": an empty
update list would close every patch clock netOrk keeps (napalm-bsd#4).
"""
result = self.run_command(command, privileged=privileged, timeout=timeout)
if result.exit_code not in ok:
reason = (result.stderr or result.stdout).strip() or f"exit {result.exit_code}"
raise RuntimeError(f"{command}: {reason}")
return result
def _out(self, command: str, *, privileged: bool = False, timeout: float = 60) -> str:
"""What *command* printed. A failing command prints nothing useful,
and the readers below treat empty output as "nothing there"."""
return self.run_command(command, privileged=privileged, timeout=timeout).stdout.strip()
def _run_host_status_command(self, command: str) -> str:
"""The transport for ``HostStatusMixin.get_host_status``: read-only, no root.
On FreeBSD the report compares ``freebsd-version -k`` with ``-r``
(napalm-device-types 4.1); elsewhere "reboot required" stays unknown.
"""
return self.run_command(command).stdout
# -- facts -------------------------------------------------------------------
def _platform(self) -> dict[str, str]: