# Default configuration structure for validateBackup # This file is loaded by the validateBackup script to provide default configuration values. # Variables from $programDefinition hashref (scriptDirectory, scriptFullPath) will be # interpolated at runtime - see ZFS_Utils::interpolateConfig. # # See validateBackup/Documentation/ProblemDefinition.md for the full design and rationale behind # every key below, and USAGE.md (Stage 8, not yet written) for an operator-facing reference. { 'dryrun' => 0, # resolves datasets/snapshots/roots and prints what would run, reads no file # data, when set. Defaults OFF (unlike replicate): validateBackup is read-only # by design, so a monthly cron entry should actually validate, not no-op. 'verbosity' => 1, # 0 is no output, 1 is normal, up to 5 is very verbose (see ZFS_Utils.md) 'debug' => 0, # 0 normal; see the script header for which integer values dump-and-die 'displayLogsOnConsole' => 1, # if set, also print log messages to the console 'displayLogsOnTTY' => '', # if set to a tty device, also write log messages there (/dev/ optional) 'report' => { 'from' => 'root@backup.example.org', # From header on email reports 'email' => 'user@example.com', # who to send the report to (one address/alias only) 'subject' => 'validateBackup Report', # base subject line on e-mail }, 'activeHost' => 'active.example.org', # REQUIRED: hostname of the active (source of truth) server; reached via ssh 'activePrefix' => 'tank', # prepended to each dataset key below to form the active-side dataset path 'backupPrefix' => 'backup', # prepended to each dataset key below to form the backup-side dataset path (unless overridden per-dataset) 'excludeSnap' => '^(hourly|frequent)', # regex on SNAPSHOT NAMES, not dataset names - keeps # automatic snapshots out of the shared-snapshot pool 'snapshot' => '', # if set, pin validation to this exact snapshot name instead of resolving the # newest shared one; must exist on both sides 'datasets' => { # key is the RELATIVE dataset path, combined with activePrefix/backupPrefix above # global defaults for all datasets; a dataset entry below overrides them individually 'recursive' => 0, # expand into child datasets (1) or not (0) 'excludeDataset' => '', # regex on dataset names to exclude from recursive expansion 'files/share' => { # becomes /files/share on the active side 'recursive' => 1, # override global: do expand into child datasets # 'backup' => 'archive/old/files_share', # uncomment to override the mapped backup-side # name instead of /files/share }, }, 'newer' => '', # if set, only compare files with active-side mtime newer than (now - N days) 'older' => '', # if set, only compare files with active-side mtime older than (now - N days) 'filterDuringListing' => 0, # opt-in speed optimization; ProblemDefinition.md's Parameters # section explains why a real mtimeDiff can masquerade as # missing/extra when this is on - leave it off unless you know why 'randFile' => 1000, # 0 disables digesting entirely, 1 digests every eligible file (full mode), # N>1 digests on average 1 file in N 'randSeed' => '', # if set, seeds Perl's rand() for a reproducible sample against the same snapshot 'maxSampleFileSize' => 0, # 0 = no limit; otherwise exclude larger files from sampling 'digestCommand' => 'sha256', # sha256, md5, or cksum are all on FreeBSD base; the threat model # is bit rot/replication error, not tampering, so a faster, # non-cryptographic digest is a legitimate choice on a large pool 'checksumArgsPerCall' => 500, # paths per xargs invocation when batching the digest command 'skipSampleOnMismatch' => 0, # if set, a file already flagged sizeDiff/mtimeDiff is excluded # from digesting; default off, since that file is the single most # informative one available to digest 'scrubMaxAge' => 30, # days; a pool whose last completed scrub is older than this is flagged 'autoscrub' => 0, # start a scrub on a flagged pool and wait for it before validating, when set. # DEFAULT OFF: the one operation here that mutates system state and can # block for hours - see ProblemDefinition.md's "Active-server safety". 'scrubWaitMax' => 28800, # seconds (8h); give up waiting for a scrub and skip validation past this 'scrubPollInterval' => 300, # seconds between zpool status polls while waiting on a scrub 'tempDir' => '/var/tmp/validateBackup', # intermediate listings/sort-spill files (backup server only) 'minTempFreeBytes' => 5000000000, # 5GB; skip a dataset rather than run out of temp space mid-listing 'keepTemp' => 0, # if set, don't delete a dataset's temp files after it finishes (debugging aid) 'findingsDir' => '/findings', # full per-finding detail; the report only carries the first N per dataset 'maxReportFindingsPerDataset' => 20, # findings inlined in the report before pointing at the findings file instead 'maxFindingsPerType' => 100000, # stop writing a given finding type to the findings file past # this many per run; it is clearly systemic by then, and a # ten-million-line findings file helps nobody. Counters stay exact. 'mtimeSlack' => 0, # seconds of mtime difference to tolerate; 0 is correct because zfs send/recv preserves mtime exactly 'parallelListings' => 0, # NOT YET IMPLEMENTED - reserved. Forking the two listings would cut # wall clock on large datasets, but its failure mode overlaps the # truncated-listing trap, so it waits until that is proven on hardware. 'useRsync' => 0, # optional opt-in cross-check using rsync --checksum, snapshot-to-snapshot. # Default OFF - see ProblemDefinition.md's "Decision: full mode is randFile: # 1, not rsync" for why this is a cross-check, not the primary engine. 'rsyncOptions' => '', # extra options appended to the rsync invocation when useRsync is set 'checksumChoice' => '', # passed as --checksum-choice to rsync when set; needs rsync 3.2+ on both ends 'activeMemoryLimitKB' => 2097152, # 2GB address-space ceiling for commands run on the active # side; a killed command is a reported failure, an OOM on # the (5+ year uptime) active server is not recoverable 'sortMemoryLimit' => '500M', # passed as sort -S on the backup side; bounds sort's in-memory # chunk size regardless of file count. '' falls back to sort's own # (uncontrolled) default - not recommended at large file counts 'activeNiceness' => 0, # 0 = don't nice; 1-20 applies 'nice' to the active side's find/stat/digest # commands (run over ssh) so a validation pass yields to production I/O 'backupNiceness' => 0, # same, for the backup side's own find/stat/digest/sort - independently # tunable since the backup host is typically far more quiescent than the # active one and may not need nicing at all 'findCommand' => 'find', # overridable in case an unusual non-interactive PATH doesn't have these 'statCommand' => 'stat', 'rsyncCommand' => 'rsync', 'sessionType' => { # named profiles selectable with --sessionType ; CLI options still # override whatever a profile sets, for that run only. Each profile is a # flat, shallow overlay onto the top-level config - a profile key replaces # the corresponding top-level key wholesale, it does not deep-merge. 'monthly' => { # a plain sampled run, e.g. from a monthly cron entry 'randFile' => 1000, }, 'quarterly' => { # equivalent to --full: digest every file, this is the expensive pass 'randFile' => 1, }, }, }