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
|
# latter case it makes one last attempt at importing, allowing the system to
|
||||||
# (eventually) boot even with a degraded pool.
|
# (eventually) boot even with a degraded pool.
|
||||||
importLib = {zpoolCmd, awkCmd, cfgZfs}: ''
|
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() {
|
poolReady() {
|
||||||
pool="$1"
|
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\" } }")"
|
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 {
|
zedConf = generators.toKeyValue {
|
||||||
mkKeyValue = generators.mkKeyValueDefault {
|
mkKeyValue = generators.mkKeyValueDefault {
|
||||||
mkValueString = v:
|
mkValueString = v:
|
||||||
|
@ -428,14 +512,6 @@ in
|
||||||
'';
|
'';
|
||||||
postDeviceCommands = concatStringsSep "\n" ([''
|
postDeviceCommands = concatStringsSep "\n" ([''
|
||||||
ZFS_FORCE="${optionalString cfgZfs.forceImportRoot "-f"}"
|
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 {
|
''] ++ [(importLib {
|
||||||
# See comments at importLib definition.
|
# See comments at importLib definition.
|
||||||
zpoolCmd = "zpool";
|
zpoolCmd = "zpool";
|
||||||
|
@ -464,6 +540,21 @@ in
|
||||||
zfs load-key ${fs}
|
zfs load-key ${fs}
|
||||||
'') cfgZfs.requestEncryptionCredentials}
|
'') cfgZfs.requestEncryptionCredentials}
|
||||||
'') rootPools));
|
'') 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" ''
|
systemd.shutdownRamfs.contents."/etc/systemd/system-shutdown/zpool".source = pkgs.writeShellScript "zpool-sync-shutdown" ''
|
||||||
|
@ -521,78 +612,10 @@ in
|
||||||
systemd.packages = [ cfgZfs.package ];
|
systemd.packages = [ cfgZfs.package ];
|
||||||
|
|
||||||
systemd.services = let
|
systemd.services = let
|
||||||
getPoolFilesystems = pool:
|
createImportService' = pool: createImportService {
|
||||||
filter (x: x.fsType == "zfs" && (fsToPool x) == pool) config.system.build.fileSystems;
|
inherit pool;
|
||||||
|
systemd = config.systemd.package;
|
||||||
getPoolMounts = pool:
|
force = cfgZfs.forceImportAll;
|
||||||
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
|
|
||||||
'';
|
|
||||||
};
|
};
|
||||||
|
|
||||||
# This forces a sync of any ZFS pools prior to poweroff, even if they're set
|
# This forces a sync of any ZFS pools prior to poweroff, even if they're set
|
||||||
|
@ -619,7 +642,7 @@ in
|
||||||
wantedBy = [ "zfs.target" ];
|
wantedBy = [ "zfs.target" ];
|
||||||
};
|
};
|
||||||
|
|
||||||
in listToAttrs (map createImportService dataPools ++
|
in listToAttrs (map createImportService' dataPools ++
|
||||||
map createSyncService allPools ++
|
map createSyncService allPools ++
|
||||||
map createZfsService [ "zfs-mount" "zfs-share" "zfs-zed" ]);
|
map createZfsService [ "zfs-mount" "zfs-share" "zfs-zed" ]);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue