zfs: Add topology attribute to zpool

This commit is contained in:
dylan madisetti 2024-08-01 15:41:09 -04:00
parent 7e1b215a0a
commit b2a106f8ed
No known key found for this signature in database
GPG key ID: 9080F46A6E933A9D
2 changed files with 110 additions and 9 deletions

View file

@ -195,6 +195,7 @@ let
|| isAttrsOfSubmodule o
# TODO don't hardcode diskoLib.subType options.
|| n == "content" || n == "partitions" || n == "datasets" || n == "swap"
|| n == "topology"
);
in
lib.toShellVars

View file

@ -1,4 +1,20 @@
{ config, options, lib, diskoLib, rootMountPoint, ... }:
let
modeOptions = [
""
"mirror"
"raidz"
"raidz2"
"raidz3"
];
format_output = (mode: members: ''
entries+=("${mode}=${
lib.concatMapStringsSep " "
(d: "/dev/disk/by-partlabel/disk-${d}-zfs") members
}")
'');
format_vdev = (vdev: format_output vdev.mode vdev.members);
in
{
options = {
name = lib.mkOption {
@ -13,13 +29,7 @@
description = "Type";
};
mode = lib.mkOption {
type = lib.types.enum [
""
"mirror"
"raidz"
"raidz2"
"raidz3"
];
type = lib.types.enum (modeOptions ++ [ "prescribed" ]);
default = "";
description = "Mode of the ZFS pool";
};
@ -50,6 +60,55 @@
});
description = "List of datasets to define";
};
topology = lib.mkOption {
type =
let
vdev = lib.types.submodule ({ name, ... }: {
options = {
mode = lib.mkOption {
type = lib.types.enum modeOptions;
default = "";
description = "mode of the zfs vdev";
};
members = lib.mkOption {
type = lib.types.listOf lib.types.str;
description = "Members of the vdev";
};
};
});
parent = config;
in
lib.types.nullOr
(lib.types.submodule
({ config, name, ... }: {
options = {
type = lib.mkOption {
type = lib.types.enum [ "zfs_topology" ];
default = "zfs_topology";
internal = true;
description = "Type";
};
# zfs disk types
vdev = lib.mkOption {
type = lib.types.listOf vdev;
default = [ ];
description = "A list of storage vdevs";
};
special = lib.mkOption {
type = lib.types.nullOr vdev;
default = null;
description = "A list of devices for the special vdev";
};
cache = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
description = "The cache device";
};
};
}));
default = null;
description = "Topology of the ZFS pool";
};
_meta = lib.mkOption {
internal = true;
readOnly = true;
@ -84,11 +143,52 @@
fi
done
if [ $continue -eq 1 ]; then
topology=""
# For shell check
mode="${config.mode}"
if [ $mode != "prescribed" ]; then
${if config.topology == null then
''topology="${config.mode} \"''${zfs_devices}\""''
else
''
echo "topology cannot be set when mode != 'prescribed', skipping creating zpool ${config.name}" >&2
continue=0
''
}
else
entries=()
${lib.concatMapStrings format_vdev config.topology.vdev}
${lib.optionalString (config.topology.special != null)
(format_output "special ${config.topology.special.mode}" config.topology.special.members)}
${lib.optionalString (config.topology.cache != null)
(format_output "cache" [config.topology.cache])}
all_devices=()
for line in "''${entries[@]}"; do
# lineformat is mode=device1 device2 device3
mode=''${line%%=*}
devs=''${line#*=}
IFS=' ' read -r -a devices <<< "$devs"
all_devices+=("''${devices[@]}")
# shellcheck disable=SC2089
topology+=" ''${mode} ''${devices[*]}"
done
# all_devices sorted should equal zfs_devices sorted
all_devices_list=$(echo "''${all_devices[*]}" | tr ' ' '\n' | sort)
zfs_devices_list=$(echo "''${zfs_devices[*]}" | tr ' ' '\n' | sort)
if [[ "$all_devices_list" != "$zfs_devices_list" ]]; then
echo "not all disks accounted for, skipping creating zpool ${config.name}" >&2
diff <(echo "$all_devices_list" ) <(echo "$zfs_devices_list") >&2
continue=0
fi
fi
fi
if [ $continue -eq 1 ]; then
# shellcheck disable=SC2090
zpool create -f ${config.name} \
-R ${rootMountPoint} ${config.mode} \
-R ${rootMountPoint} \
${lib.concatStringsSep " " (lib.mapAttrsToList (n: v: "-o ${n}=${v}") config.options)} \
${lib.concatStringsSep " " (lib.mapAttrsToList (n: v: "-O ${n}=${v}") config.rootFsOptions)} \
"''${zfs_devices[@]}"
''${topology:+ $topology}
if [[ $(zfs get -H mounted ${config.name} | cut -f3) == "yes" ]]; then
zfs unmount ${config.name}
fi