Adds SystemdServicesMixin to napalm_device_types/systemd.py. It does three things:
lists a host's systemd services in one round trip;
starts, stops, restarts, enables or disables one of them;
reads only the exit status to decide whether an action worked.
Drivers supply only _run_service_command(command, *, privileged, timeout). The commit message has the details. This goes first: napalm-linux#7, napalm-proxmox#6 and netOrk#576 build on it. Version 2.2.0.
Checked against real hosts (netOrk test server)
The new command and parser ran next to each driver's current get_services():
Host
current
new
running state
enabled
Ubuntu 24.04, 184 units
6.0 s
0.8 s
identical
enabled-runtime now counts (4 units)
Debian 13, 125 units
11.5 s
3.9 s
identical
enabled-runtime (3)
OpenMediaVault (Debian 13)
5.5 s
1.5 s
identical
enabled-runtime (7); a SysV generated unit is read via is-enabled
On Proxmox, templates, static unit files and aliases drop out.
manage_service("<unit that does not exist>", "restart") went through each driver's real transport, sudo or root included, and came back as a failure carrying systemctl's own message.
Tests
$ PYTHONPATH=. pytest -q tests
269 passed
$ ruff check --isolated --line-length 100 napalm_device_types/systemd.py tests/test_systemd_services.py
All checks passed!
$ mypy napalm_device_types # strict; the remaining errors are in base.py and _ucd_metrics.py, untouched
test_it_runs_and_parses_on_this_host runs the real command on any machine with systemd.
Adds `SystemdServicesMixin` to `napalm_device_types/systemd.py`. It does three things:
- lists a host's systemd services in one round trip;
- starts, stops, restarts, enables or disables one of them;
- reads only the exit status to decide whether an action worked.
Drivers supply only `_run_service_command(command, *, privileged, timeout)`. The commit message has the details. This goes first: napalm-linux#7, napalm-proxmox#6 and netOrk#576 build on it. Version 2.2.0.
## Checked against real hosts (netOrk test server)
The new command and parser ran next to each driver's current `get_services()`:
| Host | current | new | running state | enabled |
|---|---|---|---|---|
| Ubuntu 24.04, 184 units | 6.0 s | 0.8 s | identical | `enabled-runtime` now counts (4 units) |
| Debian 13, 125 units | 11.5 s | 3.9 s | identical | `enabled-runtime` (3) |
| OpenMediaVault (Debian 13) | 5.5 s | 1.5 s | identical | `enabled-runtime` (7); a SysV `generated` unit is read via `is-enabled` |
| Proxmox VE 9 | 1.8 s | 1.7 s | identical | 63 units now `enabled` (#6) |
The listings differ in a few ways, all intended:
- Units that are `not-found` drop out.
- Installed unit files that are not loaded come in.
- On Proxmox, templates, static unit files and aliases drop out.
`manage_service("<unit that does not exist>", "restart")` went through each driver's real transport, sudo or root included, and came back as a failure carrying systemctl's own message.
## Tests
```
$ PYTHONPATH=. pytest -q tests
269 passed
$ ruff check --isolated --line-length 100 napalm_device_types/systemd.py tests/test_systemd_services.py
All checks passed!
$ mypy napalm_device_types # strict; the remaining errors are in base.py and _ucd_metrics.py, untouched
```
`test_it_runs_and_parses_on_this_host` runs the real command on any machine with systemd.
Listing a host's services and starting or stopping one is the same on every
host that runs systemd, so the command, its parse, the check of a unit name
and the reading of an action's exit status live here once, and a driver
supplies only the transport (napalm-linux#7, napalm-proxmox#6):
- SYSTEMD_SERVICES_COMMAND: one read-only POSIX sh line. list-unit-files, then
one systemctl show over every loaded service unit (Id, Names, LoadState,
ActiveState, SubState, UnitFileState, MainPID), and is-enabled only for
generated units, whose boot state lives in a SysV script's rc links. Framed;
[no-systemd] when /run/systemd/system is missing. Replaces an is-enabled and
a show per unit: 0.8 s instead of 6 s on a 180-unit Ubuntu host.
- parse_systemd_services(): loaded units except not-found, plus installed unit
files that are not loaded; no templates, no aliases (also not the ones older
systemd lists as "enabled"). enabled = UnitFileState enabled or
enabled-runtime, read from systemctl show and never from list-unit-files'
second column, which has had a preset column after it since systemd 245.
A report whose end is missing raises ValueError, so a list cut short never
reads as services that went away; a host without systemd raises
SystemdUnavailable, a NotImplementedError, so a driver can fall back.
- unit_name() / service_action_command() / parse_action_result(): template
instances, dots, colons and \xHH escapes accepted; a bare template, a leading
"-" and anything a shell reads refused. The action runs as
"timeout 45 systemctl --no-ask-password <action> -- <unit>.service" with its
exit status printed after it; only that status decides, 124 is not called
done, and terminal colour codes are dropped. The marker also keeps the output
from ever being empty, which a transport that retries on an empty answer
would take as a reason to run the action twice.
- SystemdServicesMixin, in the template form: get_services() and
manage_service() are concrete, _run_service_command(command, *, privileged,
timeout) is the driver's hook. Mixed in by the drivers whose host runs
systemd, not by OSDriver.
Version 2.2.0.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Adds
SystemdServicesMixintonapalm_device_types/systemd.py. It does three things:Drivers supply only
_run_service_command(command, *, privileged, timeout). The commit message has the details. This goes first: napalm-linux#7, napalm-proxmox#6 and netOrk#576 build on it. Version 2.2.0.Checked against real hosts (netOrk test server)
The new command and parser ran next to each driver's current
get_services():enabled-runtimenow counts (4 units)enabled-runtime(3)enabled-runtime(7); a SysVgeneratedunit is read viais-enabledenabled(#6)The listings differ in a few ways, all intended:
not-founddrop out.manage_service("<unit that does not exist>", "restart")went through each driver's real transport, sudo or root included, and came back as a failure carrying systemctl's own message.Tests
test_it_runs_and_parses_on_this_hostruns the real command on any machine with systemd.