nixos/stage-1-systemd: ZFS support
This commit is contained in:
parent
b521c51c83
commit
44a6882f55
1 changed files with 105 additions and 82 deletions
|
@ -58,6 +58,13 @@ let
|
|||
# latter case it makes one last attempt at importing, allowing the system to
|
||||
# (eventually) boot even with a degraded pool.
|
||||
importLib = {zpoolCmd, awkCmd, cfgZfs}: ''
|
||||
for o in $(cat /proc/cmdline); do
|
||||
case $o in
|
||||
zfs_force|zfs_force=1)
|
||||
ZFS_FORCE="-f"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
poolReady() {
|
||||
pool="$1"
|
||||
state="$("${zpoolCmd}" import 2>/dev/null | "${awkCmd}" "/pool: $pool/ { found = 1 }; /state:/ { if (found == 1) { print \$2; exit } }; END { if (found == 0) { print \"MISSING\" } }")"
|
||||
|
@ -78,6 +85,83 @@ let
|
|||
}
|
||||
'';
|
||||
|
||||
getPoolFilesystems = pool:
|
||||
filter (x: x.fsType == "zfs" && (fsToPool x) == pool) config.system.build.fileSystems;
|
||||
|
||||
getPoolMounts = prefix: pool:
|
||||
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));
|
||||
in
|
||||
map (x: "${mountPoint x}.mount") (getPoolFilesystems pool);
|
||||
|
||||
createImportService = { pool, systemd, force, prefix ? "" }:
|
||||
nameValuePair "zfs-import-${pool}" {
|
||||
description = "Import ZFS pool \"${pool}\"";
|
||||
# we need systemd-udev-settle until https://github.com/zfsonlinux/zfs/pull/4943 is merged
|
||||
requires = [ "systemd-udev-settle.service" ];
|
||||
after = [
|
||||
"systemd-udev-settle.service"
|
||||
"systemd-modules-load.service"
|
||||
"systemd-ask-password-console.service"
|
||||
];
|
||||
wantedBy = (getPoolMounts prefix pool) ++ [ "local-fs.target" ];
|
||||
before = (getPoolMounts prefix pool) ++ [ "local-fs.target" ];
|
||||
unitConfig = {
|
||||
DefaultDependencies = "no";
|
||||
};
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
RemainAfterExit = true;
|
||||
};
|
||||
environment.ZFS_FORCE = optionalString force "-f";
|
||||
script = (importLib {
|
||||
# See comments at importLib definition.
|
||||
zpoolCmd = "${cfgZfs.package}/sbin/zpool";
|
||||
awkCmd = "${pkgs.gawk}/bin/awk";
|
||||
inherit cfgZfs;
|
||||
}) + ''
|
||||
poolImported "${pool}" && exit
|
||||
echo -n "importing ZFS pool \"${pool}\"..."
|
||||
# Loop across the import until it succeeds, because the devices needed may not be discovered yet.
|
||||
for trial in `seq 1 60`; do
|
||||
poolReady "${pool}" && poolImport "${pool}" && break
|
||||
sleep 1
|
||||
done
|
||||
poolImported "${pool}" || poolImport "${pool}" # Try one last time, e.g. to import a degraded pool.
|
||||
if poolImported "${pool}"; then
|
||||
${optionalString (if isBool cfgZfs.requestEncryptionCredentials
|
||||
then cfgZfs.requestEncryptionCredentials
|
||||
else cfgZfs.requestEncryptionCredentials != []) ''
|
||||
${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
|
||||
fi
|
||||
''}
|
||||
case "$kl" in
|
||||
none )
|
||||
;;
|
||||
prompt )
|
||||
${systemd}/bin/systemd-ask-password "Enter key for $ds:" | ${cfgZfs.package}/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
|
||||
''}
|
||||
echo "Successfully imported ${pool}"
|
||||
else
|
||||
exit 1
|
||||
fi
|
||||
'';
|
||||
};
|
||||
|
||||
zedConf = generators.toKeyValue {
|
||||
mkKeyValue = generators.mkKeyValueDefault {
|
||||
mkValueString = v:
|
||||
|
@ -428,14 +512,6 @@ in
|
|||
'';
|
||||
postDeviceCommands = concatStringsSep "\n" ([''
|
||||
ZFS_FORCE="${optionalString cfgZfs.forceImportRoot "-f"}"
|
||||
|
||||
for o in $(cat /proc/cmdline); do
|
||||
case $o in
|
||||
zfs_force|zfs_force=1)
|
||||
ZFS_FORCE="-f"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
''] ++ [(importLib {
|
||||
# See comments at importLib definition.
|
||||
zpoolCmd = "zpool";
|
||||
|
@ -464,6 +540,21 @@ in
|
|||
zfs load-key ${fs}
|
||||
'') cfgZfs.requestEncryptionCredentials}
|
||||
'') rootPools));
|
||||
|
||||
# Systemd in stage 1
|
||||
systemd = {
|
||||
packages = [cfgZfs.package];
|
||||
services = listToAttrs (map (pool: createImportService {
|
||||
inherit pool;
|
||||
systemd = config.boot.initrd.systemd.package;
|
||||
force = cfgZfs.forceImportRoot;
|
||||
prefix = "/sysroot";
|
||||
}) rootPools);
|
||||
extraBin = {
|
||||
# zpool and zfs are already in thanks to fsPackages
|
||||
awk = "${pkgs.gawk}/bin/awk";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
systemd.shutdownRamfs.contents."/etc/systemd/system-shutdown/zpool".source = pkgs.writeShellScript "zpool-sync-shutdown" ''
|
||||
|
@ -521,79 +612,11 @@ in
|
|||
systemd.packages = [ cfgZfs.package ];
|
||||
|
||||
systemd.services = let
|
||||
getPoolFilesystems = pool:
|
||||
filter (x: x.fsType == "zfs" && (fsToPool x) == pool) config.system.build.fileSystems;
|
||||
|
||||
getPoolMounts = pool:
|
||||
let
|
||||
mountPoint = fs: escapeSystemdPath fs.mountPoint;
|
||||
in
|
||||
map (x: "${mountPoint x}.mount") (getPoolFilesystems pool);
|
||||
|
||||
createImportService = pool:
|
||||
nameValuePair "zfs-import-${pool}" {
|
||||
description = "Import ZFS pool \"${pool}\"";
|
||||
# we need systemd-udev-settle until https://github.com/zfsonlinux/zfs/pull/4943 is merged
|
||||
requires = [ "systemd-udev-settle.service" ];
|
||||
after = [
|
||||
"systemd-udev-settle.service"
|
||||
"systemd-modules-load.service"
|
||||
"systemd-ask-password-console.service"
|
||||
];
|
||||
wantedBy = (getPoolMounts pool) ++ [ "local-fs.target" ];
|
||||
before = (getPoolMounts pool) ++ [ "local-fs.target" ];
|
||||
unitConfig = {
|
||||
DefaultDependencies = "no";
|
||||
};
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
RemainAfterExit = true;
|
||||
};
|
||||
environment.ZFS_FORCE = optionalString cfgZfs.forceImportAll "-f";
|
||||
script = (importLib {
|
||||
# See comments at importLib definition.
|
||||
zpoolCmd = "${cfgZfs.package}/sbin/zpool";
|
||||
awkCmd = "${pkgs.gawk}/bin/awk";
|
||||
inherit cfgZfs;
|
||||
}) + ''
|
||||
poolImported "${pool}" && exit
|
||||
echo -n "importing ZFS pool \"${pool}\"..."
|
||||
# Loop across the import until it succeeds, because the devices needed may not be discovered yet.
|
||||
for trial in `seq 1 60`; do
|
||||
poolReady "${pool}" && poolImport "${pool}" && break
|
||||
sleep 1
|
||||
done
|
||||
poolImported "${pool}" || poolImport "${pool}" # Try one last time, e.g. to import a degraded pool.
|
||||
if poolImported "${pool}"; then
|
||||
${optionalString (if isBool cfgZfs.requestEncryptionCredentials
|
||||
then cfgZfs.requestEncryptionCredentials
|
||||
else cfgZfs.requestEncryptionCredentials != []) ''
|
||||
${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
|
||||
fi
|
||||
''}
|
||||
case "$kl" in
|
||||
none )
|
||||
;;
|
||||
prompt )
|
||||
${config.systemd.package}/bin/systemd-ask-password "Enter key for $ds:" | ${cfgZfs.package}/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
|
||||
''}
|
||||
echo "Successfully imported ${pool}"
|
||||
else
|
||||
exit 1
|
||||
fi
|
||||
'';
|
||||
};
|
||||
createImportService' = pool: createImportService {
|
||||
inherit pool;
|
||||
systemd = config.systemd.package;
|
||||
force = cfgZfs.forceImportAll;
|
||||
};
|
||||
|
||||
# This forces a sync of any ZFS pools prior to poweroff, even if they're set
|
||||
# to sync=disabled.
|
||||
|
@ -619,7 +642,7 @@ in
|
|||
wantedBy = [ "zfs.target" ];
|
||||
};
|
||||
|
||||
in listToAttrs (map createImportService dataPools ++
|
||||
in listToAttrs (map createImportService' dataPools ++
|
||||
map createSyncService allPools ++
|
||||
map createZfsService [ "zfs-mount" "zfs-share" "zfs-zed" ]);
|
||||
|
||||
|
|
Loading…
Reference in a new issue