Module examples: sysctl¶
Scripts under examples/modules/sysctl/ — one per fact or operation of the
sysctl module. See the
Examples overview for the shared "connects for real"
warning and the error-reporting contract every script follows.
The read-only fact-gathering examples (sysctl_sysctl.py reading Sysctl
for three named keys, sysctl_sysctl_file.py reading SysctlFile for
/etc/sysctl.conf and sysctl_sysctl_files.py reading SysctlFiles across
the standard persistence paths, all over gather_fact) are not rendered
here — they read a single fact, and the facts themselves are documented on
the sysctl module page. The runtime fact's
example relocated here from examples/modules/server/server_sysctl.py when
the Sysctl fact moved to this namespace.
Both operation examples read the shared project-root inventory.json,
guard every request with when=is_centos (the web1/web2 hosts) and flag
their requests sudo=True — writing root-owned /etc
configuration and applying kernel settings needs root, while the spool
transfer itself stays unprivileged. They write kernel tuning and reload it,
so they are compile-checked and exercised by the offline example harness
rather than run against shared hosts; point INVENTORY_PATH at your own
file of throwaway machines to execute them for real.
Host-policy prerequisite. sysctl (from procps-ng) is already
installed and the requests are correctly flagged sudo=True, but applying
a kernel key (sysctl -w) and writing /etc both require an unconfined
sudo domain. On the demo hosts sudo lands in the confined staff_t
domain, so sysctl -w vm.swappiness fails with "permission denied on key"
and the drop-in install fails reading its /tmp spool / writing
/etc/sysctl.d — a host-policy prerequisite, not a script gap. Map the
login's sudo role to an unconfined domain (e.g. unconfined_t) before
these examples can apply; the read-only fact gathers are unaffected.
Managing a single kernel parameter¶
The setting operation: sysctl.setting is a reemote original superseding
the baseline's server.sysctl recipe. It manages one parameter along two
independent halves — the runtime apply (sysctl -w, gated by the
Sysctl fact with the baseline's value
normalization) and the persistence edit (a keyed line replacement gated by
SysctlFile) — each switchable
via sysctl_set and persist. present=False removes the key from the
persistence file (runtime values stay), sysctl_file targets a
/etc/sysctl.d drop-in instead of the legacy /etc/sysctl.conf, and
ignore_errors=True tolerates unknown or read-only keys on heterogeneous
fleets. The script yields the same request twice to show idempotence across
the two phases: prepare plans both as changes, while apply writes on the
first and reports the second as a no-op.
sysctl_setting
¶
Ensure one kernel parameter with sysctl.setting -- runtime and disk.
This is a usage example, not part of the library. It shows how an application drives reemote's two-phase execution model end to end over a real SSH transport (asyncssh):
- Read
inventory.jsonin the project root and parse it as JSON -- an inventory is just a list of dictionaries, which is exactly what JSON expresses -- then validate it up front withvalidate_inventoryso a malformed file fails with anInventoryErrornaming the offending entry, before any host is materialized or connected. - Build the deploy script as a sync, branch-free generator yielding
OperationRequestobjects viaop. - Run the prepare phase to get a plan, and the apply phase to execute it.
Unlike the pytest suite -- which injects the test framework's SimulatedHost
via run's host_factory and never touches the network -- this example
uses the default factory (Host.from_inventory), opening real asyncssh
connections that run closes when it finishes.
sysctl.setting is a reemote original superseding the baseline's basic
server.sysctl: two independent idempotent halves, each gated by its own
fact. The runtime half compares the desired value against the Sysctl
fact (the baseline's normalization: digits compare as ints, a multi-word value as
a list) and applies sysctl -w only on a difference; sysctl_set=False
skips it. The persist half performs a keyed line replacement in
sysctl_file (default /etc/sysctl.conf) -- replace the key's line in
place, append when absent, never touch comments -- gated by the
SysctlFile content fact; persist=False skips it and present=False
removes the key's lines instead (the runtime value is left alone). A changed
write is followed by a scoped sysctl -p <sysctl_file> reload unless
reload=False, and ignore_errors=True guards the runtime command with
|| true for cross-platform key sets.
The write follows the systemd.dropin spool pattern: an unprivileged
scp transfer spools the edited text into a dest-derived /tmp directory, then an
elevated sh -c install copies it into place 0644 and removes the
spool. Only the shell steps are escalated -- the transfer itself never is --
so the request is flagged sudo=True while the file lands root-owned.
The script yields the same request twice to show idempotence across the two phases: the prepare phase plans both as changes when the setting is absent (nothing is sent in prepare), while the apply phase converges on the first and reports the second as a no-op, because the fresh per-step fact cache now sees the value the first step applied.
This operation tunes the running kernel and writes root-owned
configuration. It is compile-checked and exercised by the offline example
harness rather than run against shared hosts; point INVENTORY_PATH at your own
throwaway machines to execute it for real.
Every request carries when=is_centos, the declarative eligibility
guard: the predicate reads only static inventory data --
host.group, set by the "group" key of the inventory entry -- and
is a stable module-level function, so the prepare and apply phases reach
the same skip decision. The driver skips a rejected request without
materializing it, so on hosts outside the "centos" group neither the
commands nor the facts behind them run, and plan and log render the step
as [skipped: when guard is_centos] in its lockstep slot. The path is
resolved from __file__, so the example works from any working
directory; point INVENTORY_PATH at your own file to target your
machines.
Run it with::
uv run python examples/modules/sysctl/sysctl_setting.py
Managing a whole drop-in file¶
The conf operation: sysctl.conf is a reemote original for bulk
persistence — the whole file's content is the rendered settings pairs,
one key = value line each in the given order, written only when it differs
from what SysctlFile reads
back (the systemd.dropin whole-file model, not setting's keyed edit).
The default target is the /etc/sysctl.d/99-reemote.conf drop-in, and a
changed write is followed by a scoped sysctl -p <sysctl_file> reload so
the settings apply immediately (reload=False defers them to the next
boot; present=False removes the file instead). settings is a tuple of
pairs, not a dict: op() rejects unhashable request arguments. The write
follows the same spool-and-install pattern — an unprivileged scp to /tmp,
then an elevated install creating /etc/sysctl.d if needed and landing the
file 0644.
sysctl_conf
¶
Manage a whole sysctl drop-in file with sysctl.conf.
This is a usage example, not part of the library. It shows how an application drives reemote's two-phase execution model end to end over a real SSH transport (asyncssh):
- Read
inventory.jsonin the project root and parse it as JSON -- an inventory is just a list of dictionaries, which is exactly what JSON expresses -- then validate it up front withvalidate_inventoryso a malformed file fails with anInventoryErrornaming the offending entry, before any host is materialized or connected. - Build the deploy script as a sync, branch-free generator yielding
OperationRequestobjects viaop. - Run the prepare phase to get a plan, and the apply phase to execute it.
Unlike the pytest suite -- which injects the test framework's SimulatedHost
via run's host_factory and never touches the network -- this example
uses the default factory (Host.from_inventory), opening real asyncssh
connections that run closes when it finishes.
sysctl.conf is a reemote original for bulk persistence: the whole
file's content is the rendered settings pairs, one key = value line
each in the given order, written only when it differs from what the
SysctlFile fact reads back (the systemd.dropin whole-file model, not
sysctl.setting's keyed edit -- stale lines are replaced, not kept). The
default target is the /etc/sysctl.d/99-reemote.conf drop-in, so the
legacy /etc/sysctl.conf and other packages' drop-ins stay untouched.
settings is a tuple of pairs, not a dict: op() rejects unhashable
request arguments. A changed write is followed by a scoped
sysctl -p <sysctl_file> reload so the settings apply at runtime
immediately (reload=False defers them to the next boot);
present=False removes the file instead, leaving runtime values alone.
The write follows the systemd.dropin spool pattern: an unprivileged
scp transfer spools the rendered text into a dest-derived /tmp directory, then an
elevated sh -c install creates /etc/sysctl.d if needed, copies
the file into place 0644 and removes the spool. Only the shell steps are
escalated -- the transfer itself never is -- so the request is flagged
sudo=True while the file lands root-owned.
The script yields the same request twice to show idempotence across the two phases: the prepare phase plans both as changes when the file is absent (nothing is sent in prepare), while the apply phase writes on the first and reports the second as a no-op, because the fresh per-step fact cache now sees the content the first step installed.
This operation writes root-owned configuration and reloads kernel
settings. It is compile-checked and exercised by the offline example
harness rather than run against shared hosts; point INVENTORY_PATH at your own
throwaway machines to execute it for real.
Every request carries when=is_centos, the declarative eligibility
guard: the predicate reads only static inventory data --
host.group, set by the "group" key of the inventory entry -- and
is a stable module-level function, so the prepare and apply phases reach
the same skip decision. The driver skips a rejected request without
materializing it, so on hosts outside the "centos" group neither the
commands nor the facts behind them run, and plan and log render the step
as [skipped: when guard is_centos] in its lockstep slot. The path is
resolved from __file__, so the example works from any working
directory; point INVENTORY_PATH at your own file to target your
machines.
Run it with::
uv run python examples/modules/sysctl/sysctl_conf.py