diff options
author | Jörg Thalheim <Mic92@users.noreply.github.com> | 2022-08-26 14:59:28 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-08-26 14:59:28 +0100 |
commit | adf901d58155ca268d15351fff164d3ef38a0890 (patch) | |
tree | c6057a3d362e06742073b35d4c11db7ee9a0a820 /default.nix | |
parent | 6b0b20da18cdffd09f04faee7128c557bcb9f054 (diff) | |
parent | 9bb4aec9640cbc30e241c267158e506278862b5e (diff) |
Merge pull request #27 from nix-community/zfs
zfs, lvm raid, btrfs subvolumes support & some fixups
Diffstat (limited to 'default.nix')
-rw-r--r-- | default.nix | 266 |
1 files changed, 192 insertions, 74 deletions
diff --git a/default.nix b/default.nix index d20b67e..51b70f5 100644 --- a/default.nix +++ b/default.nix @@ -4,12 +4,13 @@ with builtins; let - helper.find-device = device: let - environment = helper.device-id device; - in + helper.find-device = device: + let + environment = helper.device-id device; + in # DEVICE points already to /dev/disk, so we don't handle it via /dev/disk/by-path if hasPrefix "/dev/disk" device then - "${environment}='${device}'" + "${environment}='${device}'" else '' ${environment}=$(for x in $(find /dev/disk/{by-path,by-id}/); do dev=$x @@ -38,42 +39,65 @@ let }; }; + config.zfs_filesystem = q: x: { + fileSystems.${x.mountpoint} = { + device = q.device; + fsType = "zfs"; + }; + }; + config.devices = q: x: - foldl' recursiveUpdate {} (mapAttrsToList (name: config-f { device = "/dev/${name}"; }) x.content); + foldl' recursiveUpdate { } (mapAttrsToList (name: config-f { device = "/dev/${name}"; }) x.content); config.luks = q: x: { boot.initrd.luks.devices.${x.name}.device = q.device; } // config-f { device = "/dev/mapper/${x.name}"; } x.content; - config.lv = q: x: - config-f { device = "/dev/mapper/${q.vgname}-${q.name}"; } x.content; + config.lvm_lv = q: x: + config-f { device = "/dev/${q.vgname}/${q.name}"; } x.content; - config.lvm = q: x: - foldl' recursiveUpdate {} (mapAttrsToList (name: config-f { inherit name; vgname = x.name; }) x.lvs); + config.lvm_vg = q: x: + foldl' recursiveUpdate { } (mapAttrsToList (name: config-f { inherit name; vgname = x.name; }) x.lvs); - config.noop = q: x: {}; + config.noop = q: x: { }; config.partition = q: x: config-f { device = q.device + toString q.index; } x.content; config.table = q: x: - foldl' recursiveUpdate {} (imap (index: config-f (q // { inherit index; })) x.partitions); + foldl' recursiveUpdate { } (imap (index: config-f (q // { inherit index; })) x.partitions); create-f = q: x: create.${x.type} q x; - create.filesystem = q: x: '' - mkfs.${x.format} ${q.device} + create.btrfs = q: x: '' + mkfs.btrfs ${q.device} + ${lib.optionalString (!isNull x.subvolumes or null) '' + MNTPOINT=$(mktemp -d) + ( + mount ${q.device} "$MNTPOINT" + trap 'umount $MNTPOINT; rm -rf $MNTPOINT' EXIT + ${concatMapStringsSep "\n" (subvolume: "btrfs subvolume create \"$MNTPOINT\"/${subvolume}") x.subvolumes} + ) + ''} ''; - create.devices = q: x: let - raid-devices = lib.filterAttrs (_: dev: dev.type == "mdadm") x.content; - other-devices = lib.filterAttrs (_: dev: dev.type != "mdadm") x.content; - in '' - ${concatStrings (mapAttrsToList (name: create-f { device = "/dev/${name}"; }) other-devices)} - ${concatStrings (mapAttrsToList (name: create-f { device = "/dev/${name}"; name = name; }) raid-devices)} + create.filesystem = q: x: '' + mkfs.${x.format} \ + ${lib.optionalString (!isNull x.extraArgs or null) x.extraArgs} \ + ${q.device} ''; + create.devices = q: x: + let + raid-devices = lib.filterAttrs (_: dev: dev.type == "mdadm" || dev.type == "zpool" || dev.type == "lvm_vg") x.content; + other-devices = lib.filterAttrs (_: dev: dev.type != "mdadm" && dev.type != "zpool" && dev.type != "lvm_vg") x.content; + in + '' + ${concatStrings (mapAttrsToList (name: create-f { device = "/dev/${name}"; }) other-devices)} + ${concatStrings (mapAttrsToList (name: create-f { device = "/dev/${name}"; name = name; }) raid-devices)} + ''; + create.mdraid = q: x: '' RAIDDEVICES_N_${x.name}=$((''${RAIDDEVICES_N_${x.name}:-0}+1)) RAIDDEVICES_${x.name}="''${RAIDDEVICES_${x.name}:-}${q.device} " @@ -91,33 +115,44 @@ let ${create-f { device = "/dev/mapper/${x.name}"; } x.content} ''; - create.lv = q: x: '' - lvcreate ${if hasInfix "%" x.size then "-l" else "-L"} ${x.size} -n ${q.name} ${q.vgname} - ${create-f { device = "/dev/mapper/${q.vgname}-${q.name}"; } x.content} + create.lvm_pv = q: x: '' + pvcreate ${q.device} + LVMDEVICES_${x.vg}="''${LVMDEVICES_${x.vg}:-}${q.device} " ''; - create.lvm = q: x: '' - pvcreate ${q.device} - vgcreate ${x.name} ${q.device} - ${concatStrings (mapAttrsToList (name: create-f { inherit name; vgname = x.name; }) x.lvs)} + create.lvm_lv = q: x: '' + lvcreate \ + ${if hasInfix "%" x.size then "-l" else "-L"} ${x.size} \ + -n ${q.name} \ + ${lib.optionalString (!isNull x.lvm_type or null) "--type=${x.lvm_type}"} \ + ${lib.optionalString (!isNull x.extraArgs or null) x.extraArgs} \ + ${q.vgname} + ${create-f { device = "/dev/${q.vgname}/${q.name}"; } x.content} + ''; + + create.lvm_vg = q: x: '' + vgcreate ${q.name} $LVMDEVICES_${q.name} + ${concatStrings (mapAttrsToList (name: create-f { inherit name; vgname = q.name; }) x.lvs)} ''; create.noop = q: x: ""; - create.partition = q: x: let - env = helper.device-id q.device; - in '' - parted -s "''${${env}}" mkpart ${x.part-type} ${x.fs-type or ""} ${x.start} ${x.end} - # ensure /dev/disk/by-path/..-partN exists before continuing - udevadm trigger --subsystem-match=block; udevadm settle - ${optionalString (x.bootable or false) '' - parted -s "''${${env}}" set ${toString q.index} boot on - ''} - ${concatMapStringsSep "" (flag: '' - parted -s "''${${env}}" set ${toString q.index} ${flag} on - '') (x.flags or [])} - ${create-f { device = "\"\${${env}}\"-part" + toString q.index; } x.content} - ''; + create.partition = q: x: + let + env = helper.device-id q.device; + in + '' + parted -s "''${${env}}" mkpart ${x.part-type} ${x.fs-type or ""} ${x.start} ${x.end} + # ensure /dev/disk/by-path/..-partN exists before continuing + udevadm trigger --subsystem-match=block; udevadm settle + ${optionalString (x.bootable or false) '' + parted -s "''${${env}}" set ${toString q.index} boot on + ''} + ${concatMapStringsSep "" (flag: '' + parted -s "''${${env}}" set ${toString q.index} ${flag} on + '') (x.flags or [])} + ${create-f { device = "\"\${${env}}\"-part" + toString q.index; } x.content} + ''; create.table = q: x: '' ${helper.find-device q.device} @@ -125,51 +160,98 @@ let ${concatStrings (imap (index: create-f (q // { inherit index; })) x.partitions)} ''; + create.zfs = q: x: '' + ZFSDEVICES_${x.pool}="''${ZFSDEVICES_${x.pool}:-}${q.device} " + ''; + + create.zfs_filesystem = q: x: '' + zfs create ${q.pool}/${x.name} \ + ${lib.optionalString (isAttrs x.options or null) (concatStringsSep " " (mapAttrsToList (n: v: "-o ${n}=${v}") x.options))} + ''; + + create.zfs_volume = q: x: '' + zfs create ${q.pool}/${x.name} \ + -V ${x.size} \ + ${lib.optionalString (isAttrs x.options or null) (concatStringsSep " " (mapAttrsToList (n: v: "-o ${n}=${v}") x.options))} + udevadm trigger --subsystem-match=block; udevadm settle + ${create-f { device = "/dev/zvol/${q.pool}/${x.name}"; } x.content} + ''; + + create.zpool = q: x: '' + zpool create ${q.name} \ + ${lib.optionalString (!isNull (x.mode or null) && x.mode != "stripe") x.mode} \ + ${lib.optionalString (isAttrs x.options or null) (concatStringsSep " " (mapAttrsToList (n: v: "-o ${n}=${v}") x.options))} \ + ${lib.optionalString (isAttrs x.rootFsOptions or null) (concatStringsSep " " (mapAttrsToList (n: v: "-O ${n}=${v}") x.rootFsOptions))} \ + ''${ZFSDEVICES_${q.name}} + ${concatMapStrings (create-f (q // { pool = q.name; })) x.datasets} + ''; + mount-f = q: x: mount.${x.type} q x; mount.filesystem = q: x: { - fs.${x.mountpoint} = '' - if ! findmnt ${q.device} "/mnt${x.mountpoint}" > /dev/null 2>&1; then - mount ${q.device} "/mnt${x.mountpoint}" -o X-mount.mkdir - fi - ''; - }; + fs.${x.mountpoint} = '' + if ! findmnt ${q.device} "/mnt${x.mountpoint}" > /dev/null 2>&1; then + mount ${q.device} "/mnt${x.mountpoint}" \ + -o X-mount.mkdir \ + ${lib.optionalString (isList x.mountOptions or null) (concatStringsSep " " x.mountOptions)} + fi + ''; + }; - mount.devices = q: x: let - z = foldl' recursiveUpdate {} (mapAttrsToList (name: mount-f { device = "/dev/${name}"; }) x.content); - # attrValues returns values sorted by name. This is important, because it - # ensures that "/" is processed before "/foo" etc. - in '' - ${optionalString (hasAttr "table" z) (concatStringsSep "\n" (attrValues z.table))} - ${optionalString (hasAttr "luks" z) (concatStringsSep "\n" (attrValues z.luks))} - ${optionalString (hasAttr "lvm" z) (concatStringsSep "\n" (attrValues z.lvm))} - ${optionalString (hasAttr "fs" z) (concatStringsSep "\n" (attrValues z.fs))} - ''; + mount.zfs_filesystem = q: x: + optionalAttrs ((x.options.mountpoint or "") != "none") + (mount.filesystem (q // { device = q.dataset; }) (x // { mountOptions = [ + (lib.optionalString ((x.options.mountpoint or "") != "legacy") "-o zfsutil") + "-t zfs" + ]; })); + + mount.btrfs = mount.filesystem; + + mount.devices = q: x: + let + z = foldl' recursiveUpdate { } (mapAttrsToList (name: mount-f { device = "/dev/${name}"; inherit name; }) x.content); + # attrValues returns values sorted by name. This is important, because it + # ensures that "/" is processed before "/foo" etc. + in + '' + ${optionalString (hasAttr "table" z) (concatStringsSep "\n" (attrValues z.table))} + ${optionalString (hasAttr "luks" z) (concatStringsSep "\n" (attrValues z.luks))} + ${optionalString (hasAttr "lvm" z) (concatStringsSep "\n" (attrValues z.lvm))} + ${optionalString (hasAttr "zpool" z) (concatStringsSep "\n" (attrValues z.zpool))} + ${optionalString (hasAttr "zfs" z) (concatStringsSep "\n" (attrValues z.zfs))} + ${optionalString (hasAttr "fs" z) (concatStringsSep "\n" (attrValues z.fs))} + ''; mount.luks = q: x: ( recursiveUpdate - (mount-f { device = "/dev/mapper/${x.name}"; } x.content) - {luks.${q.device} = '' - cryptsetup luksOpen ${q.device} ${x.name} ${if builtins.hasAttr "keyfile" x then "--key-file " + x.keyfile else ""} - '';} + (mount-f { device = "/dev/mapper/${x.name}"; } x.content) + { + luks.${q.device} = '' + cryptsetup status ${x.name} >/dev/null 2>/dev/null || cryptsetup luksOpen ${q.device} ${x.name} ${if builtins.hasAttr "keyfile" x then "--key-file " + x.keyfile else ""} + ''; + } ); - mount.lv = q: x: - mount-f { device = "/dev/mapper/${q.vgname}-${q.name}"; } x.content; + mount.lvm_lv = q: x: + mount-f { device = "/dev/${q.vgname}/${q.name}"; } x.content; - mount.lvm = q: x: ( + mount.lvm_vg = q: x: ( recursiveUpdate - (foldl' recursiveUpdate {} (mapAttrsToList (name: mount-f { inherit name; vgname = x.name; }) x.lvs)) - {lvm.${q.device} = '' - vgchange -a y - '';} + (foldl' recursiveUpdate { } (mapAttrsToList (name: mount-f { inherit name; vgname = q.name; }) x.lvs)) + { + lvm.${q.device} = '' + vgchange -a y + ''; + } ); - mount.noop = q: x: {}; + mount.lvm_pv = mount.noop; + + mount.noop = q: x: { }; - # TODO maybe we need to do something here? - mount.mdadm = mount.noop; + mount.mdadm = q: x: + mount-f { device = "/dev/md/${q.name}"; } x.content; mount.mdraid = mount.noop; mount.partition = q: x: @@ -177,11 +259,47 @@ let mount.table = q: x: ( recursiveUpdate - (foldl' recursiveUpdate {} (imap (index: mount-f (q // { inherit index; device = helper.device-id q.device; })) x.partitions)) - {table.${q.device} = helper.find-device q.device;} + (foldl' recursiveUpdate { } (imap (index: mount-f (q // { inherit index; device = helper.device-id q.device; })) x.partitions)) + { table.${q.device} = helper.find-device q.device; } ); -in { - config = config-f {}; + + mount.zfs = mount.noop; + + mount.zpool = q: x: + let + datasets = [{ + inherit (q) name; + type = "zfs_filesystem"; + dataset = q.name; + mountpoint = x.mountpoint or "/${q.name}"; + options = q.rootFsOptions or { }; + }] ++ x.datasets; + in + recursiveUpdate + (foldl' recursiveUpdate { } + ( + (map + (x: mount-f + ({ + dataset = x.dataset or "${q.name}/${x.name}"; + mountpoint = x.mountpoint or "/${q.name}/${x.name}"; + } // q) + x) + datasets) + ) + ) + { + zpool.${q.device} = '' + zpool list '${q.name}' >/dev/null 2>/dev/null || zpool import '${q.name}' + ''; + }; + + mount.zfs_volume = q: x: + mount-f { device = "/dev/zvol/${q.dataset}"; } x.content; + +in +{ + config = config-f { }; create = cfg: '' set -efux ${create-f {} cfg} |