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
+24 -8
View File
@@ -53,8 +53,18 @@ class FreeBSDDriver(BsdDriver):
return parse.service_status(output)
def _has_pkg(self) -> bool:
"""pkg is bootstrapped; a hand-installed classic system may have only the stub."""
return self.run_command("pkg -N").exit_code == 0
"""pkg is bootstrapped; a hand-installed classic system may have only the stub.
Only the stub's "not installed" means no pkg; any other failure (a
database pkg cannot read) raises rather than read as "no packages".
"""
result = self.run_command("pkg -N")
if result.exit_code == 0:
return True
message = (result.stderr or result.stdout).strip()
if "is not installed" in message:
return False
raise RuntimeError(f"pkg -N: {message or f'exit {result.exit_code}'}")
def get_packages(self) -> list[dict]:
return parse.pkg_query(self._out(self.PKG_QUERY)) if self._has_pkg() else []
@@ -69,10 +79,13 @@ class FreeBSDDriver(BsdDriver):
"""
updates: list[dict] = []
if self._has_pkg():
updates = parse.pkg_upgrades(self._out("pkg upgrade -n", privileged=True, timeout=300))
upgrade = self._read("pkg upgrade -n", privileged=True, timeout=300)
updates = parse.pkg_upgrades(upgrade.stdout)
audit = self.run_command("pkg audit -Fq", privileged=True, timeout=120)
known = audit.exit_code in (0, 1) # 1: vulnerable packages found
vulnerable = parse.pkg_audit(audit.stdout) if known else set()
vulnerable = parse.pkg_audit(audit.stdout)
# 1 is "vulnerable packages found" and any error alike; only a list
# of packages makes it a verdict.
known = audit.exit_code == 0 or (audit.exit_code == 1 and bool(vulnerable))
for update in updates:
update["security"] = (update["name"] in vulnerable) if known else None
if "FreeBSD-base" in self._out("pkg repos -l").split():
@@ -86,10 +99,13 @@ class FreeBSDDriver(BsdDriver):
2 when there are none; it does not fetch, which ``freebsd-update cron``
does daily where it is enabled.
"""
ready = self.run_command(
"freebsd-update --not-running-from-cron updatesready", privileged=True, timeout=60
ready = self._read(
"freebsd-update --not-running-from-cron updatesready",
privileged=True,
timeout=60,
ok=(0, 2),
)
if ready.exit_code != 0:
if ready.exit_code == 2:
return []
return [
{