summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--default.nix65
-rw-r--r--example/zfs.nix41
-rw-r--r--tests/zfs.nix40
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");
+ '';
+}
+