diff options
-rw-r--r-- | default.nix | 65 | ||||
-rw-r--r-- | example/zfs.nix | 41 | ||||
-rw-r--r-- | tests/zfs.nix | 40 |
3 files changed, 143 insertions, 3 deletions
diff --git a/default.nix b/default.nix index 39e455a..4a9a033 100644 --- a/default.nix +++ b/default.nix @@ -38,6 +38,13 @@ 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); @@ -67,8 +74,8 @@ let ''; 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; + raid-devices = lib.filterAttrs (_: dev: dev.type == "mdadm" || dev.type == "zpool") x.content; + other-devices = lib.filterAttrs (_: dev: dev.type != "mdadm" && dev.type != "zpool") 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)} @@ -125,6 +132,32 @@ 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 || 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; @@ -144,6 +177,8 @@ let ${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))} ''; @@ -170,7 +205,6 @@ let mount.mdadm = q: x: mount-f { device = "/dev/md/${q.name}"; } x.content; - # TODO maybe we need to do something here? mount.mdraid = mount.noop; mount.partition = q: x: @@ -181,6 +215,31 @@ let (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;} ); + + mount.zfs = mount.noop; + + mount.zpool = q: x: ( + recursiveUpdate + (foldl' recursiveUpdate {} (map (mount-f (q // { pool = q.name; })) x.datasets)) + {zpool.${q.device} = '' + zpool list '${q.name}' >/dev/null 2>/dev/null || zpool import '${q.name}' + '';} + ); + + mount.zfs_filesystem = q: x: { + zfs.${x.mountpoint} = '' + if ! findmnt '${q.pool}/${x.name}' /mnt${x.mountpoint} > /dev/null 2>&1; then + mount \ + ${lib.optionalString ((x.options.mountpoint or "") != "legacy") "-o zfsutil"} \ + -t zfs ${q.pool}/${x.name} /mnt${x.mountpoint} \ + -o X-mount.mkdir + fi + ''; + }; + + mount.zfs_volume = q: x: + mount-f { device = "/dev/zvol/${q.pool}/${x.name}"; } x.content; + in { config = config-f {}; create = cfg: '' diff --git a/example/zfs.nix b/example/zfs.nix new file mode 100644 index 0000000..e344289 --- /dev/null +++ b/example/zfs.nix @@ -0,0 +1,41 @@ +{ + type = "devices"; + content = { + vdb = { + type = "zfs"; + pool = "zroot"; + }; + vdc = { + type = "zfs"; + pool = "zroot"; + }; + zroot = { + type = "zpool"; + mode = "mirror"; + datasets = [ + { + type = "zfs_filesystem"; + name = "zfs_fs"; + mountpoint = "/zfs_fs"; + } + { + type = "zfs_filesystem"; + name = "zfs_legacy_fs"; + options.mountpoint = "legacy"; + mountpoint = "/zfs_legacy_fs"; + } + { + type = "zfs_volume"; + name = "zfs_testvolume"; + size = "10M"; + content = { + type = "filesystem"; + format = "ext4"; + mountpoint = "/ext4onzfs"; + }; + } + ]; + }; + }; +} + diff --git a/tests/zfs.nix b/tests/zfs.nix new file mode 100644 index 0000000..bc807bc --- /dev/null +++ b/tests/zfs.nix @@ -0,0 +1,40 @@ +{ makeTest ? import <nixpkgs/nixos/tests/make-test-python.nix> +, pkgs ? (import <nixpkgs> {}) +}: +let + makeTest' = args: + makeTest args { + inherit pkgs; + inherit (pkgs) system; + }; + disko-config = import ../example/zfs.nix; + tsp-create = pkgs.writeScript "create" ((pkgs.callPackage ../. {}).create disko-config); + tsp-mount = pkgs.writeScript "mount" ((pkgs.callPackage ../. {}).mount disko-config); +in makeTest' { + name = "disko"; + + nodes.machine = + { config, pkgs, modulesPath, ... }: + + { + imports = [ + (modulesPath + "/profiles/installation-device.nix") + (modulesPath + "/profiles/base.nix") + ]; + + # speed-up eval + documentation.enable = false; + + virtualisation.emptyDiskImages = [ 512 512 ]; + }; + + testScript = '' + machine.succeed("echo 'secret' > /tmp/secret.key"); + machine.succeed("${tsp-create}"); + machine.succeed("${tsp-mount}"); + machine.succeed("${tsp-mount}"); # verify that the command is idempotent + machine.succeed("test -b /dev/zvol/zroot/zfs_testvolume"); + machine.succeed("grep -qs '/mnt/ext4onzfs' /proc/mounts"); + ''; +} + |