Commit Graph
22 Commits
Author SHA1 Message Date
Christian Manivong d33739832b feat: report running_kernel (uname -r) in get_facts 2026-08-23 19:06:10 +07:00
Christian Manivong 7faaafb7a3 feat: include Debian source package in apt get_packages
dpkg-query now also reports ${source:Package} as source_package for accurate
OSV vulnerability matching (binary -> source, e.g. libssl3 -> openssl).
2026-08-23 17:45:07 +07:00
Christian Manivong 799d1ce749 feat: declare REBOOT_SETTLE_SECONDS = 90
A general-purpose host runs through a full init sequence before it is worth
polling again. netOrk kept this in a hardcoded driver-name set duplicated across
two files (netork#113).

Worth knowing: the NAS drivers that inherit from here — OpenMediaVault and QNAP
— were not in that set and waited 45 seconds. They inherit 90 now, which is the
more honest number for a device that also brings storage up on boot.
2026-08-21 13:07:14 +07:00
Christian Manivong ce40299033 fix(tests): repair the fixture and doubles that made seven tests fail
Closes netork#110.

The seven failures had two causes, neither of them in the driver.

The `driver` fixture builds a LinuxDriver with `__new__`, bypassing `__init__`,
and never set `_sudo_password`. Every call through `_sudo()` therefore raised
AttributeError, which the callers' broad `except Exception` reported as
`{"success": False}` — so six apply_updates tests failed for a reason unrelated
to what they were asserting.

`test_apply_updates_apt_all_packages` had a second one: its `capture_send(cmd)`
double accepted no keyword arguments, while `_sudo()` passes `read_timeout`.

The seventh, the apt update listing test, supplied `side_effect=["",
APT_UPGRADABLE]` — two values for a cache refresh that never existed.
`_get_updates_apt` has made exactly one `_send` call since it was written in
2712389, so the empty first value was consumed and parsed as the package list.
Checked out that commit and ran it: the test failed there too. It was committed
red and never passed.

That settles the open question in netork#110: the implementation was not changed,
the tests were written against one that never existed. `get_available_updates`
reads the local apt cache deliberately — refreshing it needs sudo and would cost
a round trip on every poll — so there is no stale-cache bug behind the
`updates_available` warning.
2026-08-21 12:57:11 +07:00
Christian Manivong 27027eec56 refactor!: keep one name for pending updates, drop the alias
This driver implemented get_pending_updates and then carried
get_available_updates as a one-line alias, because netOrk's API only ever called
the latter. Two names for one thing, with the driver bridging the gap.

napalm-device-types v1.0 collapses them onto get_available_updates — the name
four drivers and every netOrk call site already used — so the alias has nothing
left to bridge.

BREAKING CHANGE: get_pending_updates is gone; call get_available_updates.

The seven pre-existing test failures in this repo are untouched and unrelated;
see netork#110.
2026-08-21 12:50:10 +07:00
christianmanivong c8fc46c373 feat: make the docker binary path a hook
Where docker lives is device-specific; what to do with it is not. QNAP's
Container Station installs docker under /share/<pool>/.qpkg/ and never
puts it on PATH, so a QTS driver inheriting this class found no docker at
all.

Rather than reimplementing the Docker surface in the vendor driver, the
path becomes a single overridable method and every call site goes through
it. Per docs/ARCHITECTURE.md 4.4, generic logic belongs to the shared
driver and only the device-specific mechanics belong to the vendor one.

A test asserts that *every* docker call site uses the hook — a half
converted set would let detection find the binary while the actual
queries still missed it, which only shows up against real hardware.
2026-08-21 10:28:12 +07:00
Christian Manivong 661d56074c refactor(warnings): report raw signal only, no severity/presentation
get_device_warnings() now returns only {code, meta} — severity, title,
message, and action are resolved centrally by netork's
WARNING_CATALOG (netork/core/device_warnings.py), not by the driver.
Keeps this driver independent of netork and avoids per-vendor drift in
how the same warning code is presented.
2026-07-20 09:47:39 +02:00
Christian Manivong 2f049338b5 Merge fix/apt-full-upgrade: use full-upgrade to resolve all pending updates 2026-07-08 17:31:20 +02:00
Christian Manivong 07dcdbfe50 fix(linux): apt_update_upgrade left dependency-driven updates pending
Plain "apt-get upgrade" refuses to install or remove packages even when
a newer version requires it, silently holding those updates back —
switched to "apt-get full-upgrade" so VM-provisioning bootstrap actually
finishes with nothing left to update.
2026-07-08 17:31:16 +02:00
Christian Manivong 1cee26823e Merge fix/snmp-apt-update: refresh apt cache before installing snmpd 2026-07-08 17:09:23 +02:00
Christian Manivong 8436013dcd fix(linux): fix_snmp installs snmpd without refreshing apt cache first
A fresh cloud image's apt cache is stale/effectively empty — installing
snmpd without an apt-get update first could fail outright or hang on
unreachable mirrors, and the install call wasn't guarded, so a timeout
propagated as an opaque unguarded exception instead of a clean failure
result.

Also adds a new apt_update_upgrade device action (apt-get update +
upgrade), used by netork's VM-provisioning bootstrap alongside the
existing fix_apt_proxy action to fully prep a freshly provisioned VM's
apt before installing anything on it.
2026-07-08 17:09:19 +02:00
Christian Manivong 3ca2ed72ed fix(deps): pin paramiko>=5.0.0 (CVE-2026-44405) 2026-07-02 12:22:55 +02:00
Christian Manivong d16a05e501 fix(get_config): strip veth interfaces — Docker container churn causes false-positive config changes every poll
Docker creates a fresh veth pair with a new random name and ifindex
for every container start/restart. ip addr show includes them, so
get_config() reported a "config change" on nearly every poll of a
Docker host even though nothing about the host's own configuration
changed.
2026-07-01 20:32:20 +02:00
Christian Manivong 06cd1dc7aa fix(get_config): strip valid_lft/preferred_lft — volatile DHCP fields cause false-positive config changes every poll 2026-06-30 01:29:07 +02:00
Christian ManivongandClaude Sonnet 4.6 5fb51d23ea feat: Fingerprint-Attribute für Discovery-Scoring
Ergänzt DRIVER_NAME, HTTP_FINGERPRINT, SNMP_FINGERPRINT, SSH_FINGERPRINT,
PORT_SPECS und SNMP_OBJECT_ID_PREFIX gemäß docs/DISCOVERY_FINGERPRINTING.md.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 14:47:08 +02:00
Christian ManivongandClaude Sonnet 4.6 1bfc8dd245 feat: ARM hardware detection via device tree and /proc/cpuinfo
On ARM boards (Raspberry Pi, ODROID, etc.) /sys/class/dmi/id/ does not
exist. _collect_platform_info() now falls back to:
  - /sys/firmware/devicetree/base/model  (preferred)
  - /proc/cpuinfo Model: / Serial:       (fallback)

Three bugs fixed in the process:

1. systemd-detect-virt exits 1 on bare metal, so the old
   "|| echo none" pattern produced d="none\nnone" (two lines),
   shifting all subsequent fields by one. Fixed with ${d:-none}.

2. _send() calls .strip() on output, silently eating the five leading
   blank lines that represent empty DMI fields on ARM. Fixed by
   prefixing the printf output with a DMIBEGIN sentinel so the parser
   can locate field 0 regardless of leading whitespace.

3. Vendor was always empty for ARM, falling back to the generic "Linux"
   constant. Added _ARM_VENDOR_PREFIXES lookup table and
   _arm_vendor_from_model() to derive the canonical vendor name from
   the model string (e.g. "Raspberry Pi Foundation" for any RPi board).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 12:32:01 +02:00
Christian ManivongandClaude Sonnet 4.6 450aa193ba feat: TYPE_LABEL = "Linux" overrides OSDriver.TYPE_LABEL = "OS"
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 11:37:29 +02:00
Christian ManivongandClaude Sonnet 4.6 e065515de0 feat: VM/bare-metal detection in get_facts() — vendor, model, serial
_collect_platform_info() reads sys_vendor, product_name/version,
product_serial, product_uuid and systemd-detect-virt in one SSH
round-trip. Result:

- Bare-metal: vendor from DMI sys_vendor (e.g. "Dell Inc."), model
  from product_name (product_version preferred when it looks like a
  marketing name), serial from product_serial.
- VM (KVM/VMware/Hyper-V/Xen/VirtualBox): vendor is the hypervisor
  name, model is "Virtual Machine", serial prefers product_serial and
  falls back to product_uuid (VM UUID).
- Container (Docker/LXC/Podman): vendor is the container runtime,
  model is "Container".
- Junk DMI values ("To Be Filled By O.E.M." etc.) are filtered.
- Falls back to VENDOR = "Linux" when DMI is completely unavailable.

13 new unit tests covering all scenarios including SSH failure and
detect-virt unavailability.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 11:23:24 +02:00
Christian Manivong 8cc48ca383 feat: include remote chassis MAC in LLDP neighbor entries
Enables MAC-based topology matching against devices that report their
own MAC but not a usable system name.
2026-06-12 21:08:36 +02:00
Christian ManivongandClaude Sonnet 4.6 499d48c379 feat: apt proxy check+fix as device warning
- get_device_warnings(): checks /etc/apt/apt.conf.d/00proxy on apt systems
  (only when apt_proxy_url is set via optional_args from NetOrk settings)
- _action_fix_apt_proxy(): writes the proxy config via sudo, uses base64 to
  avoid quoting issues
- run_device_action(): routes 'fix_apt_proxy' to the new method

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-07 22:34:31 +02:00
Christian ManivongandClaude Sonnet 4.6 c46732946d feat: add lldp_neighbors, package mgmt, VPN tunnels, and get_health_metrics()
- get_lldp_neighbors() via lldpctl JSON output
- install_package() / uninstall_package() via apt/dnf/apk/pacman
- search_packages() across package managers
- get_vpn_tunnels() for WireGuard via wg show
- get_health_metrics() delegated to OSDriver base class (UCD-MIB)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-07 00:44:06 +02:00
Christian ManivongandClaude Sonnet 4.6 2712389818 feat: add get_route_to() via ip route show; family-Feld für IPv4/IPv6
Initial commit mit bestehendem Code inkl. neuem get_route_to():
- Parsed ip -4 route show und ip -6 route show
- Protokoll-Map: kernel/dhcp/ra/boot→connected, static→static, ospf→ospf, bgp→bgp
- family-Feld aus Netzadresse oder Next-Hop (: = ipv6)
- default/default6 → 0.0.0.0/0 / ::/0; Host-Routen ohne Prefix bekommen /32

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-06 15:50:02 +02:00