Skip to content

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):

  1. Read inventory.json in 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 with validate_inventory so a malformed file fails with an InventoryError naming the offending entry, before any host is materialized or connected.
  2. Build the deploy script as a sync, branch-free generator yielding OperationRequest objects via op.
  3. 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):

  1. Read inventory.json in 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 with validate_inventory so a malformed file fails with an InventoryError naming the offending entry, before any host is materialized or connected.
  2. Build the deploy script as a sync, branch-free generator yielding OperationRequest objects via op.
  3. 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