diff --git a/nixos/modules/services/backup/znapzend.nix b/nixos/modules/services/backup/znapzend.nix index 7f2c89edbf37..a3ea8b209bb6 100644 --- a/nixos/modules/services/backup/znapzend.nix +++ b/nixos/modules/services/backup/znapzend.nix @@ -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 ]; }