OSV states Debian ranges in *source* package versions. A source package that
ships several binaries gives each its own upstream version, and the two are
unrelated numbers: `libldb2` is `2:2.11.0+samba4.22.11+dfsg-0+deb13u1` while its
source, samba, is `2:4.22.11+dfsg-…`.
A consumer that resolves the coordinate on `source_package` — which is what this
driver's `source:Package` is for — and then compares `version` is comparing ldb's
version against samba's range. dpkg reads `2.11.0` as older than the
`2:4.17.4+dfsg-1` that fixed CVE-2022-44640, so a Debian 13 host running samba
4.22.11, five releases past the fix, was reported vulnerable on four packages at
once.
This driver cannot fix that comparison. It is the only place that can supply the
number to make it with.
Empty when dpkg considers it equal to `Version`, and empty on a dpkg that does
not know the field, so it falls back to `Version` — which is the previous
behaviour and correct everywhere except the shape above.
`maxsplit` goes from 4 to 5 with the extra field. Summary stays last, so it keeps
whatever it contains.
**The fixture was carrying four fields against a format string asking for five.**
`source_package` had been silently receiving the description, and no assertion
looked at it. It now carries what dpkg-query actually returns, including a
package whose source version is a different number from its own.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`docker ps` reports a bare image ID instead of the tag as soon as that tag
points at a newer image — which is exactly what a pull without a recreate
does. That ID went straight into `docker buildx imagetools inspect`, which
cannot resolve an image ID, so the lookup failed and the image was silently
dropped from the outdated list. The check was blind in precisely the state
that means an update is waiting.
get_docker_info() now also reads `.Config.Image`, the reference the container
was created from, which never degrades. It compares each running container's
image ID against the ID its own tag currently resolves to, and reports
`image_ref`, `running_image_id`, `restart_pending` and `pending_version`.
get_docker_outdated() prefers `image_ref`, skips bare IDs with a log line
instead of asking the registry about them, and no longer swallows a failed
registry lookup — silence there was indistinguishable from "up to date".
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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.
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.
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.
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.
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.
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.
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.
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>
_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>
- 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>
- 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>
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>