266 lines
8.4 KiB
Nix
266 lines
8.4 KiB
Nix
{
|
|
lib,
|
|
pkgs,
|
|
config,
|
|
inputs,
|
|
...
|
|
}: let
|
|
# there currently is a bug with disko that causes long disk names to be generated improperly this hash function should alleviate it when used for disk names instead of what we are defaulting to
|
|
# max gpt length is 36 and disk adds formats it like disk-xxxx-zfs which means we need to be 9 characters under that
|
|
hashDisk = drive: (builtins.substring 0 27 (builtins.hashString "sha256" drive));
|
|
|
|
vdevs =
|
|
builtins.map (
|
|
disks:
|
|
builtins.map (disk: lib.attrsets.nameValuePair (hashDisk disk) disk) disks
|
|
)
|
|
config.host.storage.pool.vdevs;
|
|
cache =
|
|
builtins.map (
|
|
disk: lib.attrsets.nameValuePair (hashDisk disk) disk
|
|
)
|
|
config.host.storage.pool.cache;
|
|
|
|
datasets = config.host.storage.pool.datasets // config.host.storage.pool.extraDatasets;
|
|
in {
|
|
options.host.storage = {
|
|
enable = lib.mkEnableOption "are we going create zfs disks with disko on this device";
|
|
encryption = lib.mkEnableOption "is the vdev going to be encrypted";
|
|
notifications = {
|
|
enable = lib.mkEnableOption "are notifications enabled";
|
|
host = lib.mkOption {
|
|
type = lib.types.str;
|
|
description = "what is the host that we are going to send the email to";
|
|
};
|
|
port = lib.mkOption {
|
|
type = lib.types.port;
|
|
description = "what port is the host using to receive mail on";
|
|
};
|
|
to = lib.mkOption {
|
|
type = lib.types.str;
|
|
description = "what account is the email going to be sent to";
|
|
};
|
|
user = lib.mkOption {
|
|
type = lib.types.str;
|
|
description = "what user is the email going to be set from";
|
|
};
|
|
tokenFile = lib.mkOption {
|
|
type = lib.types.str;
|
|
description = "file containing the password to be used by msmtp for notifications";
|
|
};
|
|
};
|
|
pool = {
|
|
mode = lib.mkOption {
|
|
type = lib.types.str;
|
|
default = "raidz2";
|
|
description = "what level of redundancy should this pool have";
|
|
};
|
|
# list of drives in pool that will have a boot partition put onto them
|
|
bootDrives = lib.mkOption {
|
|
type = lib.types.listOf lib.types.str;
|
|
description = "list of disks that are going to have a boot partition installed on them";
|
|
default = lib.lists.flatten config.host.storage.pool.vdevs;
|
|
};
|
|
# shorthand for vdevs if you only have 1 vdev
|
|
drives = lib.mkOption {
|
|
type = lib.types.listOf lib.types.str;
|
|
description = "list of drives that are going to be in the vdev";
|
|
default = [];
|
|
};
|
|
# list of all drives in each vdev
|
|
vdevs = lib.mkOption {
|
|
type = lib.types.listOf (lib.types.listOf lib.types.str);
|
|
description = "list of disks that are going to be in";
|
|
default = [config.host.storage.pool.drives];
|
|
};
|
|
# list of cache drives for pool
|
|
cache = lib.mkOption {
|
|
type = lib.types.listOf lib.types.str;
|
|
description = "list of drives that are going to be used as cache";
|
|
default = [];
|
|
};
|
|
# Default datasets that are needed to make a functioning system
|
|
datasets = lib.mkOption {
|
|
type = lib.types.attrsOf (inputs.disko.lib.subType {
|
|
types = {inherit (inputs.disko.lib.types) zfs_fs zfs_volume;};
|
|
});
|
|
default = {
|
|
"local" = {
|
|
type = "zfs_fs";
|
|
options.canmount = "off";
|
|
};
|
|
# nix directory needs to be available pre persist and doesn't need to be snapshotted or backed up
|
|
"local/system/nix" = {
|
|
type = "zfs_fs";
|
|
mountpoint = "/nix";
|
|
options = {
|
|
atime = "off";
|
|
relatime = "off";
|
|
canmount = "on";
|
|
};
|
|
};
|
|
# dataset for root that gets rolled back on every boot
|
|
"local/system/root" = {
|
|
type = "zfs_fs";
|
|
mountpoint = "/";
|
|
options = {
|
|
canmount = "on";
|
|
};
|
|
postCreateHook = ''
|
|
zfs snapshot rpool/local/system/root@blank
|
|
'';
|
|
};
|
|
};
|
|
};
|
|
extraDatasets = lib.mkOption {
|
|
type = lib.types.attrsOf (inputs.disko.lib.subType {
|
|
types = {inherit (inputs.disko.lib.types) zfs_fs zfs_volume;};
|
|
});
|
|
description = "List of datasets to define";
|
|
default = {};
|
|
};
|
|
};
|
|
};
|
|
|
|
config = lib.mkIf config.host.storage.enable {
|
|
programs.msmtp = lib.mkIf config.host.storage.notifications.enable {
|
|
enable = true;
|
|
setSendmail = true;
|
|
defaults = {
|
|
aliases = "/etc/aliases";
|
|
port = config.host.storage.notifications.port;
|
|
tls_trust_file = "/etc/ssl/certs/ca-certificates.crt";
|
|
tls = "on";
|
|
auth = "login";
|
|
tls_starttls = "off";
|
|
};
|
|
accounts = {
|
|
zfs_notifications = {
|
|
host = config.host.storage.notifications.host;
|
|
passwordeval = "cat ${config.host.storage.notifications.tokenFile}";
|
|
user = config.host.storage.notifications.user;
|
|
from = config.host.storage.notifications.user;
|
|
};
|
|
};
|
|
};
|
|
|
|
services.zfs = {
|
|
autoScrub.enable = true;
|
|
autoSnapshot.enable = true;
|
|
|
|
zed = lib.mkIf config.host.storage.notifications.enable {
|
|
# this option is broken we are just going to disable it
|
|
enableMail = false;
|
|
|
|
settings = {
|
|
ZED_DEBUG_LOG = "/tmp/zed.debug.log";
|
|
ZED_EMAIL_ADDR = [config.host.storage.notifications.to];
|
|
ZED_EMAIL_PROG = "${pkgs.msmtp}/bin/msmtp";
|
|
ZED_EMAIL_OPTS = "@ADDRESS@";
|
|
|
|
ZED_NOTIFY_INTERVAL_SECS = 3600;
|
|
ZED_NOTIFY_VERBOSE = true;
|
|
|
|
ZED_USE_ENCLOSURE_LEDS = true;
|
|
ZED_SCRUB_AFTER_RESILVER = true;
|
|
};
|
|
};
|
|
};
|
|
|
|
disko.devices = {
|
|
disk = (
|
|
builtins.listToAttrs (
|
|
builtins.map
|
|
(drive:
|
|
lib.attrsets.nameValuePair (drive.name) {
|
|
type = "disk";
|
|
device = "/dev/disk/by-id/${drive.value}";
|
|
content = {
|
|
type = "gpt";
|
|
partitions = {
|
|
ESP = lib.mkIf (builtins.elem drive.value config.host.storage.pool.bootDrives) {
|
|
# The 2GB here for the boot partition might be a bit overkill we probably only need like 1/4th of that but storage is cheap
|
|
size = "2G";
|
|
type = "EF00";
|
|
content = {
|
|
type = "filesystem";
|
|
format = "vfat";
|
|
mountpoint = "/boot";
|
|
mountOptions = ["umask=0077"];
|
|
};
|
|
};
|
|
zfs = {
|
|
size = "100%";
|
|
content = {
|
|
type = "zfs";
|
|
pool = "rpool";
|
|
};
|
|
};
|
|
};
|
|
};
|
|
})
|
|
(
|
|
(lib.lists.flatten vdevs) ++ cache
|
|
)
|
|
)
|
|
);
|
|
zpool = {
|
|
rpool = {
|
|
type = "zpool";
|
|
mode = {
|
|
topology = {
|
|
type = "topology";
|
|
vdev = (
|
|
builtins.map (disks: {
|
|
mode = config.host.storage.pool.mode;
|
|
members =
|
|
builtins.map (disk: disk.name) disks;
|
|
})
|
|
vdevs
|
|
);
|
|
cache = builtins.map (disk: disk.name) cache;
|
|
};
|
|
};
|
|
|
|
options = {
|
|
ashift = "12";
|
|
autotrim = "on";
|
|
};
|
|
|
|
rootFsOptions =
|
|
{
|
|
canmount = "off";
|
|
mountpoint = "none";
|
|
|
|
xattr = "sa";
|
|
acltype = "posixacl";
|
|
relatime = "on";
|
|
|
|
compression = "lz4";
|
|
|
|
"com.sun:auto-snapshot" = "false";
|
|
}
|
|
// (
|
|
lib.attrsets.optionalAttrs config.host.storage.encryption {
|
|
encryption = "on";
|
|
keyformat = "hex";
|
|
keylocation = "prompt";
|
|
}
|
|
);
|
|
|
|
datasets = lib.mkMerge [
|
|
(
|
|
lib.attrsets.mapAttrs (name: value: {
|
|
type = value.type;
|
|
options = value.options;
|
|
mountpoint = value.mountpoint;
|
|
postCreateHook = value.postCreateHook;
|
|
})
|
|
datasets
|
|
)
|
|
];
|
|
};
|
|
};
|
|
};
|
|
};
|
|
}
|