feat: name only a missing base64; lldpd, auc and updates are facts (0.3.0)
CI / test (3.10) (push) Successful in 26s
CI / test (3.11) (push) Successful in 23s
CI / test (3.12) (push) Successful in 26s
CI / test (3.10) (pull_request) Successful in 24s
CI / test (3.11) (pull_request) Successful in 22s
CI / test (3.12) (pull_request) Successful in 25s

get_device_warnings no longer names lldpd_not_installed,
updates_available or update_notifications_disabled. The packages and
get_available_updates are the facts; netOrk derives the warnings with the
same reach (netOrk #1111). The old update read swallowed failures,
get_available_updates raises them.

no_base64 stays: BusyBox may provide base64 as an applet, so no package
tells the caller whether it is there.
This commit is contained in:
Christian Manivong committed 2026-10-17 09:37:43 +02:00
1 parent 9f43b8c797
commit 34d80bd994
3 files changed
+53 -62

No files matched your search

+9 -61
View File
@@ -391,69 +391,17 @@ class OpenWrtSystemMixin:
return {"success": True, "output": output}
def get_device_warnings(self) -> list[dict[str, Any]]:
"""Return a list of warning dicts for issues detected on this device.
"""Only a missing ``base64`` binary, which BusyBox may lack.
Currently detects:
- lldpd not installed (LLDP neighbor discovery unavailable)
- package updates available (uses local package cache, no network call)
- update notifications not configured (auc not installed, opkg only)
It is not a package the caller can look up -- BusyBox may provide it as
an applet -- so it stays here until the shell commands callers send get
a public method. lldpd, auc and the pending updates are facts
(``get_packages``, ``get_available_updates``); whether they are a
problem is the caller's to decide.
"""
warnings: list[dict[str, Any]] = []
# 1. LLDP daemon
lldpd_path = self._send_command("which lldpd 2>/dev/null").strip()
if not lldpd_path:
warnings.append({"code": "lldpd_not_installed"})
pm = self._pm_type()
# 2. Package updates available (local cache only – no opkg update)
def _pkg_name(line: str, pm_type: str) -> str:
"""Extract just the package name from an upgradable line.
apk: 'luci-app-firewall-26.143~abc < 26.151~def' → 'luci-app-firewall'
opkg: 'luci-app-firewall - 1.0 - 1.1' → 'luci-app-firewall'
"""
import re as _re
if pm_type == "apk":
# Strip trailing ' < ...' then remove the version suffix (-\d...)
name_ver = line.split(" ")[0]
m = _re.match(r'^(.*?)-\d', name_ver)
return m.group(1) if m else name_ver
else:
return line.split(" - ")[0].strip()
try:
if pm == "apk":
raw_upg = self._send_command("apk version 2>/dev/null | grep '<'")
else:
raw_upg = self._send_command("opkg list-upgradable 2>/dev/null")
upgradable = [ln.strip() for ln in raw_upg.splitlines() if ln.strip()]
except Exception:
upgradable = []
if upgradable:
pkg_names = [_pkg_name(ln, pm) for ln in upgradable]
warnings.append({
"code": "updates_available",
"meta": {
"count": len(upgradable),
"packages": pkg_names[:10],
},
})
# 3. Attended sysupgrade client not installed (opkg systems only)
if pm == "opkg":
auc_path = self._send_command("which auc 2>/dev/null").strip()
if not auc_path:
warnings.append({"code": "update_notifications_disabled"})
# 4. base64 not available — needed for efficient config apply
b64_path = self._send_command("command -v base64 2>/dev/null").strip()
if not b64_path:
warnings.append({"code": "no_base64"})
return warnings
if self._send_command("command -v base64 2>/dev/null").strip():
return []
return [{"code": "no_base64"}]
def get_available_updates(self) -> list[dict[str, Any]]:
"""Return list of upgradable packages from the local package manager cache.