{ config, lib, ... }: let cfg = config.disko; in { options.disko = { name = lib.mkOption { type = lib.types.str; example = "chaos"; description = "Machine name used in eg zpool name."; }; rootDisk = lib.mkOption { type = with lib.types; nullOr str; default = null; example = "/dev/sda"; description = "Path of the root disk."; }; enableCeph = lib.mkOption { type = lib.types.bool; default = true; description = "Wether to include a ceph on the root disk."; }; enableLuks = lib.mkOption { type = lib.types.bool; default = true; description = "Wether to encrypt the root disk."; }; enableZfs = lib.mkOption { type = lib.types.bool; default = true; description = "Wether to include a zfs on the root disk."; }; }; config = { assertions = [ { assertion = cfg.enableCeph || cfg.enableZfs; message = "Must enable ceph or zfs!"; } { assertion = cfg.enableCeph -> cfg.enableLuks; message = "Ceph requires Luks!"; } ]; disko = { devices = let rootSize = 200; zfs = { size = if (!cfg.enableCeph) then "100%FREE" else "${toString rootSize}GiB"; content = { pool = cfg.name; type = "zfs"; }; }; in lib.mkIf (cfg.rootDisk != "") { disk.${cfg.rootDisk} = { device = cfg.rootDisk; type = "disk"; content = { type = "table"; format = "gpt"; partitions = lib.optional cfg.enableZfs { name = "ESP"; start = "1MiB"; end = "512MiB"; bootable = true; content = { type = "filesystem"; format = "vfat"; mountpoint = "/boot"; }; } ++ [ { name = "root"; start = if cfg.enableZfs then "512MiB" else "1MiB"; end = "100%"; part-type = "primary"; content = lib.optionalAttrs cfg.enableLuks { type = "luks"; name = "crypt-${cfg.name}"; # TODO: add password, otherwise prompt opens keyFile = "/$PWD/keyFile"; content = { type = "lvm_pv"; vg = "lvm-${cfg.name}"; }; } // lib.optionalAttrs (!cfg.enableLuks) zfs.content; } ]; }; }; } // lib.optionalAttrs cfg.enableLuks { lvm_vg."lvm-${cfg.name}" = { type = "lvm_vg"; lvs = lib.optionalAttrs cfg.enableCeph { # the header is 3650 byte long and substract an additional 446 byte for aligment # error messages: # Volume group "lvm-chaos" has insufficient free space (51195 extents): 51200 required. # Size is not a multiple of 512. Try using 40057405440 or 40057405952. ceph.size = let # convert GiB to bytes rootSizeMiB = rootSize * 1024 * 1024 * 1024; # convert back to MiB and allign to 4 MiB in the process roundToMiB = "/1024/1024/4*4"; # substract 512 MiB for /boot and 20 MiB for luks+header+other bootOther = "-512-20"; in "$((($(lsblk /dev/sda --noheadings --nodeps --output SIZE --bytes)-${toString rootSizeMiB})${roundToMiB}${bootOther}))MiB"; } // lib.optionalAttrs cfg.enableZfs { inherit zfs; }; }; } // { zpool."${cfg.name}" = { type = "zpool"; mountpoint = null; mountRoot = "/mnt"; rootFsOptions.acltype = "posixacl"; options = { ashift = "12"; autotrim = "on"; }; datasets = let dataset = mountpoint: { options = { canmount = "on"; compression = "zstd"; dnodesize = "auto"; normalization = "formD"; xattr = "sa"; inherit mountpoint; }; type = "zfs_fs"; }; in { "data" = dataset "/"; "data/etc" = dataset "/etc"; "data/home" = dataset "/home"; "data/var" = dataset "/var"; # used by services.postgresqlBackup and later by restic "data/var/backup" = dataset "/var/backup"; "data/var/lib" = dataset "/var/lib"; "data/var/log" = dataset "/var/log"; "nixos" = { options = { canmount = "off"; mountpoint = "none"; }; type = "zfs_fs"; }; "nixos/nix" = dataset "/nix"; "nixos/nix/store" = { options = { atime = "off"; canmount = "on"; mountpoint = "/nix/store"; }; type = "zfs_fs"; }; "nixos/nix/var" = dataset "/nix/var"; "reserved" = { # zfs uses copy on write and requires some free space to delete files when the disk is completely filled options = { canmount = "off"; mountpoint = "none"; reservation = "5GiB"; }; type = "zfs_fs"; }; }; }; }; # we use our own hardware-configuration.nix enableConfig = false; }; }; }