Third review of #147. The run writes as root into /var/lib/zfs-scrub (0700, root's), apart from the
operator account's error counts in /var/lib/zfs-tools, and refuses a folder that is a link, open to
others or someone else's, and a file that is a link: a new file opened O_NOFOLLOW|O_EXCL, renamed over.
The second review of #147 found three blockers before the home server.
1. ${setting:} was not filled in a unit's name, so the distribution timers would have reached the
machine as "zfs-scrub-weekly@${setting:scrub-pool}.timer". mesh-controller #171 fills it and refuses
any value that is not a unit-name part. The module's own schedule needs no setting in a name now: the
node-engine's timer for the scheduled process zfs-mesh-scrub fires `zfs-tools scrub-run` every quarter
of an hour, and the run reads the pool, cadence, day and time from the configuration file.
2. The run does what upstream's zfs-scrub@.service does: if a scrub is running it waits for it with
zpool wait -t scrub, otherwise it starts one and waits. It never pauses or stops a scrub, and the unit
has no ExecStop. A resilver defers the scrub. The served slot is kept in /var/lib/zfs-tools.
3. Unassigning left the pool with no schedule. mesh-host #66 (hq ADR 0269, proposed) gives the
distribution timer back running and enabled. The README says not to unassign before it is merged.
Also: the run comes before the distribution timers in the declaration; the day and time are checked
against fixed sets and reach no unit file (an invalid value runs nothing); a due scrub not started an
hour after its time is a warning; the error counts outlive a restart in a small state file.
The safety review of the first version found it safe for the pool's data and asked for these fixes before
it goes on the home server:
- zfs_snapshot: no recursive. Every answer gives the dataset's free space and the pool's capacity, and
says that the snapshot holds every block deleted or overwritten until it is destroyed by hand.
- A command that runs out of time is ended with its whole process group. An act that timed out is
answered from the list, or as "outcome unknown", never as "NOT DONE".
- The watch runs as the operator account.
- The event follower reads ZFS's queue from its start, with a cursor per boot, so the pool's import and
any downtime reach the journal once. It writes "dropped N events", kills zpool before waiting for it,
and exits non-zero after five failed follows in a row.
- Past error counts are a warning, and counts that rise are unhealthy.
- The guard test catches .Run(, exec.Command(, exec.CommandContext( and process starts.
- The fixtures and the parser follow OpenZFS 2.4.4's own printers: human scan sizes under -p, no iostat
section lines, error scrubs, per-vdev rebuild resilvers, paused scrubs.
The operator also asked the module to own the scrub. A timer and a service of the module's own run
zpool scrub -w -- <pool> from the scrub-pool, scrub-cadence, scrub-day and scrub-time settings. In the
same declaration, the distribution's weekly and monthly timers for that pool are held stopped and
disabled, so exactly one schedule scrubs it. zfs_scrub shows the schedule and the next run, with any
resilver's progress. zfs_scrub_start, zfs_scrub_pause and zfs_scrub_stop act with why. The health check
warns of a late or failed scrub and of a running resilver, and is unhealthy for a resilver that finished
with errors.
The home server's pool holds the operator's media library, and no mesh tool could say whether it is
healthy, how full it is, when it was last scrubbed or what snapshots exist; the only way was a login.
This module reads all of it with fixed zfs and zpool commands (list, get, status, iostat, events,
history) and has one act, zfs_snapshot, which only adds a snapshot: refused for an unknown dataset, a
name outside [A-Za-z0-9._-], an existing snapshot or no why; run as sudo -n zfs snapshot -- ds@name;
answered from zfs list afterwards, with any other snapshot that came or went meanwhile; recorded as the
event snapshot.taken. No destroy, rollback, set or send exists, and a test holds every command to its
verb.
Health (ADR 0240): zfs_health on the watch process, unhealthy for a pool not ONLINE, device or data
errors, a pool past 90 % full or no ZFS at all; past 80 % and a scrub older than 35 days are warnings.
Beside it, the event follower (zpool events -f, as root) keeps ZFS's errors in its journal past a
reboot and is judged by its unit, the check the module does not run itself. No seat (ADR 0121), no
package: ZFS is the machine's.