Merge pull request #83904 from ju1m/sanoid

sanoid: fix sanoid.conf generation

authored by

Sandro and committed by
GitHub
30e2735f 035eabd2

+31 -59
+30 -58
nixos/modules/services/backup/sanoid.nix
··· 10 description = "dataset/template options"; 11 }; 12 13 - # Default values from https://github.com/jimsalterjrs/sanoid/blob/master/sanoid.defaults.conf 14 - 15 commonOptions = { 16 hourly = mkOption { 17 description = "Number of hourly snapshots."; 18 - type = types.ints.unsigned; 19 - default = 48; 20 }; 21 22 daily = mkOption { 23 description = "Number of daily snapshots."; 24 - type = types.ints.unsigned; 25 - default = 90; 26 }; 27 28 monthly = mkOption { 29 description = "Number of monthly snapshots."; 30 - type = types.ints.unsigned; 31 - default = 6; 32 }; 33 34 yearly = mkOption { 35 description = "Number of yearly snapshots."; 36 - type = types.ints.unsigned; 37 - default = 0; 38 }; 39 40 autoprune = mkOption { 41 description = "Whether to automatically prune old snapshots."; 42 - type = types.bool; 43 - default = true; 44 }; 45 46 autosnap = mkOption { 47 description = "Whether to automatically take snapshots."; 48 - type = types.bool; 49 - default = true; 50 - }; 51 - 52 - settings = mkOption { 53 - description = '' 54 - Free-form settings for this template/dataset. See 55 - <link xlink:href="https://github.com/jimsalterjrs/sanoid/blob/master/sanoid.defaults.conf"/> 56 - for allowed values. 57 - ''; 58 - type = datasetSettingsType; 59 - }; 60 - }; 61 - 62 - commonConfig = config: { 63 - settings = { 64 - hourly = mkDefault config.hourly; 65 - daily = mkDefault config.daily; 66 - monthly = mkDefault config.monthly; 67 - yearly = mkDefault config.yearly; 68 - autoprune = mkDefault config.autoprune; 69 - autosnap = mkDefault config.autosnap; 70 }; 71 }; 72 73 - datasetOptions = { 74 - useTemplate = mkOption { 75 description = "Names of the templates to use for this dataset."; 76 - type = (types.listOf (types.enum (attrNames cfg.templates))) // { 77 - description = "list of template names"; 78 - }; 79 default = []; 80 }; 81 82 recursive = mkOption { 83 description = "Whether to recursively snapshot dataset children."; ··· 85 default = false; 86 }; 87 88 - processChildrenOnly = mkOption { 89 description = "Whether to only snapshot child datasets if recursing."; 90 type = types.bool; 91 default = false; 92 }; 93 - }; 94 - 95 - datasetConfig = config: { 96 - settings = { 97 - use_template = mkDefault config.useTemplate; 98 - recursive = mkDefault config.recursive; 99 - process_children_only = mkDefault config.processChildrenOnly; 100 - }; 101 }; 102 103 # Extract pool names from configured datasets ··· 109 else generators.mkValueStringDefault {} v; 110 111 mkKeyValue = k: v: if v == null then "" 112 else generators.mkKeyValueDefault { inherit mkValueString; } "=" k v; 113 in generators.toINI { inherit mkKeyValue; } cfg.settings; 114 - 115 - configDir = pkgs.writeTextDir "sanoid.conf" configFile; 116 117 in { 118 ··· 135 }; 136 137 datasets = mkOption { 138 - type = types.attrsOf (types.submodule ({ config, ... }: { 139 options = commonOptions // datasetOptions; 140 - config = mkMerge [ (commonConfig config) (datasetConfig config) ]; 141 })); 142 default = {}; 143 description = "Datasets to snapshot."; 144 }; 145 146 templates = mkOption { 147 - type = types.attrsOf (types.submodule ({ config, ... }: { 148 options = commonOptions; 149 - config = commonConfig config; 150 - })); 151 default = {}; 152 description = "Templates for datasets."; 153 }; ··· 177 178 config = mkIf cfg.enable { 179 services.sanoid.settings = mkMerge [ 180 - (mapAttrs' (d: v: nameValuePair ("template_" + d) v.settings) cfg.templates) 181 - (mapAttrs (d: v: v.settings) cfg.datasets) 182 ]; 183 184 systemd.services.sanoid = { ··· 191 ExecStart = lib.escapeShellArgs ([ 192 "${pkgs.sanoid}/bin/sanoid" 193 "--cron" 194 - "--configdir" configDir 195 ] ++ cfg.extraArgs); 196 ExecStopPost = map (pool: lib.escapeShellArgs [ 197 "+/run/booted-system/sw/bin/zfs" "unallow" "sanoid" pool
··· 10 description = "dataset/template options"; 11 }; 12 13 commonOptions = { 14 hourly = mkOption { 15 description = "Number of hourly snapshots."; 16 + type = with types; nullOr ints.unsigned; 17 + default = null; 18 }; 19 20 daily = mkOption { 21 description = "Number of daily snapshots."; 22 + type = with types; nullOr ints.unsigned; 23 + default = null; 24 }; 25 26 monthly = mkOption { 27 description = "Number of monthly snapshots."; 28 + type = with types; nullOr ints.unsigned; 29 + default = null; 30 }; 31 32 yearly = mkOption { 33 description = "Number of yearly snapshots."; 34 + type = with types; nullOr ints.unsigned; 35 + default = null; 36 }; 37 38 autoprune = mkOption { 39 description = "Whether to automatically prune old snapshots."; 40 + type = with types; nullOr bool; 41 + default = null; 42 }; 43 44 autosnap = mkOption { 45 description = "Whether to automatically take snapshots."; 46 + type = with types; nullOr bool; 47 + default = null; 48 }; 49 }; 50 51 + datasetOptions = rec { 52 + use_template = mkOption { 53 description = "Names of the templates to use for this dataset."; 54 + type = types.listOf (types.enum (attrNames cfg.templates)); 55 default = []; 56 }; 57 + useTemplate = use_template; 58 59 recursive = mkOption { 60 description = "Whether to recursively snapshot dataset children."; ··· 62 default = false; 63 }; 64 65 + process_children_only = mkOption { 66 description = "Whether to only snapshot child datasets if recursing."; 67 type = types.bool; 68 default = false; 69 }; 70 + processChildrenOnly = process_children_only; 71 }; 72 73 # Extract pool names from configured datasets ··· 79 else generators.mkValueStringDefault {} v; 80 81 mkKeyValue = k: v: if v == null then "" 82 + else if k == "processChildrenOnly" then "" 83 + else if k == "useTemplate" then "" 84 else generators.mkKeyValueDefault { inherit mkValueString; } "=" k v; 85 in generators.toINI { inherit mkKeyValue; } cfg.settings; 86 87 in { 88 ··· 105 }; 106 107 datasets = mkOption { 108 + type = types.attrsOf (types.submodule ({config, options, ...}: { 109 + freeformType = datasetSettingsType; 110 options = commonOptions // datasetOptions; 111 + config.use_template = mkAliasDefinitions (options.useTemplate or {}); 112 + config.process_children_only = mkAliasDefinitions (options.processChildrenOnly or {}); 113 })); 114 default = {}; 115 description = "Datasets to snapshot."; 116 }; 117 118 templates = mkOption { 119 + type = types.attrsOf (types.submodule { 120 + freeformType = datasetSettingsType; 121 options = commonOptions; 122 + }); 123 default = {}; 124 description = "Templates for datasets."; 125 }; ··· 149 150 config = mkIf cfg.enable { 151 services.sanoid.settings = mkMerge [ 152 + (mapAttrs' (d: v: nameValuePair ("template_" + d) v) cfg.templates) 153 + (mapAttrs (d: v: v) cfg.datasets) 154 ]; 155 156 systemd.services.sanoid = { ··· 163 ExecStart = lib.escapeShellArgs ([ 164 "${pkgs.sanoid}/bin/sanoid" 165 "--cron" 166 + "--configdir" (pkgs.writeTextDir "sanoid.conf" configFile) 167 ] ++ cfg.extraArgs); 168 ExecStopPost = map (pool: lib.escapeShellArgs [ 169 "+/run/booted-system/sw/bin/zfs" "unallow" "sanoid" pool
+1 -1
nixos/tests/sanoid.nix
··· 33 34 autosnap = true; 35 }; 36 - datasets."pool/sanoid".useTemplate = [ "test" ]; 37 extraArgs = [ "--verbose" ]; 38 }; 39
··· 33 34 autosnap = true; 35 }; 36 + datasets."pool/sanoid".use_template = [ "test" ]; 37 extraArgs = [ "--verbose" ]; 38 }; 39