|
|
|
@ -58,6 +58,45 @@ let |
|
|
|
|
|
|
|
|
|
snapshotNames = [ "frequent" "hourly" "daily" "weekly" "monthly" ]; |
|
|
|
|
|
|
|
|
|
# When importing ZFS pools, there's one difficulty: These scripts may run |
|
|
|
|
# before the backing devices (physical HDDs, etc.) of the pool have been |
|
|
|
|
# scanned and initialized. |
|
|
|
|
# |
|
|
|
|
# An attempted import with all devices missing will just fail, and can be |
|
|
|
|
# retried, but an import where e.g. two out of three disks in a three-way |
|
|
|
|
# mirror are missing, will succeed. This is a problem: When the missing disks |
|
|
|
|
# are later discovered, they won't be automatically set online, rendering the |
|
|
|
|
# pool redundancy-less (and far slower) until such time as the system reboots. |
|
|
|
|
# |
|
|
|
|
# The solution is the below. poolReady checks the status of an un-imported |
|
|
|
|
# pool, to see if *every* device is available -- in which case the pool will be |
|
|
|
|
# in state ONLINE, as opposed to DEGRADED, FAULTED or MISSING. |
|
|
|
|
# |
|
|
|
|
# The import scripts then loop over this, waiting until the pool is ready or a |
|
|
|
|
# sufficient amount of time has passed that we can assume it won't be. In the |
|
|
|
|
# latter case it makes one last attempt at importing, allowing the system to |
|
|
|
|
# (eventually) boot even with a degraded pool. |
|
|
|
|
importLib = {zpoolCmd, awkCmd, cfgZfs}: '' |
|
|
|
|
poolReady() { |
|
|
|
|
pool="$1" |
|
|
|
|
state="$("${zpoolCmd}" import | "${awkCmd}" "/pool: $pool/ { found = 1 }; /state:/ { if (found == 1) { print \$2; exit } }; END { if (found == 0) { print \"MISSING\" } }")" |
|
|
|
|
if [[ "$state" = "ONLINE" ]]; then |
|
|
|
|
return 0 |
|
|
|
|
else |
|
|
|
|
echo "Pool $pool in state $state, waiting" |
|
|
|
|
return 1 |
|
|
|
|
fi |
|
|
|
|
} |
|
|
|
|
poolImported() { |
|
|
|
|
pool="$1" |
|
|
|
|
"${zpoolCmd}" list "$pool" >/dev/null 2>/dev/null |
|
|
|
|
} |
|
|
|
|
poolImport() { |
|
|
|
|
pool="$1" |
|
|
|
|
"${zpoolCmd}" import -d "${cfgZfs.devNodes}" -N $ZFS_FORCE "$pool" |
|
|
|
|
} |
|
|
|
|
''; |
|
|
|
|
|
|
|
|
|
in |
|
|
|
|
|
|
|
|
|
{ |
|
|
|
@ -339,19 +378,26 @@ in |
|
|
|
|
;; |
|
|
|
|
esac |
|
|
|
|
done |
|
|
|
|
''] ++ (map (pool: '' |
|
|
|
|
''] ++ [(importLib { |
|
|
|
|
# See comments at importLib definition. |
|
|
|
|
zpoolCmd = "zpool"; |
|
|
|
|
awkCmd = "awk"; |
|
|
|
|
inherit cfgZfs; |
|
|
|
|
})] ++ (map (pool: '' |
|
|
|
|
echo -n "importing root ZFS pool \"${pool}\"..." |
|
|
|
|
trial=0 |
|
|
|
|
until msg="$(zpool import -d ${cfgZfs.devNodes} -N $ZFS_FORCE '${pool}' 2>&1)"; do |
|
|
|
|
sleep 0.25 |
|
|
|
|
echo -n . |
|
|
|
|
trial=$(($trial + 1)) |
|
|
|
|
if [[ $trial -eq 60 ]]; then |
|
|
|
|
break |
|
|
|
|
# Loop across the import until it succeeds, because the devices needed may not be discovered yet. |
|
|
|
|
if ! poolImported "${pool}"; then |
|
|
|
|
for trial in `seq 1 60`; do |
|
|
|
|
poolReady "${pool}" > /dev/null && msg="$(poolImport "${pool}" 2>&1)" && break |
|
|
|
|
sleep 1 |
|
|
|
|
echo -n . |
|
|
|
|
done |
|
|
|
|
echo |
|
|
|
|
if [[ -n "$msg" ]]; then |
|
|
|
|
echo "$msg"; |
|
|
|
|
fi |
|
|
|
|
done |
|
|
|
|
echo |
|
|
|
|
if [[ -n "$msg" ]]; then echo "$msg"; fi |
|
|
|
|
poolImported "${pool}" || poolImport "${pool}" # Try one last time, e.g. to import a degraded pool. |
|
|
|
|
fi |
|
|
|
|
${lib.optionalString cfgZfs.requestEncryptionCredentials '' |
|
|
|
|
zfs load-key -a |
|
|
|
|
''} |
|
|
|
@ -395,10 +441,26 @@ in |
|
|
|
|
Type = "oneshot"; |
|
|
|
|
RemainAfterExit = true; |
|
|
|
|
}; |
|
|
|
|
script = '' |
|
|
|
|
zpool_cmd="${packages.zfsUser}/sbin/zpool" |
|
|
|
|
("$zpool_cmd" list "${pool}" >/dev/null) || "$zpool_cmd" import -d ${cfgZfs.devNodes} -N ${optionalString cfgZfs.forceImportAll "-f"} "${pool}" |
|
|
|
|
${optionalString cfgZfs.requestEncryptionCredentials "\"${packages.zfsUser}/sbin/zfs\" load-key -r \"${pool}\""} |
|
|
|
|
script = (importLib { |
|
|
|
|
# See comments at importLib definition. |
|
|
|
|
zpoolCmd="${packages.zfsUser}/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 cfgZfs.requestEncryptionCredentials "\"${packages.zfsUser}/sbin/zfs\" load-key -r \"${pool}\""} |
|
|
|
|
echo "Successfully imported ${pool}" |
|
|
|
|
else |
|
|
|
|
exit 1 |
|
|
|
|
fi |
|
|
|
|
''; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|