diff --git a/napalm_openwrt/system_mixin.py b/napalm_openwrt/system_mixin.py index be007f5..0449116 100644 --- a/napalm_openwrt/system_mixin.py +++ b/napalm_openwrt/system_mixin.py @@ -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. diff --git a/pyproject.toml b/pyproject.toml index a4e9d6a..7d1369c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "napalm-openwrt" -version = "0.2.0" +version = "0.3.0" description = "NAPALM driver for OpenWrt routers/access-points" readme = "README.md" license = { text = "Apache-2.0" } diff --git a/tests/unit/test_warnings.py b/tests/unit/test_warnings.py new file mode 100644 index 0000000..06ab904 --- /dev/null +++ b/tests/unit/test_warnings.py @@ -0,0 +1,43 @@ +"""What the driver still names itself (netOrk #1111). + +lldpd, auc and the pending updates are facts the caller reads (``get_packages``, +``get_available_updates``) and decides on. A missing ``base64`` stays: BusyBox +may provide it as an applet, so no package says whether it is there. +""" + +from __future__ import annotations + +from napalm_openwrt import OpenWrtDriver + + +def _driver(answers: dict[str, str]) -> OpenWrtDriver: + d = OpenWrtDriver("ap", "root", "pw") + issued: list[str] = [] + + def send(command: str) -> str: + issued.append(command) + return next((out for frag, out in answers.items() if frag in command), "") + + d._send_command = send + d.issued = issued + return d + + +def test_nothing_installed_names_only_base64(): + """No lldpd, no auc and an opkg system: the caller names those from the packages.""" + d = _driver({"opkg list-upgradable": "luci - 1.0 - 1.1\n"}) + + assert [w["code"] for w in d.get_device_warnings()] == ["no_base64"] + + +def test_base64_present_names_nothing(): + d = _driver({"command -v base64": "/usr/bin/base64\n"}) + + assert d.get_device_warnings() == [] + + +def test_one_command_per_poll(): + d = _driver({}) + d.get_device_warnings() + + assert d.issued == ["command -v base64 2>/dev/null"]