diff options
Diffstat (limited to 'nixos/modules/tasks/filesystems/zfs.nix')
-rw-r--r-- | nixos/modules/tasks/filesystems/zfs.nix | 161 |
1 files changed, 91 insertions, 70 deletions
diff --git a/nixos/modules/tasks/filesystems/zfs.nix b/nixos/modules/tasks/filesystems/zfs.nix index 9ca7c6fb343..376d6530f36 100644 --- a/nixos/modules/tasks/filesystems/zfs.nix +++ b/nixos/modules/tasks/filesystems/zfs.nix @@ -17,20 +17,8 @@ let inInitrd = any (fs: fs == "zfs") config.boot.initrd.supportedFilesystems; inSystem = any (fs: fs == "zfs") config.boot.supportedFilesystems; - enableZfs = inInitrd || inSystem; - - kernel = config.boot.kernelPackages; - - packages = if config.boot.zfs.enableUnstable then { - zfs = kernel.zfsUnstable; - zfsUser = pkgs.zfsUnstable; - } else { - zfs = kernel.zfs; - zfsUser = pkgs.zfs; - }; - autosnapPkg = pkgs.zfstools.override { - zfs = packages.zfsUser; + zfs = cfgZfs.package; }; zfsAutoSnap = "${autosnapPkg}/bin/zfs-auto-snapshot"; @@ -111,6 +99,21 @@ in options = { boot.zfs = { + package = mkOption { + readOnly = true; + type = types.package; + default = if config.boot.zfs.enableUnstable then pkgs.zfsUnstable else pkgs.zfs; + defaultText = "if config.boot.zfs.enableUnstable then pkgs.zfsUnstable else pkgs.zfs"; + description = "Configured ZFS userland tools package."; + }; + + enabled = mkOption { + readOnly = true; + type = types.bool; + default = inInitrd || inSystem; + description = "True if ZFS filesystem support is enabled"; + }; + enableUnstable = mkOption { type = types.bool; default = false; @@ -175,14 +178,10 @@ in forceImportAll = mkOption { type = types.bool; - default = true; + default = false; description = '' Forcibly import all ZFS pool(s). - This is enabled by default for backwards compatibility purposes, but it is highly - recommended to disable this option, as it bypasses some of the safeguards ZFS uses - to protect your ZFS pools. - If you set this option to <literal>false</literal> and NixOS subsequently fails to import your non-root ZFS pool(s), you should manually import each pool with "zpool import -f <pool-name>", and then reboot. You should only need to do @@ -304,7 +303,7 @@ in }; services.zfs.autoScrub = { - enable = mkEnableOption "Enables periodic scrubbing of ZFS pools."; + enable = mkEnableOption "periodic scrubbing of ZFS pools"; interval = mkOption { default = "Sun, 02:00"; @@ -328,39 +327,53 @@ in }; }; - services.zfs.zed.settings = mkOption { - type = with types; attrsOf (oneOf [ str int bool (listOf str) ]); - example = literalExample '' - { - ZED_DEBUG_LOG = "/tmp/zed.debug.log"; + services.zfs.zed = { + enableMail = mkEnableOption "ZED's ability to send emails" // { + default = cfgZfs.package.enableMail; + }; + + settings = mkOption { + type = with types; attrsOf (oneOf [ str int bool (listOf str) ]); + example = literalExample '' + { + ZED_DEBUG_LOG = "/tmp/zed.debug.log"; - ZED_EMAIL_ADDR = [ "root" ]; - ZED_EMAIL_PROG = "mail"; - ZED_EMAIL_OPTS = "-s '@SUBJECT@' @ADDRESS@"; + ZED_EMAIL_ADDR = [ "root" ]; + ZED_EMAIL_PROG = "mail"; + ZED_EMAIL_OPTS = "-s '@SUBJECT@' @ADDRESS@"; - ZED_NOTIFY_INTERVAL_SECS = 3600; - ZED_NOTIFY_VERBOSE = false; + ZED_NOTIFY_INTERVAL_SECS = 3600; + ZED_NOTIFY_VERBOSE = false; - ZED_USE_ENCLOSURE_LEDS = true; - ZED_SCRUB_AFTER_RESILVER = false; - } - ''; - description = '' - ZFS Event Daemon /etc/zfs/zed.d/zed.rc content - - See - <citerefentry><refentrytitle>zed</refentrytitle><manvolnum>8</manvolnum></citerefentry> - for details on ZED and the scripts in /etc/zfs/zed.d to find the possible variables - ''; + ZED_USE_ENCLOSURE_LEDS = true; + ZED_SCRUB_AFTER_RESILVER = false; + } + ''; + description = '' + ZFS Event Daemon /etc/zfs/zed.d/zed.rc content + + See + <citerefentry><refentrytitle>zed</refentrytitle><manvolnum>8</manvolnum></citerefentry> + for details on ZED and the scripts in /etc/zfs/zed.d to find the possible variables + ''; + }; }; }; ###### implementation config = mkMerge [ - (mkIf enableZfs { + (mkIf cfgZfs.enabled { assertions = [ { + assertion = cfgZED.enableMail -> cfgZfs.package.enableMail; + message = '' + To allow ZED to send emails, ZFS needs to be configured to enable + this. To do so, one must override the `zfs` package and set + `enableMail` to true. + ''; + } + { assertion = config.networking.hostId != null; message = "ZFS requires networking.hostId to be set"; } @@ -370,20 +383,24 @@ in } ]; - virtualisation.lxd.zfsSupport = true; - boot = { kernelModules = [ "zfs" ]; - extraModulePackages = with packages; [ zfs ]; + + extraModulePackages = [ + (if config.boot.zfs.enableUnstable then + config.boot.kernelPackages.zfsUnstable + else + config.boot.kernelPackages.zfs) + ]; }; boot.initrd = mkIf inInitrd { kernelModules = [ "zfs" ] ++ optional (!cfgZfs.enableUnstable) "spl"; extraUtilsCommands = '' - copy_bin_and_libs ${packages.zfsUser}/sbin/zfs - copy_bin_and_libs ${packages.zfsUser}/sbin/zdb - copy_bin_and_libs ${packages.zfsUser}/sbin/zpool + copy_bin_and_libs ${cfgZfs.package}/sbin/zfs + copy_bin_and_libs ${cfgZfs.package}/sbin/zdb + copy_bin_and_libs ${cfgZfs.package}/sbin/zpool ''; extraUtilsCommandsTest = mkIf inInitrd '' @@ -430,21 +447,22 @@ in '') rootPools)); }; - boot.loader.grub = mkIf inInitrd { + # TODO FIXME See https://github.com/NixOS/nixpkgs/pull/99386#issuecomment-798813567. To not break people's bootloader and as probably not everybody would read release notes that thoroughly add inSystem. + boot.loader.grub = mkIf (inInitrd || inSystem) { zfsSupport = true; }; services.zfs.zed.settings = { - ZED_EMAIL_PROG = mkDefault "${pkgs.mailutils}/bin/mail"; + ZED_EMAIL_PROG = mkIf cfgZED.enableMail (mkDefault "${pkgs.mailutils}/bin/mail"); PATH = lib.makeBinPath [ - packages.zfsUser + cfgZfs.package pkgs.coreutils pkgs.curl pkgs.gawk pkgs.gnugrep pkgs.gnused pkgs.nettools - pkgs.utillinux + pkgs.util-linux ]; }; @@ -465,18 +483,18 @@ in "vdev_clear-led.sh" ] ) - (file: { source = "${packages.zfsUser}/etc/${file}"; }) + (file: { source = "${cfgZfs.package}/etc/${file}"; }) // { "zfs/zed.d/zed.rc".text = zedConf; - "zfs/zpool.d".source = "${packages.zfsUser}/etc/zfs/zpool.d/"; + "zfs/zpool.d".source = "${cfgZfs.package}/etc/zfs/zpool.d/"; }; - system.fsPackages = [ packages.zfsUser ]; # XXX: needed? zfs doesn't have (need) a fsck - environment.systemPackages = [ packages.zfsUser ] + system.fsPackages = [ cfgZfs.package ]; # XXX: needed? zfs doesn't have (need) a fsck + environment.systemPackages = [ cfgZfs.package ] ++ optional cfgSnapshots.enable autosnapPkg; # so the user can run the command to see flags - services.udev.packages = [ packages.zfsUser ]; # to hook zvol naming, etc. - systemd.packages = [ packages.zfsUser ]; + services.udev.packages = [ cfgZfs.package ]; # to hook zvol naming, etc. + systemd.packages = [ cfgZfs.package ]; systemd.services = let getPoolFilesystems = pool: @@ -507,10 +525,11 @@ in Type = "oneshot"; RemainAfterExit = true; }; + environment.ZFS_FORCE = optionalString cfgZfs.forceImportAll "-f"; script = (importLib { # See comments at importLib definition. - zpoolCmd="${packages.zfsUser}/sbin/zpool"; - awkCmd="${pkgs.gawk}/bin/awk"; + zpoolCmd = "${cfgZfs.package}/sbin/zpool"; + awkCmd = "${pkgs.gawk}/bin/awk"; inherit cfgZfs; }) + '' poolImported "${pool}" && exit @@ -525,7 +544,7 @@ in ${optionalString (if isBool cfgZfs.requestEncryptionCredentials then cfgZfs.requestEncryptionCredentials else cfgZfs.requestEncryptionCredentials != []) '' - ${packages.zfsUser}/sbin/zfs list -rHo name,keylocation ${pool} | while IFS=$'\t' read ds kl; do + ${cfgZfs.package}/sbin/zfs list -rHo name,keylocation ${pool} | while IFS=$'\t' read ds kl; do (${optionalString (!isBool cfgZfs.requestEncryptionCredentials) '' if ! echo '${concatStringsSep "\n" cfgZfs.requestEncryptionCredentials}' | grep -qFx "$ds"; then continue @@ -535,10 +554,10 @@ in none ) ;; prompt ) - ${config.systemd.package}/bin/systemd-ask-password "Enter key for $ds:" | ${packages.zfsUser}/sbin/zfs load-key "$ds" + ${config.systemd.package}/bin/systemd-ask-password "Enter key for $ds:" | ${cfgZfs.package}/sbin/zfs load-key "$ds" ;; * ) - ${packages.zfsUser}/sbin/zfs load-key "$ds" + ${cfgZfs.package}/sbin/zfs load-key "$ds" ;; esac) < /dev/null # To protect while read ds kl in case anything reads stdin done @@ -564,7 +583,7 @@ in RemainAfterExit = true; }; script = '' - ${packages.zfsUser}/sbin/zfs set nixos:shutdown-time="$(date)" "${pool}" + ${cfgZfs.package}/sbin/zfs set nixos:shutdown-time="$(date)" "${pool}" ''; }; createZfsService = serv: @@ -590,7 +609,7 @@ in systemd.targets.zfs.wantedBy = [ "multi-user.target" ]; }) - (mkIf (enableZfs && cfgSnapshots.enable) { + (mkIf (cfgZfs.enabled && cfgSnapshots.enable) { systemd.services = let descr = name: if name == "frequent" then "15 mins" else if name == "hourly" then "hour" @@ -628,7 +647,7 @@ in }) snapshotNames); }) - (mkIf (enableZfs && cfgScrub.enable) { + (mkIf (cfgZfs.enabled && cfgScrub.enable) { systemd.services.zfs-scrub = { description = "ZFS pools scrubbing"; after = [ "zfs-import.target" ]; @@ -636,11 +655,11 @@ in Type = "oneshot"; }; script = '' - ${packages.zfsUser}/bin/zpool scrub ${ + ${cfgZfs.package}/bin/zpool scrub ${ if cfgScrub.pools != [] then (concatStringsSep " " cfgScrub.pools) else - "$(${packages.zfsUser}/bin/zpool list -H -o name)" + "$(${cfgZfs.package}/bin/zpool list -H -o name)" } ''; }; @@ -655,18 +674,20 @@ in }; }) - (mkIf (enableZfs && cfgTrim.enable) { + (mkIf (cfgZfs.enabled && cfgTrim.enable) { systemd.services.zpool-trim = { description = "ZFS pools trim"; after = [ "zfs-import.target" ]; - path = [ packages.zfsUser ]; + path = [ cfgZfs.package ]; startAt = cfgTrim.interval; # By default we ignore errors returned by the trim command, in case: # - HDDs are mixed with SSDs # - There is a SSDs in a pool that is currently trimmed. # - There are only HDDs and we would set the system in a degraded state - serviceConfig.ExecStart = ''${pkgs.runtimeShell} -c 'for pool in $(zpool list -H -o name); do zpool trim $pool; done || true' ''; + serviceConfig.ExecStart = "${pkgs.runtimeShell} -c 'for pool in $(zpool list -H -o name); do zpool trim $pool; done || true' "; }; + + systemd.timers.zpool-trim.timerConfig.Persistent = "yes"; }) ]; } |