nixos/services.znapzend: remove with lib;

This commit is contained in:
Felix Buehler 2024-08-27 20:42:53 +02:00
parent 8cf91e2c5b
commit fcfa7fa441

View File

@ -1,8 +1,4 @@
{ config, lib, pkgs, ... }:
with lib;
with types;
let
planDescription = ''
@ -29,46 +25,46 @@ let
planExample = "1h=>10min,1d=>1h,1w=>1d,1m=>1w,1y=>1m";
# A type for a string of the form number{b|k|M|G}
mbufferSizeType = str // {
check = x: str.check x && builtins.isList (builtins.match "^[0-9]+[bkMG]$" x);
mbufferSizeType = lib.types.str // {
check = x: lib.types.str.check x && builtins.isList (builtins.match "^[0-9]+[bkMG]$" x);
description = "string of the form number{b|k|M|G}";
};
enabledFeatures = concatLists (mapAttrsToList (name: enabled: optional enabled name) cfg.features);
enabledFeatures = lib.concatLists (lib.mapAttrsToList (name: enabled: lib.optional enabled name) cfg.features);
# Type for a string that must contain certain other strings (the list parameter).
# Note that these would need regex escaping.
stringContainingStrings = list: let
matching = s: map (str: builtins.match ".*${str}.*" s) list;
in str // {
check = x: str.check x && all isList (matching x);
description = "string containing all of the characters ${concatStringsSep ", " list}";
in lib.types.str // {
check = x: lib.types.str.check x && lib.all lib.isList (matching x);
description = "string containing all of the characters ${lib.concatStringsSep ", " list}";
};
timestampType = stringContainingStrings [ "%Y" "%m" "%d" "%H" "%M" "%S" ];
destType = srcConfig: submodule ({ name, ... }: {
destType = srcConfig: lib.types.submodule ({ name, ... }: {
options = {
label = mkOption {
type = str;
label = lib.mkOption {
type = lib.types.str;
description = "Label for this destination. Defaults to the attribute name.";
};
plan = mkOption {
type = str;
plan = lib.mkOption {
type = lib.types.str;
description = planDescription;
example = planExample;
};
dataset = mkOption {
type = str;
dataset = lib.mkOption {
type = lib.types.str;
description = "Dataset name to send snapshots to.";
example = "tank/main";
};
host = mkOption {
type = nullOr str;
host = lib.mkOption {
type = lib.types.nullOr lib.types.str;
description = ''
Host to use for the destination dataset. Can be prefixed with
`user@` to specify the ssh user.
@ -77,8 +73,8 @@ let
example = "john@example.com";
};
presend = mkOption {
type = nullOr str;
presend = lib.mkOption {
type = lib.types.nullOr lib.types.str;
description = ''
Command to run before sending the snapshot to the destination.
Intended to run a remote script via {command}`ssh` on the
@ -89,8 +85,8 @@ let
example = "ssh root@bserv zpool import -Nf tank";
};
postsend = mkOption {
type = nullOr str;
postsend = lib.mkOption {
type = lib.types.nullOr lib.types.str;
description = ''
Command to run after sending the snapshot to the destination.
Intended to run a remote script via {command}`ssh` on the
@ -103,37 +99,37 @@ let
};
config = {
label = mkDefault name;
plan = mkDefault srcConfig.plan;
label = lib.mkDefault name;
plan = lib.mkDefault srcConfig.plan;
};
});
srcType = submodule ({ name, config, ... }: {
srcType = lib.types.submodule ({ name, config, ... }: {
options = {
enable = mkOption {
type = bool;
enable = lib.mkOption {
type = lib.types.bool;
description = "Whether to enable this source.";
default = true;
};
recursive = mkOption {
type = bool;
recursive = lib.mkOption {
type = lib.types.bool;
description = "Whether to do recursive snapshots.";
default = false;
};
mbuffer = {
enable = mkOption {
type = bool;
enable = lib.mkOption {
type = lib.types.bool;
description = "Whether to use {command}`mbuffer`.";
default = false;
};
port = mkOption {
type = nullOr ints.u16;
port = lib.mkOption {
type = lib.types.nullOr lib.types.ints.u16;
description = ''
Port to use for {command}`mbuffer`.
@ -147,7 +143,7 @@ let
default = null;
};
size = mkOption {
size = lib.mkOption {
type = mbufferSizeType;
description = ''
The size for {command}`mbuffer`.
@ -158,32 +154,32 @@ let
};
};
presnap = mkOption {
type = nullOr str;
presnap = lib.mkOption {
type = lib.types.nullOr lib.types.str;
description = ''
Command to run before snapshots are taken on the source dataset,
e.g. for database locking/flushing. See also
{option}`postsnap`.
'';
default = null;
example = literalExpression ''
example = lib.literalExpression ''
'''''${pkgs.mariadb}/bin/mysql -e "set autocommit=0;flush tables with read lock;\\! ''${pkgs.coreutils}/bin/sleep 600" & ''${pkgs.coreutils}/bin/echo $! > /tmp/mariadblock.pid ; sleep 10'''
'';
};
postsnap = mkOption {
type = nullOr str;
postsnap = lib.mkOption {
type = lib.types.nullOr lib.types.str;
description = ''
Command to run after snapshots are taken on the source dataset,
e.g. for database unlocking. See also {option}`presnap`.
'';
default = null;
example = literalExpression ''
example = lib.literalExpression ''
"''${pkgs.coreutils}/bin/kill `''${pkgs.coreutils}/bin/cat /tmp/mariadblock.pid`;''${pkgs.coreutils}/bin/rm /tmp/mariadblock.pid"
'';
};
timestampFormat = mkOption {
timestampFormat = lib.mkOption {
type = timestampType;
description = ''
The timestamp format to use for constructing snapshot names.
@ -197,8 +193,8 @@ let
example = "znapzend-%m.%d.%Y-%H%M%SZ";
};
sendDelay = mkOption {
type = int;
sendDelay = lib.mkOption {
type = lib.types.int;
description = ''
Specify delay (in seconds) before sending snaps to the destination.
May be useful if you want to control sending time.
@ -207,23 +203,23 @@ let
example = 60;
};
plan = mkOption {
type = str;
plan = lib.mkOption {
type = lib.types.str;
description = planDescription;
example = planExample;
};
dataset = mkOption {
type = str;
dataset = lib.mkOption {
type = lib.types.str;
description = "The dataset to use for this source.";
example = "tank/home";
};
destinations = mkOption {
type = attrsOf (destType config);
destinations = lib.mkOption {
type = lib.types.attrsOf (destType config);
description = "Additional destinations.";
default = {};
example = literalExpression ''
example = lib.literalExpression ''
{
local = {
dataset = "btank/backup";
@ -240,7 +236,7 @@ let
};
config = {
dataset = mkDefault name;
dataset = lib.mkDefault name;
};
});
@ -251,18 +247,18 @@ let
onOff = b: if b then "on" else "off";
nullOff = b: if b == null then "off" else toString b;
stripSlashes = replaceStrings [ "/" ] [ "." ];
stripSlashes = lib.replaceStrings [ "/" ] [ "." ];
attrsToFile = config: concatStringsSep "\n" (builtins.attrValues (
mapAttrs (n: v: "${n}=${v}") config));
attrsToFile = config: lib.concatStringsSep "\n" (builtins.attrValues (
lib.mapAttrs (n: v: "${n}=${v}") config));
mkDestAttrs = dst: with dst;
mapAttrs' (n: v: nameValuePair "dst_${label}${n}" v) ({
"" = optionalString (host != null) "${host}:" + dataset;
lib.mapAttrs' (n: v: lib.nameValuePair "dst_${label}${n}" v) ({
"" = lib.optionalString (host != null) "${host}:" + dataset;
_plan = plan;
} // optionalAttrs (presend != null) {
} // lib.optionalAttrs (presend != null) {
_precmd = presend;
} // optionalAttrs (postsend != null) {
} // lib.optionalAttrs (postsend != null) {
_pstcmd = postsend;
});
@ -270,7 +266,7 @@ let
enabled = onOff enable;
# mbuffer is not referenced by its full path to accommodate non-NixOS systems or differing mbuffer versions between source and target
mbuffer = with mbuffer; if enable then "mbuffer"
+ optionalString (port != null) ":${toString port}" else "off";
+ lib.optionalString (port != null) ":${toString port}" else "off";
mbuffer_size = mbuffer.size;
post_znap_cmd = nullOff postsnap;
pre_znap_cmd = nullOff presnap;
@ -279,11 +275,11 @@ let
src_plan = plan;
tsformat = timestampFormat;
zend_delay = toString sendDelay;
} // foldr (a: b: a // b) {} (
} // lib.foldr (a: b: a // b) {} (
map mkDestAttrs (builtins.attrValues destinations)
);
files = mapAttrs' (n: srcCfg: let
files = lib.mapAttrs' (n: srcCfg: let
fileText = attrsToFile (mkSrcAttrs srcCfg);
in {
name = srcCfg.dataset;
@ -294,20 +290,20 @@ in
{
options = {
services.znapzend = {
enable = mkEnableOption "ZnapZend ZFS backup daemon";
enable = lib.mkEnableOption "ZnapZend ZFS backup daemon";
logLevel = mkOption {
logLevel = lib.mkOption {
default = "debug";
example = "warning";
type = enum ["debug" "info" "warning" "err" "alert"];
type = lib.types.enum ["debug" "info" "warning" "err" "alert"];
description = ''
The log level when logging to file. Any of debug, info, warning, err,
alert. Default in daemonized form is debug.
'';
};
logTo = mkOption {
type = str;
logTo = lib.mkOption {
type = lib.types.str;
default = "syslog::daemon";
example = "/var/log/znapzend.log";
description = ''
@ -315,31 +311,31 @@ in
'';
};
mailErrorSummaryTo = mkOption {
type = singleLineStr;
mailErrorSummaryTo = lib.mkOption {
type = lib.types.singleLineStr;
default = "";
description = ''
Email address to send a summary to if "send task(s) failed".
'';
};
noDestroy = mkOption {
type = bool;
noDestroy = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Does all changes to the filesystem except destroy.";
};
autoCreation = mkOption {
type = bool;
autoCreation = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Automatically create the destination dataset if it does not exist.";
};
zetup = mkOption {
type = attrsOf srcType;
zetup = lib.mkOption {
type = lib.types.attrsOf srcType;
description = "Znapzend configuration.";
default = {};
example = literalExpression ''
example = lib.literalExpression ''
{
"tank/home" = {
# Make snapshots of tank/home every hour, keep those for 1 day,
@ -356,8 +352,8 @@ in
'';
};
pure = mkOption {
type = bool;
pure = lib.mkOption {
type = lib.types.bool;
description = ''
Do not persist any stateful znapzend setups. If this option is
enabled, your previously set znapzend setups will be cleared and only
@ -366,17 +362,17 @@ in
default = false;
};
features.oracleMode = mkEnableOption ''
features.oracleMode = lib.mkEnableOption ''
destroying snapshots one by one instead of using one long argument list.
If source and destination are out of sync for a long time, you may have
so many snapshots to destroy that the argument gets is too long and the
command fails
'';
features.recvu = mkEnableOption ''
features.recvu = lib.mkEnableOption ''
recvu feature which uses `-u` on the receiving end to keep the destination
filesystem unmounted
'';
features.compressed = mkEnableOption ''
features.compressed = lib.mkEnableOption ''
compressed feature which adds the options `-Lce` to
the {command}`zfs send` command. When this is enabled, make
sure that both the sending and receiving pool have the same relevant
@ -387,7 +383,7 @@ in
and {manpage}`zfs(8)`
for more info
'';
features.sendRaw = mkEnableOption ''
features.sendRaw = lib.mkEnableOption ''
sendRaw feature which adds the options `-w` to the
{command}`zfs send` command. For encrypted source datasets this
instructs zfs not to decrypt before sending which results in a remote
@ -396,7 +392,7 @@ in
option must be used consistently, raw incrementals cannot be based on
non-raw snapshots and vice versa
'';
features.skipIntermediates = mkEnableOption ''
features.skipIntermediates = lib.mkEnableOption ''
the skipIntermediates feature to send a single increment
between latest common snapshot and the newly made one. It may skip
several source snaps if the destination was offline for some time, and
@ -404,14 +400,14 @@ in
destinations, the new snapshot is sent as soon as it is created on the
source, so there are no automatic increments to skip
'';
features.lowmemRecurse = mkEnableOption ''
features.lowmemRecurse = lib.mkEnableOption ''
use lowmemRecurse on systems where you have too many datasets, so a
recursive listing of attributes to find backup plans exhausts the
memory available to {command}`znapzend`: instead, go the slower
way to first list all impacted dataset names, and then query their
configs one by one
'';
features.zfsGetType = mkEnableOption ''
features.zfsGetType = lib.mkEnableOption ''
using zfsGetType if your {command}`zfs get` supports a
`-t` argument for filtering by dataset type at all AND
lists properties for snapshots by default when recursing, so that there
@ -425,7 +421,7 @@ in
};
};
config = mkIf cfg.enable {
config = lib.mkIf cfg.enable {
environment.systemPackages = [ pkgs.znapzend ];
systemd.services = {
@ -436,12 +432,12 @@ in
path = with pkgs; [ zfs mbuffer openssh ];
preStart = optionalString cfg.pure ''
preStart = lib.optionalString cfg.pure ''
echo Resetting znapzend zetups
${pkgs.znapzend}/bin/znapzendzetup list \
| grep -oP '(?<=\*\*\* backup plan: ).*(?= \*\*\*)' \
| xargs -I{} ${pkgs.znapzend}/bin/znapzendzetup delete "{}"
'' + concatStringsSep "\n" (mapAttrsToList (dataset: config: ''
'' + lib.concatStringsSep "\n" (lib.mapAttrsToList (dataset: config: ''
echo Importing znapzend zetup ${config} for dataset ${dataset}
${pkgs.znapzend}/bin/znapzendzetup import --write ${dataset} ${config} &
'') files) + ''
@ -458,15 +454,15 @@ in
# Needs to have write access to ZFS
User = "root";
ExecStart = let
args = concatStringsSep " " [
args = lib.concatStringsSep " " [
"--logto=${cfg.logTo}"
"--loglevel=${cfg.logLevel}"
(optionalString cfg.noDestroy "--nodestroy")
(optionalString cfg.autoCreation "--autoCreation")
(optionalString (cfg.mailErrorSummaryTo != "")
(lib.optionalString cfg.noDestroy "--nodestroy")
(lib.optionalString cfg.autoCreation "--autoCreation")
(lib.optionalString (cfg.mailErrorSummaryTo != "")
"--mailErrorSummaryTo=${cfg.mailErrorSummaryTo}")
(optionalString (enabledFeatures != [])
"--features=${concatStringsSep "," enabledFeatures}")
(lib.optionalString (enabledFeatures != [])
"--features=${lib.concatStringsSep "," enabledFeatures}")
]; in "${pkgs.znapzend}/bin/znapzend ${args}";
ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
Restart = "on-failure";
@ -475,5 +471,5 @@ in
};
};
meta.maintainers = with maintainers; [ SlothOfAnarchy ];
meta.maintainers = with lib.maintainers; [ SlothOfAnarchy ];
}