storage-refactor #9
2 changed files with 0 additions and 368 deletions
|
|
@ -1,267 +0,0 @@
|
||||||
{
|
|
||||||
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 = {
|
|
||||||
auth = true;
|
|
||||||
tls = true;
|
|
||||||
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 {
|
|
||||||
enableMail = true;
|
|
||||||
|
|
||||||
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 = "-a zfs_notifications @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
|
|
||||||
)
|
|
||||||
];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
@ -1,101 +0,0 @@
|
||||||
{
|
|
||||||
config,
|
|
||||||
lib,
|
|
||||||
...
|
|
||||||
}: {
|
|
||||||
options.host.impermanence.enable = lib.mkEnableOption "are we going to use impermanence on this device";
|
|
||||||
|
|
||||||
config = lib.mkMerge [
|
|
||||||
{
|
|
||||||
assertions = [
|
|
||||||
{
|
|
||||||
assertion = !(config.host.impermanence.enable && !config.host.storage.enable);
|
|
||||||
message = ''
|
|
||||||
Disko storage must be enabled to use impermanence.
|
|
||||||
'';
|
|
||||||
}
|
|
||||||
];
|
|
||||||
}
|
|
||||||
(
|
|
||||||
lib.mkIf config.host.impermanence.enable {
|
|
||||||
assertions = [
|
|
||||||
{
|
|
||||||
assertion = config.host.impermanence.enable && config.host.storage.enable;
|
|
||||||
message = "Impermanence can not be used without managed host storage.";
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
# fixes issues with /var/lib/private not having the correct permissions https://github.com/nix-community/impermanence/issues/254
|
|
||||||
system.activationScripts."createPersistentStorageDirs".deps = ["var-lib-private-permissions" "users" "groups"];
|
|
||||||
system.activationScripts = {
|
|
||||||
"var-lib-private-permissions" = {
|
|
||||||
deps = ["specialfs"];
|
|
||||||
text = ''
|
|
||||||
mkdir -p /persist/system/root/var/lib/private
|
|
||||||
chmod 0700 /persist/system/root/var/lib/private
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
programs.fuse.userAllowOther = true;
|
|
||||||
|
|
||||||
boot.initrd.postResumeCommands = lib.mkAfter ''
|
|
||||||
zfs rollback -r rpool/local/system/root@blank
|
|
||||||
'';
|
|
||||||
|
|
||||||
fileSystems = {
|
|
||||||
"/".neededForBoot = true;
|
|
||||||
"/persist/system/root".neededForBoot = true;
|
|
||||||
"/persist/system/var/log".neededForBoot = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
host.storage.pool.extraDatasets = {
|
|
||||||
# persist datasets are datasets that contain information that we would like to keep around
|
|
||||||
"persist" = {
|
|
||||||
type = "zfs_fs";
|
|
||||||
options.canmount = "off";
|
|
||||||
options = {
|
|
||||||
"com.sun:auto-snapshot" = "true";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
# this is where root data actually lives
|
|
||||||
"persist/system/root" = {
|
|
||||||
type = "zfs_fs";
|
|
||||||
mountpoint = "/persist/system/root";
|
|
||||||
};
|
|
||||||
"persist/system/var/log" = {
|
|
||||||
type = "zfs_fs";
|
|
||||||
mountpoint = "/persist/system/var/log";
|
|
||||||
# logs should be append only so we shouldn't need to snapshot them
|
|
||||||
options = {
|
|
||||||
"com.sun:auto-snapshot" = "false";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
environment.persistence."/persist/system/var/log" = {
|
|
||||||
enable = true;
|
|
||||||
hideMounts = true;
|
|
||||||
directories = [
|
|
||||||
"/var/log"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
environment.persistence."/persist/system/root" = {
|
|
||||||
enable = true;
|
|
||||||
hideMounts = true;
|
|
||||||
directories = [
|
|
||||||
"/var/lib/nixos"
|
|
||||||
"/var/lib/systemd/coredump"
|
|
||||||
];
|
|
||||||
files = [
|
|
||||||
"/etc/machine-id"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
# TODO: this should live in leylas home manager configuration
|
|
||||||
security.sudo.extraConfig = "Defaults lecture=never";
|
|
||||||
}
|
|
||||||
)
|
|
||||||
];
|
|
||||||
}
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue