nixos/zfs: remove incorrect conditional use of spl kmod; remove withs (#344271)

This commit is contained in:
Franz Pletz 2024-09-28 15:30:13 +02:00 committed by GitHub
commit cc2142b9f0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -1,14 +1,10 @@
{ config, lib, options, pkgs, utils, ... }:
{ config, lib, pkgs, utils, ... }:
#
# TODO: zfs tunables
with utils;
with lib;
let
cfgZfs = config.boot.zfs;
optZfs = options.boot.zfs;
cfgExpandOnBoot = config.services.zfs.expandOnBoot;
cfgSnapshots = config.services.zfs.autoSnapshot;
cfgSnapFlags = cfgSnapshots.flags;
@ -17,7 +13,7 @@ let
cfgZED = config.services.zfs.zed;
selectModulePackage = package: config.boot.kernelPackages.${package.kernelModuleAttribute};
clevisDatasets = attrNames (filterAttrs (device: _: any (e: e.fsType == "zfs" && (fsNeededForBoot e) && (e.device == device || hasPrefix "${device}/" e.device)) config.system.build.fileSystems) config.boot.initrd.clevis.devices);
clevisDatasets = lib.attrNames (lib.filterAttrs (device: _: lib.any (e: e.fsType == "zfs" && (utils.fsNeededForBoot e) && (e.device == device || lib.hasPrefix "${device}/" e.device)) config.system.build.fileSystems) config.boot.initrd.clevis.devices);
inInitrd = config.boot.initrd.supportedFilesystems.zfs or false;
inSystem = config.boot.supportedFilesystems.zfs or false;
@ -28,17 +24,17 @@ let
zfsAutoSnap = "${autosnapPkg}/bin/zfs-auto-snapshot";
datasetToPool = x: elemAt (splitString "/" x) 0;
datasetToPool = x: lib.elemAt (lib.splitString "/" x) 0;
fsToPool = fs: datasetToPool fs.device;
zfsFilesystems = filter (x: x.fsType == "zfs") config.system.build.fileSystems;
zfsFilesystems = lib.filter (x: x.fsType == "zfs") config.system.build.fileSystems;
allPools = unique ((map fsToPool zfsFilesystems) ++ cfgZfs.extraPools);
allPools = lib.unique ((map fsToPool zfsFilesystems) ++ cfgZfs.extraPools);
rootPools = unique (map fsToPool (filter fsNeededForBoot zfsFilesystems));
rootPools = lib.unique (map fsToPool (lib.filter utils.fsNeededForBoot zfsFilesystems));
dataPools = unique (filter (pool: !(elem pool rootPools)) allPools);
dataPools = lib.unique (lib.filter (pool: !(lib.elem pool rootPools)) allPools);
snapshotNames = [ "frequent" "hourly" "daily" "weekly" "monthly" ];
@ -89,7 +85,7 @@ let
'';
getPoolFilesystems = pool:
filter (x: x.fsType == "zfs" && (fsToPool x) == pool) config.system.build.fileSystems;
lib.filter (x: x.fsType == "zfs" && (fsToPool x) == pool) config.system.build.fileSystems;
getPoolMounts = prefix: pool:
let
@ -98,36 +94,36 @@ let
# Remove the "/" suffix because even though most mountpoints
# won't have it, the "/" mountpoint will, and we can't have the
# trailing slash in "/sysroot/" in stage 1.
mountPoint = fs: escapeSystemdPath (prefix + (lib.removeSuffix "/" fs.mountPoint));
mountPoint = fs: utils.escapeSystemdPath (prefix + (lib.removeSuffix "/" fs.mountPoint));
hasUsr = lib.any (fs: fs.mountPoint == "/usr") poolFSes;
in
map (x: "${mountPoint x}.mount") poolFSes
++ lib.optional hasUsr "sysusr-usr.mount";
getKeyLocations = pool: if isBool cfgZfs.requestEncryptionCredentials then {
getKeyLocations = pool: if lib.isBool cfgZfs.requestEncryptionCredentials then {
hasKeys = cfgZfs.requestEncryptionCredentials;
command = "${cfgZfs.package}/sbin/zfs list -rHo name,keylocation,keystatus -t volume,filesystem ${pool}";
} else let
keys = filter (x: datasetToPool x == pool) cfgZfs.requestEncryptionCredentials;
keys = lib.filter (x: datasetToPool x == pool) cfgZfs.requestEncryptionCredentials;
in {
hasKeys = keys != [];
command = "${cfgZfs.package}/sbin/zfs list -Ho name,keylocation,keystatus -t volume,filesystem ${toString keys}";
};
createImportService = { pool, systemd, force, prefix ? "" }:
nameValuePair "zfs-import-${pool}" {
lib.nameValuePair "zfs-import-${pool}" {
description = "Import ZFS pool \"${pool}\"";
# We wait for systemd-udev-settle to ensure devices are available,
# but don't *require* it, because mounts shouldn't be killed if it's stopped.
# In the future, hopefully someone will complete this:
# https://github.com/zfsonlinux/zfs/pull/4943
wants = [ "systemd-udev-settle.service" ] ++ optional (config.boot.initrd.clevis.useTang) "network-online.target";
wants = [ "systemd-udev-settle.service" ] ++ lib.optional (config.boot.initrd.clevis.useTang) "network-online.target";
after = [
"systemd-udev-settle.service"
"systemd-modules-load.service"
"systemd-ask-password-console.service"
] ++ optional (config.boot.initrd.clevis.useTang) "network-online.target";
] ++ lib.optional (config.boot.initrd.clevis.useTang) "network-online.target";
requiredBy = getPoolMounts prefix pool ++ [ "zfs-import.target" ];
before = getPoolMounts prefix pool ++ [ "shutdown.target" "zfs-import.target" ];
conflicts = [ "shutdown.target" ];
@ -138,7 +134,7 @@ let
Type = "oneshot";
RemainAfterExit = true;
};
environment.ZFS_FORCE = optionalString force "-f";
environment.ZFS_FORCE = lib.optionalString force "-f";
script = let
keyLocations = getKeyLocations pool;
in (importLib {
@ -157,10 +153,10 @@ let
poolImported "${pool}" || poolImport "${pool}" # Try one last time, e.g. to import a degraded pool.
fi
if poolImported "${pool}"; then
${optionalString config.boot.initrd.clevis.enable (concatMapStringsSep "\n" (elem: "clevis decrypt < /etc/clevis/${elem}.jwe | zfs load-key ${elem} || true ") (filter (p: (elemAt (splitString "/" p) 0) == pool) clevisDatasets))}
${lib.optionalString config.boot.initrd.clevis.enable (lib.concatMapStringsSep "\n" (elem: "clevis decrypt < /etc/clevis/${elem}.jwe | zfs load-key ${elem} || true ") (lib.filter (p: (lib.elemAt (lib.splitString "/" p) 0) == pool) clevisDatasets))}
${optionalString keyLocations.hasKeys ''
${lib.optionalString keyLocations.hasKeys ''
${keyLocations.command} | while IFS=$'\t' read ds kl ks; do
{
if [[ "$ks" != unavailable ]]; then
@ -193,15 +189,15 @@ let
'';
};
zedConf = generators.toKeyValue {
mkKeyValue = generators.mkKeyValueDefault {
zedConf = lib.generators.toKeyValue {
mkKeyValue = lib.generators.mkKeyValueDefault {
mkValueString = v:
if isInt v then toString v
else if isString v then "\"${v}\""
if lib.isInt v then toString v
else if lib.isString v then "\"${v}\""
else if true == v then "1"
else if false == v then "0"
else if isList v then "\"" + (concatStringsSep " " v) + "\""
else err "this value is" (toString v);
else if lib.isList v then "\"" + (lib.concatStringsSep " " v) + "\""
else lib.err "this value is" (toString v);
} "=";
} cfgZED.settings;
in
@ -209,38 +205,38 @@ in
{
imports = [
(mkRemovedOptionModule [ "boot" "zfs" "enableLegacyCrypto" ] "The corresponding package was removed from nixpkgs.")
(mkRemovedOptionModule [ "boot" "zfs" "enableUnstable" ] "Instead set `boot.zfs.package = pkgs.zfs_unstable;`")
(lib.mkRemovedOptionModule [ "boot" "zfs" "enableLegacyCrypto" ] "The corresponding package was removed from nixpkgs.")
(lib.mkRemovedOptionModule [ "boot" "zfs" "enableUnstable" ] "Instead set `boot.zfs.package = pkgs.zfs_unstable;`")
];
###### interface
options = {
boot.zfs = {
package = mkOption {
type = types.package;
package = lib.mkOption {
type = lib.types.package;
default = pkgs.zfs;
defaultText = literalExpression "pkgs.zfs";
defaultText = lib.literalExpression "pkgs.zfs";
description = "Configured ZFS userland tools package, use `pkgs.zfs_unstable` if you want to track the latest staging ZFS branch.";
};
modulePackage = mkOption {
modulePackage = lib.mkOption {
internal = true; # It is supposed to be selected automatically, but can be overridden by expert users.
default = selectModulePackage cfgZfs.package;
type = types.package;
type = lib.types.package;
description = "Configured ZFS kernel module package.";
};
enabled = mkOption {
enabled = lib.mkOption {
readOnly = true;
type = types.bool;
type = lib.types.bool;
default = inInitrd || inSystem;
defaultText = literalMD "`true` if ZFS filesystem support is enabled";
defaultText = lib.literalMD "`true` if ZFS filesystem support is enabled";
description = "True if ZFS filesystem support is enabled";
};
allowHibernation = mkOption {
type = types.bool;
allowHibernation = lib.mkOption {
type = lib.types.bool;
default = false;
description = ''
Allow hibernation support, this may be a unsafe option depending on your
@ -248,8 +244,8 @@ in
'';
};
extraPools = mkOption {
type = types.listOf types.str;
extraPools = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [];
example = [ "tank" "data" ];
description = ''
@ -267,8 +263,8 @@ in
'';
};
devNodes = mkOption {
type = types.path;
devNodes = lib.mkOption {
type = lib.types.path;
default = "/dev/disk/by-id";
description = ''
Name of directory from which to import ZFS devices.
@ -278,8 +274,8 @@ in
'';
};
forceImportRoot = mkOption {
type = types.bool;
forceImportRoot = lib.mkOption {
type = lib.types.bool;
default = true;
description = ''
Forcibly import the ZFS root pool(s) during early boot.
@ -296,8 +292,8 @@ in
'';
};
forceImportAll = mkOption {
type = types.bool;
forceImportAll = lib.mkOption {
type = lib.types.bool;
default = false;
description = ''
Forcibly import all ZFS pool(s).
@ -309,8 +305,8 @@ in
'';
};
requestEncryptionCredentials = mkOption {
type = types.either types.bool (types.listOf types.str);
requestEncryptionCredentials = lib.mkOption {
type = lib.types.either lib.types.bool (lib.types.listOf lib.types.str);
default = true;
example = [ "tank" "data" ];
description = ''
@ -321,8 +317,8 @@ in
'';
};
passwordTimeout = mkOption {
type = types.int;
passwordTimeout = lib.mkOption {
type = lib.types.int;
default = 0;
description = ''
Timeout in seconds to wait for password entry for decrypt at boot.
@ -332,7 +328,7 @@ in
};
removeLinuxDRM = lib.mkOption {
type = types.bool;
type = lib.types.bool;
default = false;
description = ''
Patch the kernel to change symbols needed by ZFS from
@ -345,9 +341,9 @@ in
};
services.zfs.autoSnapshot = {
enable = mkOption {
enable = lib.mkOption {
default = false;
type = types.bool;
type = lib.types.bool;
description = ''
Enable the (OpenSolaris-compatible) ZFS auto-snapshotting service.
Note that you must set the `com.sun:auto-snapshot`
@ -360,10 +356,10 @@ in
'';
};
flags = mkOption {
flags = lib.mkOption {
default = "-k -p";
example = "-k -p --utc";
type = types.str;
type = lib.types.str;
description = ''
Flags to pass to the zfs-auto-snapshot command.
@ -379,41 +375,41 @@ in
'';
};
frequent = mkOption {
frequent = lib.mkOption {
default = 4;
type = types.int;
type = lib.types.int;
description = ''
Number of frequent (15-minute) auto-snapshots that you wish to keep.
'';
};
hourly = mkOption {
hourly = lib.mkOption {
default = 24;
type = types.int;
type = lib.types.int;
description = ''
Number of hourly auto-snapshots that you wish to keep.
'';
};
daily = mkOption {
daily = lib.mkOption {
default = 7;
type = types.int;
type = lib.types.int;
description = ''
Number of daily auto-snapshots that you wish to keep.
'';
};
weekly = mkOption {
weekly = lib.mkOption {
default = 4;
type = types.int;
type = lib.types.int;
description = ''
Number of weekly auto-snapshots that you wish to keep.
'';
};
monthly = mkOption {
monthly = lib.mkOption {
default = 12;
type = types.int;
type = lib.types.int;
description = ''
Number of monthly auto-snapshots that you wish to keep.
'';
@ -421,16 +417,16 @@ in
};
services.zfs.trim = {
enable = mkOption {
enable = lib.mkOption {
description = "Whether to enable periodic TRIM on all ZFS pools.";
default = true;
example = false;
type = types.bool;
type = lib.types.bool;
};
interval = mkOption {
interval = lib.mkOption {
default = "weekly";
type = types.str;
type = lib.types.str;
example = "daily";
description = ''
How often we run trim. For most desktop and server systems
@ -441,9 +437,9 @@ in
'';
};
randomizedDelaySec = mkOption {
randomizedDelaySec = lib.mkOption {
default = "6h";
type = types.str;
type = lib.types.str;
example = "12h";
description = ''
Add a randomized delay before each ZFS trim.
@ -455,11 +451,11 @@ in
};
services.zfs.autoScrub = {
enable = mkEnableOption "periodic scrubbing of ZFS pools";
enable = lib.mkEnableOption "periodic scrubbing of ZFS pools";
interval = mkOption {
interval = lib.mkOption {
default = "monthly";
type = types.str;
type = lib.types.str;
example = "quarterly";
description = ''
Systemd calendar expression when to scrub ZFS pools. See
@ -467,9 +463,9 @@ in
'';
};
randomizedDelaySec = mkOption {
randomizedDelaySec = lib.mkOption {
default = "6h";
type = types.str;
type = lib.types.str;
example = "12h";
description = ''
Add a randomized delay before each ZFS autoscrub.
@ -479,9 +475,9 @@ in
'';
};
pools = mkOption {
pools = lib.mkOption {
default = [];
type = types.listOf types.str;
type = lib.types.listOf lib.types.str;
example = [ "tank" ];
description = ''
List of ZFS pools to periodically scrub. If empty, all pools
@ -490,8 +486,8 @@ in
};
};
services.zfs.expandOnBoot = mkOption {
type = types.either (types.enum [ "disabled" "all" ]) (types.listOf types.str);
services.zfs.expandOnBoot = lib.mkOption {
type = lib.types.either (lib.types.enum [ "disabled" "all" ]) (lib.types.listOf lib.types.str);
default = "disabled";
example = [ "tank" "dozer" ];
description = ''
@ -508,10 +504,10 @@ in
};
services.zfs.zed = {
enableMail = mkOption {
type = types.bool;
enableMail = lib.mkOption {
type = lib.types.bool;
default = config.services.mail.sendmailSetuidWrapper != null;
defaultText = literalExpression ''
defaultText = lib.literalExpression ''
config.services.mail.sendmailSetuidWrapper != null
'';
description = ''
@ -519,9 +515,9 @@ in
'';
};
settings = mkOption {
type = with types; attrsOf (oneOf [ str int bool (listOf str) ]);
example = literalExpression ''
settings = lib.mkOption {
type = let t = lib.types; in t.attrsOf (t.oneOf [ t.str t.int t.bool (t.listOf t.str) ]);
example = lib.literalExpression ''
{
ZED_DEBUG_LOG = "/tmp/zed.debug.log";
@ -549,8 +545,8 @@ in
###### implementation
config = mkMerge [
(mkIf cfgZfs.enabled {
config = lib.mkMerge [
(lib.mkIf cfgZfs.enabled {
assertions = [
{
assertion = cfgZfs.modulePackage.version == cfgZfs.package.version;
@ -569,7 +565,7 @@ in
message = "boot.zfs.allowHibernation while force importing is enabled will cause data corruption";
}
{
assertion = !(elem "" allPools);
assertion = !(lib.elem "" allPools);
message = ''
Automatic pool detection found an empty pool name, which can't be used.
Hint: for `fileSystems` entries with `fsType = zfs`, the `device` attribute
@ -591,11 +587,10 @@ in
];
};
boot.initrd = mkIf inInitrd {
# spl has been removed in ≥ 2.2.0.
kernelModules = [ "zfs" ] ++ lib.optional (lib.versionOlder "2.2.0" version) "spl";
boot.initrd = lib.mkIf inInitrd {
kernelModules = [ "zfs" ];
extraUtilsCommands =
mkIf (!config.boot.initrd.systemd.enable) ''
lib.mkIf (!config.boot.initrd.systemd.enable) ''
copy_bin_and_libs ${cfgZfs.package}/sbin/zfs
copy_bin_and_libs ${cfgZfs.package}/sbin/zdb
copy_bin_and_libs ${cfgZfs.package}/sbin/zpool
@ -603,12 +598,12 @@ in
copy_bin_and_libs ${cfgZfs.package}/lib/udev/zvol_id
'';
extraUtilsCommandsTest =
mkIf (!config.boot.initrd.systemd.enable) ''
lib.mkIf (!config.boot.initrd.systemd.enable) ''
$out/bin/zfs --help >/dev/null 2>&1
$out/bin/zpool --help >/dev/null 2>&1
'';
postResumeCommands = mkIf (!config.boot.initrd.systemd.enable) (concatStringsSep "\n" ([''
ZFS_FORCE="${optionalString cfgZfs.forceImportRoot "-f"}"
postResumeCommands = lib.mkIf (!config.boot.initrd.systemd.enable) (lib.concatStringsSep "\n" ([''
ZFS_FORCE="${lib.optionalString cfgZfs.forceImportRoot "-f"}"
''] ++ [(importLib {
# See comments at importLib definition.
zpoolCmd = "zpool";
@ -630,21 +625,21 @@ in
poolImported "${pool}" || poolImport "${pool}" # Try one last time, e.g. to import a degraded pool.
fi
${optionalString config.boot.initrd.clevis.enable (concatMapStringsSep "\n" (elem: "clevis decrypt < /etc/clevis/${elem}.jwe | zfs load-key ${elem}") (filter (p: (elemAt (splitString "/" p) 0) == pool) clevisDatasets))}
${lib.optionalString config.boot.initrd.clevis.enable (lib.concatMapStringsSep "\n" (elem: "clevis decrypt < /etc/clevis/${elem}.jwe | zfs load-key ${elem}") (lib.filter (p: (lib.elemAt (lib.splitString "/" p) 0) == pool) clevisDatasets))}
${if isBool cfgZfs.requestEncryptionCredentials
then optionalString cfgZfs.requestEncryptionCredentials ''
${if lib.isBool cfgZfs.requestEncryptionCredentials
then lib.optionalString cfgZfs.requestEncryptionCredentials ''
zfs load-key -a
''
else concatMapStrings (fs: ''
zfs load-key -- ${escapeShellArg fs}
'') (filter (x: datasetToPool x == pool) cfgZfs.requestEncryptionCredentials)}
else lib.concatMapStrings (fs: ''
zfs load-key -- ${lib.escapeShellArg fs}
'') (lib.filter (x: datasetToPool x == pool) cfgZfs.requestEncryptionCredentials)}
'') rootPools)));
# Systemd in stage 1
systemd = mkIf config.boot.initrd.systemd.enable {
systemd = lib.mkIf config.boot.initrd.systemd.enable {
packages = [cfgZfs.package];
services = listToAttrs (map (pool: createImportService {
services = lib.listToAttrs (map (pool: createImportService {
inherit pool;
systemd = config.boot.initrd.systemd.package;
force = cfgZfs.forceImportRoot;
@ -671,18 +666,18 @@ in
systemd.shutdownRamfs.storePaths = ["${cfgZfs.package}/bin/zpool"];
# 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) {
boot.loader.grub = lib.mkIf (inInitrd || inSystem) {
zfsSupport = true;
zfsPackage = cfgZfs.package;
};
services.zfs.zed.settings = {
ZED_EMAIL_PROG = mkIf cfgZED.enableMail (mkDefault (
ZED_EMAIL_PROG = lib.mkIf cfgZED.enableMail (lib.mkDefault (
config.security.wrapperDir + "/" +
config.services.mail.sendmailSetuidWrapper.program
));
# subject in header for sendmail
ZED_EMAIL_OPTS = mkIf cfgZED.enableMail (mkDefault "@ADDRESS@");
ZED_EMAIL_OPTS = lib.mkIf cfgZED.enableMail (lib.mkDefault "@ADDRESS@");
PATH = lib.makeBinPath [
cfgZfs.package
@ -701,7 +696,7 @@ in
ACTION=="add|change", KERNEL=="sd[a-z]*[0-9]*|mmcblk[0-9]*p[0-9]*|nvme[0-9]*n[0-9]*p[0-9]*", ENV{ID_FS_TYPE}=="zfs_member", ATTR{../queue/scheduler}="none"
'';
environment.etc = genAttrs
environment.etc = lib.genAttrs
(map
(file: "zfs/zed.d/${file}")
[
@ -726,7 +721,7 @@ in
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
++ lib.optional cfgSnapshots.enable autosnapPkg; # so the user can run the command to see flags
services.udev.packages = [ cfgZfs.package ]; # to hook zvol naming, etc.
systemd.packages = [ cfgZfs.package ];
@ -741,7 +736,7 @@ in
# This forces a sync of any ZFS pools prior to poweroff, even if they're set
# to sync=disabled.
createSyncService = pool:
nameValuePair "zfs-sync-${pool}" {
lib.nameValuePair "zfs-sync-${pool}" {
description = "Sync ZFS pool \"${pool}\"";
wantedBy = [ "shutdown.target" ];
unitConfig = {
@ -757,12 +752,12 @@ in
};
createZfsService = serv:
nameValuePair serv {
lib.nameValuePair serv {
after = [ "systemd-modules-load.service" ];
wantedBy = [ "zfs.target" ];
};
in listToAttrs (map createImportService' dataPools ++
in lib.listToAttrs (map createImportService' dataPools ++
map createSyncService allPools ++
map createZfsService [ "zfs-mount" "zfs-share" "zfs-zed" ]);
@ -771,7 +766,7 @@ in
systemd.targets.zfs.wantedBy = [ "multi-user.target" ];
})
(mkIf (cfgZfs.enabled && cfgExpandOnBoot != "disabled") {
(lib.mkIf (cfgZfs.enabled && cfgExpandOnBoot != "disabled") {
systemd.services."zpool-expand@" = {
description = "Expand ZFS pools";
after = [ "zfs.target" ];
@ -824,7 +819,7 @@ in
};
})
(mkIf (cfgZfs.enabled && cfgSnapshots.enable) {
(lib.mkIf (cfgZfs.enabled && cfgSnapshots.enable) {
systemd.services = let
descr = name: if name == "frequent" then "15 mins"
else if name == "hourly" then "hour"
@ -862,7 +857,7 @@ in
}) snapshotNames);
})
(mkIf (cfgZfs.enabled && cfgScrub.enable) {
(lib.mkIf (cfgZfs.enabled && cfgScrub.enable) {
systemd.services.zfs-scrub = {
description = "ZFS pools scrubbing";
after = [ "zfs-import.target" ];
@ -872,7 +867,7 @@ in
script = ''
${cfgZfs.package}/bin/zpool scrub -w ${
if cfgScrub.pools != [] then
(concatStringsSep " " cfgScrub.pools)
(lib.concatStringsSep " " cfgScrub.pools)
else
"$(${cfgZfs.package}/bin/zpool list -H -o name)"
}
@ -890,7 +885,7 @@ in
};
})
(mkIf (cfgZfs.enabled && cfgTrim.enable) {
(lib.mkIf (cfgZfs.enabled && cfgTrim.enable) {
systemd.services.zpool-trim = {
description = "ZFS pools trim";
after = [ "zfs-import.target" ];