Files
napalm-bsd/README.md
T
Christian Manivong 32a63411d3
CI / test (3.10) (push) Successful in 29s
CI / test (3.11) (push) Successful in 30s
CI / test (3.12) (push) Successful in 29s
CI / test (3.10) (pull_request) Successful in 29s
CI / test (3.11) (pull_request) Successful in 46s
CI / test (3.12) (pull_request) Successful in 42s
feat: say whether a host is still in its first boot
first_boot_pending() is true while /firstboot exists on FreeBSD. A FreeBSD
cloud image upgrades its base system on its first boot, starts sshd only
after that and restarts right away, and /etc/rc removes /firstboot just
before that restart. netOrk waits for this before it sets up a new VM
(NetOrk/netork#795).

OpenBSD has no such marker to ask yet, so it returns False there.
2026-10-08 12:58:00 +02:00

5.6 KiB

napalm-bsd

NAPALM drivers for FreeBSD (freebsd) and OpenBSD (openbsd) hosts, over SSH, built on napalm-device-types' OSDriver. Written for netOrk, which provisions and manages BSD VMs (NetOrk/netork#792).

Design

  • One shared BsdDriver (napalm_bsd/base.py) holds the SSH layer and every reader whose tools agree between the BSDs. FreeBSDDriver and OpenBSDDriver name only the commands that differ (hostname, release, kernel, hardware, netstat -rnW vs -rn).
  • Every command runs on its own SSH exec channel (run_on_transport from napalm-device-types): no PTY to parse a prompt from, stdout and stderr apart, a real exit status. run_command() / open_stream() are public (CommandChannelMixin).
  • Root comes from sudo -S with the sudo password on stdin (never on a command line), or sudo -n without one, as in napalm-linux. netOrk provisions sudo on BSD VMs for that reason; doas cannot read a password from stdin.
  • Parsing is in napalm_bsd/parse.py, pure functions tested on output recorded from real systems (tests/fixtures/, each directory's COMMANDS.txt lists what produced it).

Supported getters

Getter FreeBSD OpenBSD Source
get_facts ✓ ✓ kern.hostname/hostname, freebsd-version/uname, kenv smbios.*/hw.*, kern.boottime
get_interfaces, get_interfaces_ip ✓ ✓ ifconfig -a
get_route_to ✓ ✓ netstat -rnW / netstat -rn
get_arp_table ✓ ✓ arp -an
get_lldp_neighbors {} {} no LLDP daemon in either base system
get_users, get_processes, get_cron_jobs ✓ ✓ /etc/passwd+/etc/group, ps … lstart, system crontab + crontab -l
get_listening_sockets ✓ ✓ sockstat -46lq / fstat -n as root, netstat -an without
get_packages, install_package, uninstall_package ✓ ✓ pkg query / pkg install, pkg delete; pkg_info / pkg_add -I, pkg_delete
get_available_updates ✓ ✓ pkg upgrade -n + pkg audit (VuXML); pkg_add -u -n -v + syspatch -c
get_services, manage_service ✓ ✓ service -e + service … status / service … <action>; rcctl ls on + rcctl check / rcctl <action>
run_device_action("fix_snmp"), get_snmp_config ✓ ✓ net-snmp from packages: /usr/local/etc/snmp/snmpd.conf + service snmpd; /etc/snmp/snmpd.conf + rcctl … netsnmpd
get_host_status ✓ unknown napalm-device-types' host status: on FreeBSD freebsd-version -k vs -r (4.1+)
first_boot_pending ✓ always False whether /firstboot still exists

get_listening_sockets has the shape of napalm-device-types' ListeningSocketsMixin (whose ss/cgroup reading is Linux's) and its rule: read as root first, without root when that brings nothing back (attributed: False). FreeBSD's sockstat sees every socket either way; OpenBSD's fstat shows a user only their own processes, so without root the sockets come from netstat -an, unnamed.

There is deliberately no get_kernel_facts: KernelFactsMixin reports a Linux kernel's modules and CONFIG_* options, which a BSD kernel does not have, and hasattr(driver, "get_kernel_facts") has to stay truthful.

Updates. security on FreeBSD comes from VuXML (pkg audit): True for a package it lists as vulnerable, False for one it does not, None when the audit could not run. OpenBSD's tools do not say, so it is None there. Base-system patches are one entry, base-system (NetOrk/netork#799): OpenBSD's syspatch applies its patches together and in order; a classic FreeBSD base reports what freebsd-update has fetched (updatesready). On FreeBSD with pkgbase the base system is packages from the FreeBSD-base repository and needs no extra entry.

A reader that cannot read raises rather than return an empty list: netOrk takes [] as "no updates" and would close every patch clock on the host. That covers a refused sudo, a failing pkg, syspatch or freebsd-update, and a pkg database pkg cannot read (only pkg's "not installed" means no packages).

Reboot. get_host_status reports a FreeBSD host whose installed kernel (freebsd-version -k) differs from the running one (-r) as needing a reboot. OpenBSD has no such reading yet, so it stays unknown there.

First boot. A FreeBSD cloud image upgrades its base system on its first boot, starts sshd only after that and restarts right away. /etc/rc removes /firstboot just before the restart, so first_boot_pending() is true until then; netOrk waits for it before it sets up a new VM. OpenBSD has no such marker to ask yet.

Services are the enabled ones. FreeBSD reads their status as root, as root-only pidfiles hide a daemon from anyone else, and without root when sudo refuses; OpenBSD's rcctl check needs no root. Actions run as root.

SNMP is net-snmp from packages (NetOrk/netork#800), configured as netOrk does on Linux (v2c, community public, every address), so the agent answers UCD-SNMP-MIB for netOrk's health metrics. Memory, swap and load are right on both systems. CPU: FreeBSD reports ssCpuIdle about a minute after the agent starts; on OpenBSD net-snmp's CPU figures are wrong (0 % idle on an idle machine, also in hrProcessorLoad).

Connection arguments

optional_args: port (22), key_file, sudo_password, allow_agent, look_for_keys (both off by default).

Development

pip install "napalm-device-types @ git+https://git.netork.io/NAPALM/napalm-device-types.git"
pip install -e ".[dev]"
pytest