- Long shell command lines are split into adjacent string literals.
- get_os_users: a first loop over /etc/group filled a map nobody read;
it is gone, the output is unchanged.
- The five except Exception: pass now log at debug level (B110), so a
swallowed failure leaves a trace.
- nosec for the staging path on the device (B108) and the sudo hint
message (B105); both are false positives.
- tests: raise ... from exc, unused loop variable.
Mechanical: ruff format, and ruff check --fix with the rule set every NAPALM
driver uses (E, F, I, UP, B at line length 100, Python 3.10). What ruff
cannot fix comes in the next commit.
Mixes in napalm-device-types' HostFirewallMixin. The transport runs the shared
read-only command as root on an exec channel: nft prints the whole ruleset as
one line of JSON, longer than the PTY takes reliably, and both nft and
iptables-save need CAP_NET_ADMIN. Requires napalm-device-types 4.21.0;
version 0.13.0.
Mixes in napalm-device-types' ServicePackagesMixin. The transport runs the
shared read-only command on an exec channel under sh, without sudo: it is
about 1 kB, past what the PTY takes reliably, and written for sh whatever
the login shell. Requires napalm-device-types 4.20.0; version 0.12.0.
The driver accepted any SSH host key. It now verifies through
napalm-device-types' HostKeyCheck (>= 4.19.0): with a known key in
optional_args["ssh_host_keys"] any other key is refused before login, and
every key seen is reported to optional_args["ssh_host_key_observer"].
Without either argument it connects as before (first use).
LinuxDriver mixes in napalm-device-types' ServiceClustersMixin (4.17.0):
get_service_clusters() reads Patroni's members and roles, adguardhome-sync's
origin and replicas and a BunkerWeb worker's manager from the host's own
configuration -- the evidence that two hosts run one service, not merely
answer at one address (NetOrk/netork#1125). _run_service_clusters_command
carries the shared command on an exec channel, root first, like the virtual
IPs' one.
get_device_warnings decided two things for the application: that any
pending update is a warning (it re-read the updates for that), and
whether apt's proxy is the one the application's setting wants, which
it was handed as optional_args["apt_proxy_url"].
The driver reports facts instead (napalm-device-types 4.14):
get_package_proxy() says which proxy apt fetches through, None for none
or on a non-apt host; the updates are get_available_updates() as
before. get_device_warnings and the apt_proxy_url argument go.
BREAKING CHANGE: get_device_warnings and optional_args["apt_proxy_url"]
are removed. Requires napalm-device-types 4.14.
Closes#28
Dict, List and Optional were used in 30 annotations without an import;
from __future__ import annotations kept that from failing until
something resolved them (typing.get_type_hints raised NameError).
Closes#29
The driver filled run_device_action with an application's remediations
and their values: community "public", "Managed by netOrk", the
caller's address guessed from ss and a /24 ufw rule, the proxy from an
optional_args key.
It implements napalm-device-types' fix templates instead (4.12), and
the caller supplies every value:
- configure_snmp_agent(settings): net-snmp installed, snmpd.conf with
one rocommunity line per allowed source, udp/161 opened in ufw or
iptables for those sources only, snmpd enabled and restarted. Every
step reads its exit status. A community, source or text that would
break the file is refused.
- configure_package_proxy(url): apt's 00proxy, or removed for "".
- grant_container_engine_access(user): usermod -aG docker for the user
named.
- ensure_package(name): from the base package, over get_packages and
install_package.
run_device_action, its four _action_* methods and the unused
apt_update_upgrade are gone. The sudo hint no longer names an
application's settings.
BREAKING CHANGE: run_device_action is removed; callers use the
templates. Requires napalm-device-types 4.12.
Closes#27
Mixes in napalm-device-types 4.9's SystemIdentityMixin: the host's
SMBIOS system UUID, product serial and board serial, read as root on an
exec channel, vendor placeholders empty. netOrk links a server's BMC to
the host on these.
napalm-device-types 4.7 changed three things this driver depends on:
- parse_dnf_security needs the "__DNF_RC=<status>" line that
DNF_SECURITY_COMMAND prints and raises ValueError without it or when dnf
failed. The yum security query had no such line, so listing updates on
any yum host raised. _YUM_SECURITY_COMMAND now prints the marker the same
way ("{ ...; echo __DNF_RC=$?; } | cat"). _rpm_security_names checks
__DNF_RC itself and turns a failed query, or output cut short before the
marker, into None (security unknown) rather than "no advisories" or an
exception.
- OSDriver.get_users was renamed to get_os_users, since NAPALM's own
get_users has another shape. LinuxDriver.get_users is now get_os_users
with the same return value.
- run_on_transport reads an exec channel to EOF after its exit status
arrives; the tests' fake channels gain recv/recv_stderr for that.
Require napalm-device-types>=4.7.1 and bump the version to 0.6.0.
Now that a whole _sudo line runs as root, `mv /tmp/netork_snmpd.conf
/etc/snmp/snmpd.conf && chown root:root … && chmod 644 …` would have let a
local user plant a symlink at the fixed /tmp path and have root's chown and
chmod follow it (e.g. to /etc/shadow). The same held for 00proxy.
_install_file_as_root stages the content unprivileged in a fresh mktemp file
(umask 077), checks the path it got back, and root installs it as a new
regular file next to the target and renames it over: nothing root runs
follows a symlink or changes the owner or mode of an existing path. Nothing
runs as root when staging failed.
get_vpn_tunnels always reads `wg show all dump` from the exec channel; the
predictable /tmp/.netork_wg_<hash> file holding the private keys is gone.
_sudo typed `echo '<password>' | sudo -S -p "" <command>` into netmiko's
interactive shell. bash kept it in its history, so every poll wrote the sudo
password into the login user's ~/.bash_history, and while the command ran it
was in the process list for every local user (NetOrk/netork#1039).
_sudo, _sudo_status and _run_privileged with a password now run on an exec
channel like run_command(privileged=True): the password reaches sudo -S on
stdin, there is no PTY to echo it, and the script starts with
`exec </dev/null` so a non-prompting sudo leaves it unread. The whole
command runs as root in one sh -c - before, only the part up to the first
`;`, `&&` or `|` did. The exit status comes from the channel.
The interactive session switches its history off before its first command.
get_vpn_tunnels with a password reads `wg show all dump` from the exec
channel instead of a temp file that held the private keys.
run_command(privileged=True, stdin=data) sent `sudo -S` with stdin =
<sudo password>\n + data. A sudo that does not prompt (NOPASSWD, a cached
timestamp) leaves the password line on stdin, and the command reads it as
the first line of the data - `cat > file` wrote it into the file
(NetOrk/netork#1029).
run_command now raises ValueError for that combination before opening a
channel. Stdin still reaches the command under `sudo -n` (no password set),
for a root login, and for unprivileged commands. Callers stage the data
without privilege and have root install the file. open_stream documents the
same caveat.
LinuxDriver mixes in OpenSshServerMixin, so netOrk can take algorithms
ssh-audit flagged out of what sshd offers. The work is the base's; the
driver brings run_command, which already runs as root over an exec channel.
Requires napalm-device-types 4.5.0; bumps to 0.5.0. NetOrk/netork#1027
LinuxDriver mixes in napalm-device-types' VirtualIpsMixin (4.2.0) and
carries its command on the exec channel: with the awk filter it is about
4 kB, past the line the PTY netmiko holds reliably takes. Root first,
then without it (NetOrk/netork#829).
Version 0.4.0.
get_docker_info(), get_docker_outdated(), reconstruct_docker_run() and
the _docker_bin() hook go, with the image-ID helpers. netOrk reads and
handles containers over the Engine API through open_container_engine()
since NetOrk/netork#765 and no longer calls them (phase 9). The
fix_docker_permissions action stays: it is an OS action.
Version 0.3.0.
netOrk drove this driver's shell through the private `_send` (an
interactive PTY, stdout and stderr merged, no exit code) and opened its
own paramiko connections for Docker. napalm-device-types 2.6.0 makes the
channel public; this implements it (NetOrk/netork#765):
- `run_command()` and `open_stream()` open an exec channel on the SSH
transport netmiko already holds: no second login, no PTY, a real exit
status.
- `privileged=True` follows the driver's existing rules: as root the
command runs directly; with a sudo password it goes through
`sudo -S -p ''` and the password is written to stdin, never onto the
command line; without one `sudo -n` fails at once instead of hanging.
- `ContainerEngineMixin` is mixed in, so `open_container_engine("docker")`
streams the Engine API over `docker system dial-stdio`.
The existing Docker methods are unchanged. Version 0.2.0, requires
napalm-device-types >= 2.6.0.
Refs NAPALM/napalm-device-types#17
The same job as napalm-fritzbox and napalm-opnsense: Python 3.10, 3.11 and
3.12, pytest, then wheel and sdist. napalm-device-types comes from
git.netork.io first, because PyPI has an unrelated package of that name.
LinuxDriver mixes in ListeningSocketsMixin (napalm-device-types 2.4.0) and
supplies its transport: privileged readings go through _run_privileged --
as they are for a root login, through sudo with a password, sudo -n
without one -- and the rest through _send. The command arrives as one
sh -c argument, so sudo covers the whole script; when sudo refuses, the
mixin reads again without it.
OpenMediaVault and QNAP QTS inherit it; a host without ss raises
ListeningSocketsUnavailable.
For netOrk#658.
netOrk asks a driver for reboot_host before it offers a restart, and the
Linux driver had none, so a Linux host -- and OpenMediaVault and QTS, which
inherit this driver -- could not be restarted from netOrk at all (netOrk
#637). reboot_host runs /sbin/reboot detached and two seconds late through
_run_privileged: the launcher's exit status comes back before the host goes
down, and closing the session cannot take the restart with it. A refusal
(sudo without a password) raises with what the host said.
_send waited for any output ending in "#", "$" or ">". netmiko matches that
against everything read so far, so a chunk that happened to end in such a
line ended the read while the command still ran. apt's bad-signature line,
"... <ftpmaster@ubuntu.com>", did exactly that: refresh_available_updates
returned half its output, and the rest -- exit status and prompt included --
arrived as the next command's output, so the update read after it failed
with "no exit status" (netOrk #615, seen on nine hosts on 2026-10-06).
A command that ends in "echo __NAME=$?" (__NETORK_RC=, and device-types'
__APT_RC= and __SVC_RC=) is now read until that marker, with a number, and
the prompt line after it. The echoed command line carries a literal $? and
cannot match. Every other command keeps the prompt pattern.
For netOrk MVP 5, on napalm-device-types 2.3.0:
- get_available_updates (apt) runs the shared APT_UPGRADABLE_COMMAND and
parse_apt_upgradable: origin and security from apt's suites, and a
ValueError instead of [] when apt failed or its output was cut short.
- dnf/yum: check-update's exit status decides (0 none, 100 updates, anything
else raises); security comes from `updateinfo list --security`, and is None
when dnf cannot say. The repository column becomes the origin.
- refresh_available_updates(): apt-get update, dnf/yum makecache, apk update;
pacman is left out (-Sy without -u invites a partial upgrade).
- HostStatusMixin: reboot required and self-patching, read over SSH.
- _run_privileged(): root runs directly, a sudo password goes through _sudo,
otherwise sudo -n. Shared by service control and the refresh.
- _split_status() drops terminal codes before it looks for the exit status;
a pseudo-terminal left keypad codes in front of the marker.
OpenMediaVault inherits all of it.
napalm-device-types' SystemdServicesMixin (2.2.0) now provides get_services()
and manage_service(); this driver supplies only the transport (#7):
- _run_service_command(): unprivileged reads and root logins run as they
are -- the user is asked once per session with "id -u", so a root login on a
box without sudo is not prefixed with one. With a sudo password the command
goes through _sudo(); without one through "sudo -n", which fails at once
instead of hanging the session on a password prompt until the read timeout.
- get_services(): one round trip instead of an is-enabled and a show per unit
(6.0 s -> 0.8 s on a 184-unit Ubuntu host). A host without systemd still
falls back to "service --status-all".
- manage_service(): when sudo wants a password netOrk does not have, the
failure says how to fix it -- the same hint the apt and SNMP actions give,
now one constant (_SUDO_PASSWORD_HINT) instead of two copies.
OpenMediaVault and QNAP inherit this driver. OMV gets service control with
it; QNAP opts out (napalm-qnap-qts), since QTS has no systemd.
README: the sudo option is sudo_password, not secret; manage_service and the
systemctl permissions are listed.
Closes#7
LinuxDriver mixes in KernelFactsMixin from napalm-device-types and supplies
only the transport: the shared read-only command over the existing SSH
session, no sudo, one round trip. OpenMediaVault and QNAP inherit it.
Requires napalm-device-types 2.1.0.