Merge pull request #127245 from lunik1/snapraid-module

nixos/snapraid: init

authored by

Ryan Mulligan and committed by
GitHub
b466d6c7 cd4d1621

+242
+7
nixos/doc/manual/from_md/release-notes/rl-2111.section.xml
··· 78 78 <link linkend="opt-services.vikunja.enable">services.vikunja</link>. 79 79 </para> 80 80 </listitem> 81 + <listitem> 82 + <para> 83 + <link xlink:href="https://www.snapraid.it/">snapraid</link>, a 84 + backup program for disk arrays. Available as 85 + <link linkend="opt-snapraid.enable">snapraid</link>. 86 + </para> 87 + </listitem> 81 88 </itemizedlist> 82 89 </section> 83 90 <section xml:id="sec-release-21.11-incompatibilities">
+4
nixos/doc/manual/release-notes/rl-2111.section.md
··· 23 23 24 24 - [vikunja](https://vikunja.io), a to-do list app. Available as [services.vikunja](#opt-services.vikunja.enable). 25 25 26 + - [snapraid](https://www.snapraid.it/), a backup program for disk arrays. 27 + Available as [snapraid](#opt-snapraid.enable). 28 + 29 + 26 30 ## Backward Incompatibilities {#sec-release-21.11-incompatibilities} 27 31 28 32 - The `staticjinja` package has been upgraded from 1.0.4 to 3.0.1
+1
nixos/modules/module-list.nix
··· 1104 1104 ./tasks/network-interfaces-systemd.nix 1105 1105 ./tasks/network-interfaces-scripted.nix 1106 1106 ./tasks/scsi-link-power-management.nix 1107 + ./tasks/snapraid.nix 1107 1108 ./tasks/swraid.nix 1108 1109 ./tasks/trackpoint.nix 1109 1110 ./tasks/powertop.nix
+230
nixos/modules/tasks/snapraid.nix
··· 1 + { config, lib, pkgs, ... }: 2 + 3 + with lib; 4 + 5 + let cfg = config.snapraid; 6 + in 7 + { 8 + options.snapraid = with types; { 9 + enable = mkEnableOption "SnapRAID"; 10 + dataDisks = mkOption { 11 + default = { }; 12 + example = { 13 + d1 = "/mnt/disk1/"; 14 + d2 = "/mnt/disk2/"; 15 + d3 = "/mnt/disk3/"; 16 + }; 17 + description = "SnapRAID data disks."; 18 + type = attrsOf str; 19 + }; 20 + parityFiles = mkOption { 21 + default = [ ]; 22 + example = [ 23 + "/mnt/diskp/snapraid.parity" 24 + "/mnt/diskq/snapraid.2-parity" 25 + "/mnt/diskr/snapraid.3-parity" 26 + "/mnt/disks/snapraid.4-parity" 27 + "/mnt/diskt/snapraid.5-parity" 28 + "/mnt/disku/snapraid.6-parity" 29 + ]; 30 + description = "SnapRAID parity files."; 31 + type = listOf str; 32 + }; 33 + contentFiles = mkOption { 34 + default = [ ]; 35 + example = [ 36 + "/var/snapraid.content" 37 + "/mnt/disk1/snapraid.content" 38 + "/mnt/disk2/snapraid.content" 39 + ]; 40 + description = "SnapRAID content list files."; 41 + type = listOf str; 42 + }; 43 + exclude = mkOption { 44 + default = [ ]; 45 + example = [ "*.unrecoverable" "/tmp/" "/lost+found/" ]; 46 + description = "SnapRAID exclude directives."; 47 + type = listOf str; 48 + }; 49 + touchBeforeSync = mkOption { 50 + default = true; 51 + example = false; 52 + description = 53 + "Whether <command>snapraid touch</command> should be run before <command>snapraid sync</command>."; 54 + type = bool; 55 + }; 56 + sync.interval = mkOption { 57 + default = "01:00"; 58 + example = "daily"; 59 + description = "How often to run <command>snapraid sync</command>."; 60 + type = str; 61 + }; 62 + scrub = { 63 + interval = mkOption { 64 + default = "Mon *-*-* 02:00:00"; 65 + example = "weekly"; 66 + description = "How often to run <command>snapraid scrub</command>."; 67 + type = str; 68 + }; 69 + plan = mkOption { 70 + default = 8; 71 + example = 5; 72 + description = 73 + "Percent of the array that should be checked by <command>snapraid scrub</command>."; 74 + type = int; 75 + }; 76 + olderThan = mkOption { 77 + default = 10; 78 + example = 20; 79 + description = 80 + "Number of days since data was last scrubbed before it can be scrubbed again."; 81 + type = int; 82 + }; 83 + }; 84 + extraConfig = mkOption { 85 + default = ""; 86 + example = '' 87 + nohidden 88 + blocksize 256 89 + hashsize 16 90 + autosave 500 91 + pool /pool 92 + ''; 93 + description = "Extra config options for SnapRAID."; 94 + type = lines; 95 + }; 96 + }; 97 + 98 + config = 99 + let 100 + nParity = builtins.length cfg.parityFiles; 101 + mkPrepend = pre: s: pre + s; 102 + in 103 + mkIf cfg.enable { 104 + assertions = [ 105 + { 106 + assertion = nParity <= 6; 107 + message = "You can have no more than six SnapRAID parity files."; 108 + } 109 + { 110 + assertion = builtins.length cfg.contentFiles >= nParity + 1; 111 + message = 112 + "There must be at least one SnapRAID content file for each SnapRAID parity file plus one."; 113 + } 114 + ]; 115 + 116 + environment = { 117 + systemPackages = with pkgs; [ snapraid ]; 118 + 119 + etc."snapraid.conf" = { 120 + text = with cfg; 121 + let 122 + prependData = mkPrepend "data "; 123 + prependContent = mkPrepend "content "; 124 + prependExclude = mkPrepend "exclude "; 125 + in 126 + concatStringsSep "\n" 127 + (map prependData 128 + ((mapAttrsToList (name: value: name + " " + value)) dataDisks) 129 + ++ zipListsWith (a: b: a + b) 130 + ([ "parity " ] ++ map (i: toString i + "-parity ") (range 2 6)) 131 + parityFiles ++ map prependContent contentFiles 132 + ++ map prependExclude exclude) + "\n" + extraConfig; 133 + }; 134 + }; 135 + 136 + systemd.services = with cfg; { 137 + snapraid-scrub = { 138 + description = "Scrub the SnapRAID array"; 139 + startAt = scrub.interval; 140 + serviceConfig = { 141 + Type = "oneshot"; 142 + ExecStart = "${pkgs.snapraid}/bin/snapraid scrub -p ${ 143 + toString scrub.plan 144 + } -o ${toString scrub.olderThan}"; 145 + Nice = 19; 146 + IOSchedulingPriority = 7; 147 + CPUSchedulingPolicy = "batch"; 148 + 149 + LockPersonality = true; 150 + MemoryDenyWriteExecute = true; 151 + NoNewPrivileges = true; 152 + PrivateDevices = true; 153 + PrivateTmp = true; 154 + ProtectClock = true; 155 + ProtectControlGroups = true; 156 + ProtectHostname = true; 157 + ProtectKernelLogs = true; 158 + ProtectKernelModules = true; 159 + ProtectKernelTunables = true; 160 + RestrictAddressFamilies = "none"; 161 + RestrictNamespaces = true; 162 + RestrictRealtime = true; 163 + RestrictSUIDSGID = true; 164 + SystemCallArchitectures = "native"; 165 + SystemCallFilter = "@system-service"; 166 + SystemCallErrorNumber = "EPERM"; 167 + CapabilityBoundingSet = "CAP_DAC_OVERRIDE"; 168 + 169 + ProtectSystem = "strict"; 170 + ProtectHome = "read-only"; 171 + ReadWritePaths = 172 + # scrub requires access to directories containing content files 173 + # to remove them if they are stale 174 + let 175 + contentDirs = map dirOf contentFiles; 176 + in 177 + unique ( 178 + attrValues dataDisks ++ contentDirs 179 + ); 180 + }; 181 + unitConfig.After = "snapraid-sync.service"; 182 + }; 183 + snapraid-sync = { 184 + description = "Synchronize the state of the SnapRAID array"; 185 + startAt = sync.interval; 186 + serviceConfig = { 187 + Type = "oneshot"; 188 + ExecStart = "${pkgs.snapraid}/bin/snapraid sync"; 189 + Nice = 19; 190 + IOSchedulingPriority = 7; 191 + CPUSchedulingPolicy = "batch"; 192 + 193 + LockPersonality = true; 194 + MemoryDenyWriteExecute = true; 195 + NoNewPrivileges = true; 196 + PrivateDevices = true; 197 + PrivateTmp = true; 198 + ProtectClock = true; 199 + ProtectControlGroups = true; 200 + ProtectHostname = true; 201 + ProtectKernelLogs = true; 202 + ProtectKernelModules = true; 203 + ProtectKernelTunables = true; 204 + RestrictAddressFamilies = "none"; 205 + RestrictNamespaces = true; 206 + RestrictRealtime = true; 207 + RestrictSUIDSGID = true; 208 + SystemCallArchitectures = "native"; 209 + SystemCallFilter = "@system-service"; 210 + SystemCallErrorNumber = "EPERM"; 211 + CapabilityBoundingSet = "CAP_DAC_OVERRIDE"; 212 + 213 + ProtectSystem = "strict"; 214 + ProtectHome = "read-only"; 215 + ReadWritePaths = 216 + # sync requires access to directories containing content files 217 + # to remove them if they are stale 218 + let 219 + contentDirs = map dirOf contentFiles; 220 + in 221 + unique ( 222 + attrValues dataDisks ++ parityFiles ++ contentDirs 223 + ); 224 + } // optionalAttrs touchBeforeSync { 225 + ExecStartPre = "${pkgs.snapraid}/bin/snapraid touch"; 226 + }; 227 + }; 228 + }; 229 + }; 230 + }