mirror of
https://github.com/nix-community/disko
synced 2024-11-10 06:14:14 +00:00
split disko type into multiple files
This commit is contained in:
parent
aa26c0ce0d
commit
48e4c06004
21 changed files with 1595 additions and 3 deletions
|
@ -2,7 +2,7 @@
|
|||
, rootMountPoint ? "/mnt"
|
||||
}:
|
||||
let
|
||||
types = import ./types.nix { inherit lib rootMountPoint; };
|
||||
types = import ./types { inherit lib rootMountPoint; };
|
||||
eval = cfg: lib.evalModules {
|
||||
modules = lib.singleton {
|
||||
# _file = toString input;
|
||||
|
|
2
doc.nix
2
doc.nix
|
@ -1,7 +1,7 @@
|
|||
{ lib, nixosOptionsDoc, runCommand, fetchurl, pandoc }:
|
||||
|
||||
let
|
||||
types = import ./types.nix {
|
||||
types = import ./types {
|
||||
inherit lib;
|
||||
rootMountPoint = "/mnt";
|
||||
};
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{ config, lib, pkgs, ... }:
|
||||
let
|
||||
types = import ./types.nix {
|
||||
types = import ./types {
|
||||
inherit lib;
|
||||
rootMountPoint = config.disko.rootMountPoint;
|
||||
};
|
||||
|
|
86
types/btrfs.nix
Normal file
86
types/btrfs.nix
Normal file
|
@ -0,0 +1,86 @@
|
|||
({ config, options, diskoLib, lib, subTypes, optionTypes, rootMountPoint, ... }:
|
||||
{
|
||||
options = {
|
||||
type = lib.mkOption {
|
||||
type = lib.types.enum [ "btrfs" ];
|
||||
internal = true;
|
||||
description = "Type";
|
||||
};
|
||||
extraArgs = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "";
|
||||
description = "Arguments to pass to BTRFS";
|
||||
};
|
||||
mountOptions = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.str;
|
||||
default = [ "defaults" ];
|
||||
description = "A list of options to pass to mount.";
|
||||
};
|
||||
subvolumes = lib.mkOption {
|
||||
type = lib.types.attrsOf subTypes.btrfs_subvol;
|
||||
default = { };
|
||||
description = "Subvolumes to define for BTRFS.";
|
||||
};
|
||||
mountpoint = lib.mkOption {
|
||||
type = lib.types.nullOr optionTypes.absolute-pathname;
|
||||
default = null;
|
||||
description = "A path to mount the BTRFS filesystem to.";
|
||||
};
|
||||
_meta = lib.mkOption {
|
||||
internal = true;
|
||||
readOnly = true;
|
||||
type = lib.types.functionTo diskoLib.jsonType;
|
||||
default = dev:
|
||||
diskoLib.deepMergeMap (subvol: subvol._meta dev) (lib.attrValues config.subvolumes);
|
||||
description = "Metadata";
|
||||
};
|
||||
_create = diskoLib.mkCreateOption {
|
||||
inherit config options;
|
||||
default = { dev }: ''
|
||||
mkfs.btrfs ${dev} ${config.extraArgs}
|
||||
${lib.concatMapStrings (subvol: subvol._create { inherit dev; }) (lib.attrValues config.subvolumes)}
|
||||
'';
|
||||
};
|
||||
_mount = diskoLib.mkMountOption {
|
||||
inherit config options;
|
||||
default = { dev }:
|
||||
let
|
||||
subvolMounts = diskoLib.deepMergeMap (subvol: subvol._mount { inherit dev; parent = config.mountpoint; }) (lib.attrValues config.subvolumes);
|
||||
in
|
||||
{
|
||||
fs = subvolMounts.fs // lib.optionalAttrs (!isNull config.mountpoint) {
|
||||
${config.mountpoint} = ''
|
||||
if ! findmnt ${dev} "${rootMountPoint}${config.mountpoint}" > /dev/null 2>&1; then
|
||||
mount ${dev} "${rootMountPoint}${config.mountpoint}" \
|
||||
${lib.concatMapStringsSep " " (opt: "-o ${opt}") config.mountOptions} \
|
||||
-o X-mount.mkdir
|
||||
fi
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
_config = lib.mkOption {
|
||||
internal = true;
|
||||
readOnly = true;
|
||||
default = dev: [
|
||||
(map (subvol: subvol._config dev config.mountpoint) (lib.attrValues config.subvolumes))
|
||||
(lib.optional (!isNull config.mountpoint) {
|
||||
fileSystems.${config.mountpoint} = {
|
||||
device = dev;
|
||||
fsType = "btrfs";
|
||||
options = config.mountOptions;
|
||||
};
|
||||
})
|
||||
];
|
||||
description = "NixOS configuration";
|
||||
};
|
||||
_pkgs = lib.mkOption {
|
||||
internal = true;
|
||||
readOnly = true;
|
||||
type = lib.types.functionTo (lib.types.listOf lib.types.package);
|
||||
default = pkgs:
|
||||
[ pkgs.btrfs-progs ] ++ lib.flatten (map (subvolume: subvolume._pkgs pkgs) (lib.attrValues config.subvolumes));
|
||||
description = "Packages";
|
||||
};
|
||||
};
|
||||
})
|
94
types/btrfs_subvol.nix
Normal file
94
types/btrfs_subvol.nix
Normal file
|
@ -0,0 +1,94 @@
|
|||
{ config, options, diskoLib, lib, optionTypes, rootMountPoint, ... }:
|
||||
{
|
||||
options = {
|
||||
name = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = config._module.args.name;
|
||||
description = "Name of the BTRFS subvolume.";
|
||||
};
|
||||
type = lib.mkOption {
|
||||
type = lib.types.enum [ "btrfs_subvol" ];
|
||||
default = "btrfs_subvol";
|
||||
internal = true;
|
||||
description = "Type";
|
||||
};
|
||||
extraArgs = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "";
|
||||
description = "Extra arguments to pass";
|
||||
};
|
||||
mountOptions = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.str;
|
||||
default = [ "defaults" ];
|
||||
description = "Options to pass to mount";
|
||||
};
|
||||
mountpoint = lib.mkOption {
|
||||
type = lib.types.nullOr optionTypes.absolute-pathname;
|
||||
default = null;
|
||||
description = "Location to mount the subvolume to.";
|
||||
};
|
||||
_meta = lib.mkOption {
|
||||
internal = true;
|
||||
readOnly = true;
|
||||
type = lib.types.functionTo diskoLib.jsonType;
|
||||
default = dev: { };
|
||||
description = "Metadata";
|
||||
};
|
||||
_create = diskoLib.mkCreateOption {
|
||||
inherit config options;
|
||||
default = { dev }: ''
|
||||
MNTPOINT=$(mktemp -d)
|
||||
(
|
||||
mount ${dev} "$MNTPOINT" -o subvol=/
|
||||
trap 'umount $MNTPOINT; rm -rf $MNTPOINT' EXIT
|
||||
btrfs subvolume create "$MNTPOINT"/${config.name} ${config.extraArgs}
|
||||
)
|
||||
'';
|
||||
};
|
||||
_mount = diskoLib.mkMountOption {
|
||||
inherit config options;
|
||||
default = { dev, parent }:
|
||||
let
|
||||
mountpoint =
|
||||
if (!isNull config.mountpoint) then config.mountpoint
|
||||
else if (isNull parent) then config.name
|
||||
else null;
|
||||
in
|
||||
lib.optionalAttrs (!isNull mountpoint) {
|
||||
fs.${mountpoint} = ''
|
||||
if ! findmnt ${dev} "${rootMountPoint}${mountpoint}" > /dev/null 2>&1; then
|
||||
mount ${dev} "${rootMountPoint}${mountpoint}" \
|
||||
${lib.concatMapStringsSep " " (opt: "-o ${opt}") (config.mountOptions ++ [ "subvol=${config.name}" ])} \
|
||||
-o X-mount.mkdir
|
||||
fi
|
||||
'';
|
||||
};
|
||||
};
|
||||
_config = lib.mkOption {
|
||||
internal = true;
|
||||
readOnly = true;
|
||||
default = dev: parent:
|
||||
let
|
||||
mountpoint =
|
||||
if (!isNull config.mountpoint) then config.mountpoint
|
||||
else if (isNull parent) then config.name
|
||||
else null;
|
||||
in
|
||||
lib.optional (!isNull mountpoint) {
|
||||
fileSystems.${mountpoint} = {
|
||||
device = dev;
|
||||
fsType = "btrfs";
|
||||
options = config.mountOptions ++ [ "subvol=${config.name}" ];
|
||||
};
|
||||
};
|
||||
description = "NixOS configuration";
|
||||
};
|
||||
_pkgs = lib.mkOption {
|
||||
internal = true;
|
||||
readOnly = true;
|
||||
type = lib.types.functionTo (lib.types.listOf lib.types.package);
|
||||
default = pkgs: [ pkgs.coreutils ];
|
||||
description = "Packages";
|
||||
};
|
||||
};
|
||||
}
|
346
types/default.nix
Normal file
346
types/default.nix
Normal file
|
@ -0,0 +1,346 @@
|
|||
{ lib, rootMountPoint }:
|
||||
with lib;
|
||||
with builtins;
|
||||
|
||||
rec {
|
||||
|
||||
diskoLib = {
|
||||
# like lib.types.oneOf but instead of a list takes an attrset
|
||||
# uses the field "type" to find the correct type in the attrset
|
||||
subType = typeAttr: lib.mkOptionType rec {
|
||||
name = "subType";
|
||||
description = "one of ${concatStringsSep "," (attrNames typeAttr)}";
|
||||
check = x: if x ? type then typeAttr.${x.type}.check x else throw "No type option set in:\n${generators.toPretty {} x}";
|
||||
merge = loc: defs:
|
||||
foldl' (res: def: typeAttr.${def.value.type}.merge loc [ def ]) { } defs;
|
||||
nestedTypes = typeAttr;
|
||||
};
|
||||
|
||||
# option for valid contents of partitions (basically like devices, but without tables)
|
||||
partitionType = lib.mkOption {
|
||||
type = lib.types.nullOr (diskoLib.subType { inherit (subTypes) btrfs filesystem zfs mdraid luks lvm_pv swap; });
|
||||
default = null;
|
||||
description = "The type of partition";
|
||||
};
|
||||
|
||||
# option for valid contents of devices
|
||||
deviceType = lib.mkOption {
|
||||
type = lib.types.nullOr (diskoLib.subType { inherit (subTypes) table btrfs filesystem zfs mdraid luks lvm_pv swap; });
|
||||
default = null;
|
||||
description = "The type of device";
|
||||
};
|
||||
|
||||
/* deepMergeMap takes a function and a list of attrsets and deep merges them
|
||||
|
||||
deepMergeMap :: -> (AttrSet -> AttrSet ) -> [ AttrSet ] -> Attrset
|
||||
|
||||
Example:
|
||||
deepMergeMap (x: x.t = "test") [ { x = { y = 1; z = 3; }; } { x = { bla = 234; }; } ]
|
||||
=> { x = { y = 1; z = 3; bla = 234; t = "test"; }; }
|
||||
*/
|
||||
deepMergeMap = f: listOfAttrs:
|
||||
foldr (attr: acc: (recursiveUpdate acc (f attr))) { } listOfAttrs;
|
||||
|
||||
/* get a device and an index to get the matching device name
|
||||
|
||||
deviceNumbering :: str -> int -> str
|
||||
|
||||
Example:
|
||||
deviceNumbering "/dev/sda" 3
|
||||
=> "/dev/sda3"
|
||||
|
||||
deviceNumbering "/dev/disk/by-id/xxx" 2
|
||||
=> "/dev/disk/by-id/xxx-part2"
|
||||
*/
|
||||
deviceNumbering = dev: index:
|
||||
if match "/dev/[vs]d.+" dev != null then
|
||||
dev + toString index # /dev/{s,v}da style
|
||||
else if match "/dev/disk/.+" dev != null then
|
||||
"${dev}-part${toString index}" # /dev/disk/by-id/xxx style
|
||||
else if match "/dev/(nvme|md/|mmcblk).+" dev != null then
|
||||
"${dev}p${toString index}" # /dev/nvme0n1p1 style
|
||||
else
|
||||
abort "${dev} seems not to be a supported disk format";
|
||||
|
||||
/* A nix option type representing a json datastructure, vendored from nixpkgs to avoid dependency on pkgs */
|
||||
jsonType =
|
||||
let
|
||||
valueType = lib.types.nullOr
|
||||
(lib.types.oneOf [
|
||||
lib.types.bool
|
||||
lib.types.int
|
||||
lib.types.float
|
||||
lib.types.str
|
||||
lib.types.path
|
||||
(lib.types.attrsOf valueType)
|
||||
(lib.types.listOf valueType)
|
||||
]) // {
|
||||
description = "JSON value";
|
||||
};
|
||||
in
|
||||
valueType;
|
||||
|
||||
/* Given a attrset of deviceDependencies and a devices attrset
|
||||
returns a sorted list by deviceDependencies. aborts if a loop is found
|
||||
|
||||
sortDevicesByDependencies :: AttrSet -> AttrSet -> [ [ str str ] ]
|
||||
*/
|
||||
sortDevicesByDependencies = deviceDependencies: devices:
|
||||
let
|
||||
dependsOn = a: b:
|
||||
elem a (attrByPath b [ ] deviceDependencies);
|
||||
maybeSortedDevices = toposort dependsOn (diskoLib.deviceList devices);
|
||||
in
|
||||
if (hasAttr "cycle" maybeSortedDevices) then
|
||||
abort "detected a cycle in your disk setup: ${maybeSortedDevices.cycle}"
|
||||
else
|
||||
maybeSortedDevices.result;
|
||||
|
||||
/* Takes a devices attrSet and returns it as a list
|
||||
|
||||
deviceList :: AttrSet -> [ [ str str ] ]
|
||||
|
||||
Example:
|
||||
deviceList { zfs.pool1 = {}; zfs.pool2 = {}; mdadm.raid1 = {}; }
|
||||
=> [ [ "zfs" "pool1" ] [ "zfs" "pool2" ] [ "mdadm" "raid1" ] ]
|
||||
*/
|
||||
deviceList = devices:
|
||||
concatLists (mapAttrsToList (n: v: (map (x: [ n x ]) (attrNames v))) devices);
|
||||
|
||||
/* Takes either a string or null and returns the string or an empty string
|
||||
|
||||
maybeStr :: Either (str null) -> str
|
||||
|
||||
Example:
|
||||
maybeStr null
|
||||
=> ""
|
||||
maybeSTr "hello world"
|
||||
=> "hello world"
|
||||
*/
|
||||
maybeStr = x: optionalString (!isNull x) x;
|
||||
|
||||
/* Takes a Submodules config and options argument and returns a serializable
|
||||
subset of config variables as a shell script snippet.
|
||||
*/
|
||||
defineHookVariables = { config, options }:
|
||||
let
|
||||
sanitizeName = lib.replaceStrings [ "-" ] [ "_" ];
|
||||
isAttrsOfSubmodule = o: o.type.name == "attrsOf" && o.type.nestedTypes.elemType.name == "submodule";
|
||||
isSerializable = n: o: !(
|
||||
lib.hasPrefix "_" n
|
||||
|| lib.hasSuffix "Hook" n
|
||||
|| isAttrsOfSubmodule o
|
||||
# TODO don't hardcode diskoLib.subType options.
|
||||
|| n == "content" || n == "partitions"
|
||||
);
|
||||
in
|
||||
lib.toShellVars
|
||||
(lib.mapAttrs'
|
||||
(n: o: lib.nameValuePair (sanitizeName n) o.value)
|
||||
(lib.filterAttrs isSerializable options));
|
||||
|
||||
mkHook = description: lib.mkOption {
|
||||
inherit description;
|
||||
type = lib.types.str;
|
||||
default = "";
|
||||
};
|
||||
|
||||
mkSubType = module: lib.types.submodule ([
|
||||
module
|
||||
|
||||
{
|
||||
options = {
|
||||
preCreateHook = diskoLib.mkHook "shell commands to run before create";
|
||||
postCreateHook = diskoLib.mkHook "shell commands to run after create";
|
||||
preMountHook = diskoLib.mkHook "shell commands to run before mount";
|
||||
postMountHook = diskoLib.mkHook "shell commands to run after mount";
|
||||
};
|
||||
config._module.args = {
|
||||
inherit diskoLib optionTypes subTypes rootMountPoint;
|
||||
};
|
||||
}
|
||||
]);
|
||||
|
||||
mkCreateOption = { config, options, default }@attrs:
|
||||
lib.mkOption {
|
||||
internal = true;
|
||||
readOnly = true;
|
||||
type = lib.types.functionTo lib.types.str;
|
||||
default = args:
|
||||
let
|
||||
name = "format";
|
||||
test = lib.optionalString (config ? name) "${config.${name}}";
|
||||
in
|
||||
''
|
||||
( # ${config.type} ${concatMapStringsSep " " (n: toString (config.${n} or "")) ["name" "device" "format" "mountpoint"]}
|
||||
${diskoLib.defineHookVariables { inherit config options; }}
|
||||
${config.preCreateHook}
|
||||
${attrs.default args}
|
||||
${config.postCreateHook}
|
||||
)
|
||||
'';
|
||||
description = "Creation script";
|
||||
};
|
||||
|
||||
mkMountOption = { config, options, default }@attrs:
|
||||
lib.mkOption {
|
||||
internal = true;
|
||||
readOnly = true;
|
||||
type = lib.types.functionTo diskoLib.jsonType;
|
||||
default = args: attrs.default args;
|
||||
description = "Mount script";
|
||||
};
|
||||
|
||||
|
||||
/* Takes a disko device specification, returns an attrset with metadata
|
||||
|
||||
meta :: lib.types.devices -> AttrSet
|
||||
*/
|
||||
meta = devices: diskoLib.deepMergeMap (dev: dev._meta) (flatten (map attrValues (attrValues devices)));
|
||||
|
||||
/* Takes a disko device specification and returns a string which formats the disks
|
||||
|
||||
create :: lib.types.devices -> str
|
||||
*/
|
||||
create = devices:
|
||||
let
|
||||
sortedDeviceList = diskoLib.sortDevicesByDependencies ((diskoLib.meta devices).deviceDependencies or { }) devices;
|
||||
in
|
||||
''
|
||||
set -efux
|
||||
|
||||
disko_devices_dir=$(mktemp -d)
|
||||
trap 'rm -rf "$disko_devices_dir"' EXIT
|
||||
mkdir -p "$disko_devices_dir"
|
||||
|
||||
${concatMapStrings (dev: (attrByPath (dev ++ [ "_create" ]) ({}: {}) devices) {}) sortedDeviceList}
|
||||
'';
|
||||
/* Takes a disko device specification and returns a string which mounts the disks
|
||||
|
||||
mount :: lib.types.devices -> str
|
||||
*/
|
||||
mount = devices:
|
||||
let
|
||||
fsMounts = diskoLib.deepMergeMap (dev: (dev._mount { }).fs or { }) (flatten (map attrValues (attrValues devices)));
|
||||
sortedDeviceList = diskoLib.sortDevicesByDependencies ((diskoLib.meta devices).deviceDependencies or { }) devices;
|
||||
in
|
||||
''
|
||||
set -efux
|
||||
# first create the necessary devices
|
||||
${concatMapStrings (dev: ((attrByPath (dev ++ [ "_mount" ]) {} devices) {}).dev or "") sortedDeviceList}
|
||||
|
||||
# and then mount the filesystems in alphabetical order
|
||||
${concatStrings (attrValues fsMounts)}
|
||||
'';
|
||||
|
||||
/* takes a disko device specification and returns a string which unmounts, destroys all disks and then runs create and mount
|
||||
|
||||
zapCreateMount :: lib.types.devices -> str
|
||||
*/
|
||||
zapCreateMount = devices: ''
|
||||
set -efux
|
||||
umount -Rv "${rootMountPoint}" || :
|
||||
|
||||
for dev in ${toString (lib.catAttrs "device" (lib.attrValues devices.disk))}; do
|
||||
${../disk-deactivate}/disk-deactivate "$dev" | bash -x
|
||||
done
|
||||
|
||||
echo 'creating partitions...'
|
||||
${diskoLib.create devices}
|
||||
echo 'mounting partitions...'
|
||||
${diskoLib.mount devices}
|
||||
'';
|
||||
/* Takes a disko device specification and returns a nixos configuration
|
||||
|
||||
config :: lib.types.devices -> nixosConfig
|
||||
*/
|
||||
config = devices: flatten (map (dev: dev._config) (flatten (map attrValues (attrValues devices))));
|
||||
/* Takes a disko device specification and returns a function to get the needed packages to format/mount the disks
|
||||
|
||||
packages :: lib.types.devices -> pkgs -> [ derivation ]
|
||||
*/
|
||||
packages = devices: pkgs: unique (flatten (map (dev: dev._pkgs pkgs) (flatten (map attrValues (attrValues devices)))));
|
||||
};
|
||||
|
||||
optionTypes = rec {
|
||||
filename = lib.mkOptionType {
|
||||
name = "filename";
|
||||
check = isString;
|
||||
merge = mergeOneOption;
|
||||
description = "A filename";
|
||||
};
|
||||
|
||||
absolute-pathname = lib.mkOptionType {
|
||||
name = "absolute pathname";
|
||||
check = x: isString x && substring 0 1 x == "/" && pathname.check x;
|
||||
merge = mergeOneOption;
|
||||
description = "An absolute path";
|
||||
};
|
||||
|
||||
pathname = lib.mkOptionType {
|
||||
name = "pathname";
|
||||
check = x:
|
||||
let
|
||||
# The filter is used to normalize paths, i.e. to remove duplicated and
|
||||
# trailing slashes. It also removes leading slashes, thus we have to
|
||||
# check for "/" explicitly below.
|
||||
xs = filter (s: stringLength s > 0) (splitString "/" x);
|
||||
in
|
||||
isString x && (x == "/" || (length xs > 0 && all filename.check xs));
|
||||
merge = mergeOneOption;
|
||||
description = "A path name";
|
||||
};
|
||||
};
|
||||
|
||||
/* topLevel type of the disko config, takes attrsets of disks, mdadms, zpools, nodevs, and lvm vgs.
|
||||
*/
|
||||
devices = lib.types.submodule {
|
||||
options = {
|
||||
disk = lib.mkOption {
|
||||
type = lib.types.attrsOf subTypes.disk;
|
||||
default = { };
|
||||
description = "Block device";
|
||||
};
|
||||
mdadm = lib.mkOption {
|
||||
type = lib.types.attrsOf subTypes.mdadm;
|
||||
default = { };
|
||||
description = "mdadm device";
|
||||
};
|
||||
zpool = lib.mkOption {
|
||||
type = lib.types.attrsOf subTypes.zpool;
|
||||
default = { };
|
||||
description = "ZFS pool device";
|
||||
};
|
||||
lvm_vg = lib.mkOption {
|
||||
type = lib.types.attrsOf subTypes.lvm_vg;
|
||||
default = { };
|
||||
description = "LVM VG device";
|
||||
};
|
||||
nodev = lib.mkOption {
|
||||
type = lib.types.attrsOf subTypes.nodev;
|
||||
default = { };
|
||||
description = "A non-block device";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
subTypes = lib.mapAttrs (_: module: diskoLib.mkSubType module) {
|
||||
nodev = ./nodev.nix;
|
||||
btrfs = ./btrfs.nix;
|
||||
btrfs_subvol = ./btrfs_subvol.nix;
|
||||
filesystem = ./filesystem.nix;
|
||||
table = ./table.nix;
|
||||
partition = ./partition.nix;
|
||||
swap = ./swap.nix;
|
||||
lvm_pv = ./lvm_pv.nix;
|
||||
lvm_vg = ./lvm_vg.nix;
|
||||
lvm_lv = ./lvm_lv.nix;
|
||||
zfs = ./zfs.nix;
|
||||
zpool = ./zpool.nix;
|
||||
zfs_dataset = ./zfs_dataset.nix;
|
||||
mdadm = ./mdadm.nix;
|
||||
mdraid = ./mdraid.nix;
|
||||
luks = ./luks.nix;
|
||||
disk = ./disk.nix;
|
||||
};
|
||||
}
|
52
types/disk.nix
Normal file
52
types/disk.nix
Normal file
|
@ -0,0 +1,52 @@
|
|||
{ config, options, lib, diskoLib, optionTypes, ... }:
|
||||
{
|
||||
options = {
|
||||
name = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = config._module.args.name;
|
||||
description = "Device name";
|
||||
};
|
||||
type = lib.mkOption {
|
||||
type = lib.types.enum [ "disk" ];
|
||||
default = "disk";
|
||||
internal = true;
|
||||
description = "Type";
|
||||
};
|
||||
device = lib.mkOption {
|
||||
type = optionTypes.absolute-pathname; # TODO check if subpath of /dev ? - No! eg: /.swapfile
|
||||
description = "Device path";
|
||||
};
|
||||
content = diskoLib.deviceType;
|
||||
_meta = lib.mkOption {
|
||||
internal = true;
|
||||
readOnly = true;
|
||||
type = diskoLib.jsonType;
|
||||
default =
|
||||
lib.optionalAttrs (!isNull config.content) (config.content._meta [ "disk" config.device ]);
|
||||
description = "Metadata";
|
||||
};
|
||||
_create = diskoLib.mkCreateOption {
|
||||
inherit config options;
|
||||
default = {}: config.content._create { dev = config.device; };
|
||||
};
|
||||
_mount = diskoLib.mkMountOption {
|
||||
inherit config options;
|
||||
default = {}:
|
||||
lib.optionalAttrs (!isNull config.content) (config.content._mount { dev = config.device; });
|
||||
};
|
||||
_config = lib.mkOption {
|
||||
internal = true;
|
||||
readOnly = true;
|
||||
default =
|
||||
lib.optional (!isNull config.content) (config.content._config config.device);
|
||||
description = "NixOS configuration";
|
||||
};
|
||||
_pkgs = lib.mkOption {
|
||||
internal = true;
|
||||
readOnly = true;
|
||||
type = lib.types.functionTo (lib.types.listOf lib.types.package);
|
||||
default = pkgs: [ pkgs.jq ] ++ lib.optionals (!isNull config.content) (config.content._pkgs pkgs);
|
||||
description = "Packages";
|
||||
};
|
||||
};
|
||||
}
|
86
types/filesystem.nix
Normal file
86
types/filesystem.nix
Normal file
|
@ -0,0 +1,86 @@
|
|||
{ config, options, lib, diskoLib, optionTypes, rootMountPoint, ... }:
|
||||
{
|
||||
options = {
|
||||
type = lib.mkOption {
|
||||
type = lib.types.enum [ "filesystem" ];
|
||||
internal = true;
|
||||
description = "Type";
|
||||
};
|
||||
extraArgs = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "";
|
||||
description = "Arguments to pass";
|
||||
};
|
||||
mountOptions = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.str;
|
||||
default = [ "defaults" ];
|
||||
description = "Options to pass to mount";
|
||||
};
|
||||
mountpoint = lib.mkOption {
|
||||
type = optionTypes.absolute-pathname;
|
||||
description = "Path to mount the filesystem to";
|
||||
};
|
||||
format = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "Format of the filesystem";
|
||||
};
|
||||
_meta = lib.mkOption {
|
||||
internal = true;
|
||||
readOnly = true;
|
||||
type = lib.types.functionTo diskoLib.jsonType;
|
||||
default = dev: { };
|
||||
description = "Metadata";
|
||||
};
|
||||
_create = diskoLib.mkCreateOption {
|
||||
inherit config options;
|
||||
default = { dev }: ''
|
||||
mkfs.${config.format} \
|
||||
${config.extraArgs} \
|
||||
${dev}
|
||||
'';
|
||||
};
|
||||
_mount = diskoLib.mkMountOption {
|
||||
inherit config options;
|
||||
default = { dev }: {
|
||||
fs.${config.mountpoint} = ''
|
||||
if ! findmnt ${dev} "${rootMountPoint}${config.mountpoint}" > /dev/null 2>&1; then
|
||||
mount ${dev} "${rootMountPoint}${config.mountpoint}" \
|
||||
-t "${config.format}" \
|
||||
${lib.concatMapStringsSep " " (opt: "-o ${opt}") config.mountOptions} \
|
||||
-o X-mount.mkdir
|
||||
fi
|
||||
'';
|
||||
};
|
||||
};
|
||||
_config = lib.mkOption {
|
||||
internal = true;
|
||||
readOnly = true;
|
||||
default = dev: [{
|
||||
fileSystems.${config.mountpoint} = {
|
||||
device = dev;
|
||||
fsType = config.format;
|
||||
options = config.mountOptions;
|
||||
};
|
||||
}];
|
||||
description = "NixOS configuration";
|
||||
};
|
||||
_pkgs = lib.mkOption {
|
||||
internal = true;
|
||||
readOnly = true;
|
||||
# type = lib.types.functionTo (lib.types.listOf lib.types.package);
|
||||
default = pkgs:
|
||||
[ pkgs.util-linux ] ++ (
|
||||
# TODO add many more
|
||||
if (config.format == "xfs") then [ pkgs.xfsprogs ]
|
||||
else if (config.format == "btrfs") then [ pkgs.btrfs-progs ]
|
||||
else if (config.format == "vfat") then [ pkgs.dosfstools ]
|
||||
else if (config.format == "ext2") then [ pkgs.e2fsprogs ]
|
||||
else if (config.format == "ext3") then [ pkgs.e2fsprogs ]
|
||||
else if (config.format == "ext4") then [ pkgs.e2fsprogs ]
|
||||
else if (config.format == "bcachefs") then [ pkgs.bcachefs-tools ]
|
||||
else [ ]
|
||||
);
|
||||
description = "Packages";
|
||||
};
|
||||
};
|
||||
}
|
73
types/luks.nix
Normal file
73
types/luks.nix
Normal file
|
@ -0,0 +1,73 @@
|
|||
{ config, options, lib, diskoLib, optionTypes, ... }:
|
||||
{
|
||||
options = {
|
||||
type = lib.mkOption {
|
||||
type = lib.types.enum [ "luks" ];
|
||||
internal = true;
|
||||
description = "Type";
|
||||
};
|
||||
name = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "Name of the LUKS";
|
||||
};
|
||||
keyFile = lib.mkOption {
|
||||
type = lib.types.nullOr optionTypes.absolute-pathname;
|
||||
default = null;
|
||||
description = "Path to the key for encryption";
|
||||
};
|
||||
extraArgs = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.str;
|
||||
default = [ ];
|
||||
description = "Extra arguments";
|
||||
};
|
||||
content = diskoLib.deviceType;
|
||||
_meta = lib.mkOption {
|
||||
internal = true;
|
||||
readOnly = true;
|
||||
type = lib.types.functionTo diskoLib.jsonType;
|
||||
default = dev:
|
||||
lib.optionalAttrs (!isNull config.content) (config.content._meta dev);
|
||||
description = "Metadata";
|
||||
};
|
||||
_create = diskoLib.mkCreateOption {
|
||||
inherit config options;
|
||||
default = { dev }: ''
|
||||
cryptsetup -q luksFormat ${dev} ${diskoLib.maybeStr config.keyFile} ${toString config.extraArgs}
|
||||
cryptsetup luksOpen ${dev} ${config.name} ${lib.optionalString (!isNull config.keyFile) "--key-file ${config.keyFile}"}
|
||||
${lib.optionalString (!isNull config.content) (config.content._create {dev = "/dev/mapper/${config.name}";})}
|
||||
'';
|
||||
};
|
||||
_mount = diskoLib.mkMountOption {
|
||||
inherit config options;
|
||||
default = { dev }:
|
||||
let
|
||||
contentMount = config.content._mount { dev = "/dev/mapper/${config.name}"; };
|
||||
in
|
||||
{
|
||||
dev = ''
|
||||
cryptsetup status ${config.name} >/dev/null 2>/dev/null ||
|
||||
cryptsetup luksOpen ${dev} ${config.name} ${lib.optionalString (!isNull config.keyFile) "--key-file ${config.keyFile}"}
|
||||
${lib.optionalString (!isNull config.content) contentMount.dev or ""}
|
||||
'';
|
||||
fs = lib.optionalAttrs (!isNull config.content) contentMount.fs or { };
|
||||
};
|
||||
};
|
||||
_config = lib.mkOption {
|
||||
internal = true;
|
||||
readOnly = true;
|
||||
default = dev:
|
||||
[
|
||||
# TODO do we need this always in initrd and only there?
|
||||
{ boot.initrd.luks.devices.${config.name}.device = dev; }
|
||||
] ++ (lib.optional (!isNull config.content) (config.content._config "/dev/mapper/${config.name}"));
|
||||
description = "NixOS configuration";
|
||||
};
|
||||
_pkgs = lib.mkOption {
|
||||
internal = true;
|
||||
readOnly = true;
|
||||
type = lib.types.functionTo (lib.types.listOf lib.types.package);
|
||||
default = pkgs: [ pkgs.cryptsetup ] ++ (lib.optionals (!isNull config.content) (config.content._pkgs pkgs));
|
||||
description = "Packages";
|
||||
};
|
||||
};
|
||||
}
|
76
types/lvm_lv.nix
Normal file
76
types/lvm_lv.nix
Normal file
|
@ -0,0 +1,76 @@
|
|||
{ config, options, lib, diskoLib, ... }:
|
||||
{
|
||||
options = {
|
||||
name = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = config._module.args.name;
|
||||
description = "Name of the logical volume";
|
||||
};
|
||||
type = lib.mkOption {
|
||||
type = lib.types.enum [ "lvm_lv" ];
|
||||
default = "lvm_lv";
|
||||
internal = true;
|
||||
description = "Type";
|
||||
};
|
||||
size = lib.mkOption {
|
||||
type = lib.types.str; # TODO lvm size type
|
||||
description = "Size of the logical volume";
|
||||
};
|
||||
lvm_type = lib.mkOption {
|
||||
type = lib.types.nullOr (lib.types.enum [ "mirror" "raid0" "raid1" ]); # TODO add all lib.types
|
||||
default = null; # maybe there is always a default type?
|
||||
description = "LVM type";
|
||||
};
|
||||
extraArgs = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "";
|
||||
description = "Extra arguments";
|
||||
};
|
||||
content = diskoLib.partitionType;
|
||||
_meta = lib.mkOption {
|
||||
internal = true;
|
||||
readOnly = true;
|
||||
type = lib.types.functionTo diskoLib.jsonType;
|
||||
default = dev:
|
||||
lib.optionalAttrs (!isNull config.content) (config.content._meta dev);
|
||||
description = "Metadata";
|
||||
};
|
||||
_create = diskoLib.mkCreateOption {
|
||||
inherit config options;
|
||||
default = { vg }: ''
|
||||
lvcreate \
|
||||
--yes \
|
||||
${if lib.hasInfix "%" config.size then "-l" else "-L"} ${config.size} \
|
||||
-n ${config.name} \
|
||||
${lib.optionalString (!isNull config.lvm_type) "--type=${config.lvm_type}"} \
|
||||
${config.extraArgs} \
|
||||
${vg}
|
||||
${lib.optionalString (!isNull config.content) (config.content._create {dev = "/dev/${vg}/${config.name}";})}
|
||||
'';
|
||||
};
|
||||
_mount = diskoLib.mkMountOption {
|
||||
inherit config options;
|
||||
default = { vg }:
|
||||
lib.optionalAttrs (!isNull config.content) (config.content._mount { dev = "/dev/${vg}/${config.name}"; });
|
||||
};
|
||||
_config = lib.mkOption {
|
||||
internal = true;
|
||||
readOnly = true;
|
||||
default = vg:
|
||||
[
|
||||
(lib.optional (!isNull config.content) (config.content._config "/dev/${vg}/${config.name}"))
|
||||
(lib.optional (!isNull config.lvm_type) {
|
||||
boot.initrd.kernelModules = [ "dm-${config.lvm_type}" ];
|
||||
})
|
||||
];
|
||||
description = "NixOS configuration";
|
||||
};
|
||||
_pkgs = lib.mkOption {
|
||||
internal = true;
|
||||
readOnly = true;
|
||||
type = lib.types.functionTo (lib.types.listOf lib.types.package);
|
||||
default = pkgs: lib.optionals (!isNull config.content) (config.content._pkgs pkgs);
|
||||
description = "Packages";
|
||||
};
|
||||
};
|
||||
}
|
48
types/lvm_pv.nix
Normal file
48
types/lvm_pv.nix
Normal file
|
@ -0,0 +1,48 @@
|
|||
{ config, options, lib, diskoLib, ... }:
|
||||
{
|
||||
options = {
|
||||
type = lib.mkOption {
|
||||
type = lib.types.enum [ "lvm_pv" ];
|
||||
internal = true;
|
||||
description = "Type";
|
||||
};
|
||||
vg = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "Volume group";
|
||||
};
|
||||
_meta = lib.mkOption {
|
||||
internal = true;
|
||||
readOnly = true;
|
||||
type = lib.types.functionTo diskoLib.jsonType;
|
||||
default = dev: {
|
||||
deviceDependencies.lvm_vg.${config.vg} = [ dev ];
|
||||
};
|
||||
description = "Metadata";
|
||||
};
|
||||
_create = diskoLib.mkCreateOption {
|
||||
inherit config options;
|
||||
default = { dev }: ''
|
||||
pvcreate ${dev}
|
||||
echo "${dev}" >> $disko_devices_dir/lvm_${config.vg}
|
||||
'';
|
||||
};
|
||||
_mount = diskoLib.mkMountOption {
|
||||
inherit config options;
|
||||
default = { dev }:
|
||||
{ };
|
||||
};
|
||||
_config = lib.mkOption {
|
||||
internal = true;
|
||||
readOnly = true;
|
||||
default = dev: [ ];
|
||||
description = "NixOS configuration";
|
||||
};
|
||||
_pkgs = lib.mkOption {
|
||||
internal = true;
|
||||
readOnly = true;
|
||||
type = lib.types.functionTo (lib.types.listOf lib.types.package);
|
||||
default = pkgs: [ pkgs.lvm2 ];
|
||||
description = "Packages";
|
||||
};
|
||||
};
|
||||
}
|
63
types/lvm_vg.nix
Normal file
63
types/lvm_vg.nix
Normal file
|
@ -0,0 +1,63 @@
|
|||
{ config, options, lib, diskoLib, subTypes, ... }:
|
||||
{
|
||||
options = {
|
||||
name = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = config._module.args.name;
|
||||
description = "Name of the volume gorup";
|
||||
};
|
||||
type = lib.mkOption {
|
||||
type = lib.types.enum [ "lvm_vg" ];
|
||||
internal = true;
|
||||
description = "Type";
|
||||
};
|
||||
lvs = lib.mkOption {
|
||||
type = lib.types.attrsOf subTypes.lvm_lv;
|
||||
default = { };
|
||||
description = "LVS for the volume group";
|
||||
};
|
||||
_meta = lib.mkOption {
|
||||
internal = true;
|
||||
readOnly = true;
|
||||
type = diskoLib.jsonType;
|
||||
default =
|
||||
diskoLib.deepMergeMap (lv: lv._meta [ "lvm_vg" config.name ]) (lib.attrValues config.lvs);
|
||||
description = "Metadata";
|
||||
};
|
||||
_create = diskoLib.mkCreateOption {
|
||||
inherit config options;
|
||||
default = {}: ''
|
||||
vgcreate ${config.name} $(tr '\n' ' ' < $disko_devices_dir/lvm_${config.name})
|
||||
${lib.concatMapStrings (lv: lv._create {vg = config.name; }) (lib.attrValues config.lvs)}
|
||||
'';
|
||||
};
|
||||
_mount = diskoLib.mkMountOption {
|
||||
inherit config options;
|
||||
default = {}:
|
||||
let
|
||||
lvMounts = diskoLib.deepMergeMap (lv: lv._mount { vg = config.name; }) (lib.attrValues config.lvs);
|
||||
in
|
||||
{
|
||||
dev = ''
|
||||
vgchange -a y
|
||||
${lib.concatMapStrings (x: x.dev or "") (lib.attrValues lvMounts)}
|
||||
'';
|
||||
fs = lvMounts.fs;
|
||||
};
|
||||
};
|
||||
_config = lib.mkOption {
|
||||
internal = true;
|
||||
readOnly = true;
|
||||
default =
|
||||
map (lv: lv._config config.name) (lib.attrValues config.lvs);
|
||||
description = "NixOS configuration";
|
||||
};
|
||||
_pkgs = lib.mkOption {
|
||||
internal = true;
|
||||
readOnly = true;
|
||||
type = lib.types.functionTo (lib.types.listOf lib.types.package);
|
||||
default = pkgs: lib.flatten (map (lv: lv._pkgs pkgs) (lib.attrValues config.lvs));
|
||||
description = "Packages";
|
||||
};
|
||||
};
|
||||
}
|
69
types/mdadm.nix
Normal file
69
types/mdadm.nix
Normal file
|
@ -0,0 +1,69 @@
|
|||
{ config, options, lib, diskoLib, ... }:
|
||||
{
|
||||
options = {
|
||||
name = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = config._module.args.name;
|
||||
description = "Name";
|
||||
};
|
||||
type = lib.mkOption {
|
||||
type = lib.types.enum [ "mdadm" ];
|
||||
default = "mdadm";
|
||||
internal = true;
|
||||
description = "Type";
|
||||
};
|
||||
level = lib.mkOption {
|
||||
type = lib.types.int;
|
||||
default = 1;
|
||||
description = "mdadm level";
|
||||
};
|
||||
metadata = lib.mkOption {
|
||||
type = lib.types.enum [ "1" "1.0" "1.1" "1.2" "default" "ddf" "imsm" ];
|
||||
default = "default";
|
||||
description = "Metadata";
|
||||
};
|
||||
content = diskoLib.deviceType;
|
||||
_meta = lib.mkOption {
|
||||
internal = true;
|
||||
readOnly = true;
|
||||
type = diskoLib.jsonType;
|
||||
default =
|
||||
lib.optionalAttrs (!isNull config.content) (config.content._meta [ "mdadm" config.name ]);
|
||||
description = "Metadata";
|
||||
};
|
||||
_create = diskoLib.mkCreateOption {
|
||||
inherit config options;
|
||||
default = {}: ''
|
||||
echo 'y' | mdadm --create /dev/md/${config.name} \
|
||||
--level=${toString config.level} \
|
||||
--raid-devices=$(wc -l $disko_devices_dir/raid_${config.name} | cut -f 1 -d " ") \
|
||||
--metadata=${config.metadata} \
|
||||
--force \
|
||||
--homehost=any \
|
||||
$(tr '\n' ' ' < $disko_devices_dir/raid_${config.name})
|
||||
udevadm trigger --subsystem-match=block; udevadm settle
|
||||
${lib.optionalString (!isNull config.content) (config.content._create {dev = "/dev/md/${config.name}";})}
|
||||
'';
|
||||
};
|
||||
_mount = diskoLib.mkMountOption {
|
||||
inherit config options;
|
||||
default = {}:
|
||||
lib.optionalAttrs (!isNull config.content) (config.content._mount { dev = "/dev/md/${config.name}"; });
|
||||
# TODO we probably need to assemble the mdadm somehow
|
||||
};
|
||||
_config = lib.mkOption {
|
||||
internal = true;
|
||||
readOnly = true;
|
||||
default =
|
||||
lib.optional (!isNull config.content) (config.content._config "/dev/md/${config.name}");
|
||||
description = "NixOS configuration";
|
||||
};
|
||||
_pkgs = lib.mkOption {
|
||||
internal = true;
|
||||
readOnly = true;
|
||||
type = lib.types.functionTo (lib.types.listOf lib.types.package);
|
||||
default = pkgs: (lib.optionals (!isNull config.content) (config.content._pkgs pkgs));
|
||||
description = "Packages";
|
||||
};
|
||||
};
|
||||
}
|
48
types/mdraid.nix
Normal file
48
types/mdraid.nix
Normal file
|
@ -0,0 +1,48 @@
|
|||
{ config, options, lib, diskoLib, ... }:
|
||||
{
|
||||
options = {
|
||||
type = lib.mkOption {
|
||||
type = lib.types.enum [ "mdraid" ];
|
||||
internal = true;
|
||||
description = "Type";
|
||||
};
|
||||
|
||||
name = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "Name";
|
||||
};
|
||||
_meta = lib.mkOption {
|
||||
internal = true;
|
||||
readOnly = true;
|
||||
type = lib.types.functionTo diskoLib.jsonType;
|
||||
default = dev: {
|
||||
deviceDependencies.mdadm.${config.name} = [ dev ];
|
||||
};
|
||||
description = "Metadata";
|
||||
};
|
||||
_create = diskoLib.mkCreateOption {
|
||||
inherit config options;
|
||||
default = { dev }: ''
|
||||
echo "${dev}" >> $disko_devices_dir/raid_${config.name}
|
||||
'';
|
||||
};
|
||||
_mount = diskoLib.mkMountOption {
|
||||
inherit config options;
|
||||
default = { dev }:
|
||||
{ };
|
||||
};
|
||||
_config = lib.mkOption {
|
||||
internal = true;
|
||||
readOnly = true;
|
||||
default = dev: [ ];
|
||||
description = "NixOS configuration";
|
||||
};
|
||||
_pkgs = lib.mkOption {
|
||||
internal = true;
|
||||
readOnly = true;
|
||||
type = lib.types.functionTo (lib.types.listOf lib.types.package);
|
||||
default = pkgs: [ pkgs.mdadm ];
|
||||
description = "Packages";
|
||||
};
|
||||
};
|
||||
}
|
72
types/nodev.nix
Normal file
72
types/nodev.nix
Normal file
|
@ -0,0 +1,72 @@
|
|||
{ lib, config, options, diskoLib, optionTypes, rootMountPoint, ... }:
|
||||
{
|
||||
options = {
|
||||
type = lib.mkOption {
|
||||
type = lib.types.enum [ "nodev" ];
|
||||
default = "nodev";
|
||||
internal = true;
|
||||
description = "Device type";
|
||||
};
|
||||
fsType = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "File system type";
|
||||
};
|
||||
device = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "none";
|
||||
description = "Device to use";
|
||||
};
|
||||
mountpoint = lib.mkOption {
|
||||
type = optionTypes.absolute-pathname;
|
||||
default = config._module.args.name;
|
||||
description = "Location to mount the file system at";
|
||||
};
|
||||
mountOptions = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.str;
|
||||
default = [ "defaults" ];
|
||||
description = "Options to pass to mount";
|
||||
};
|
||||
_meta = lib.mkOption {
|
||||
internal = true;
|
||||
readOnly = true;
|
||||
type = diskoLib.jsonType;
|
||||
default = { };
|
||||
description = "Metadata";
|
||||
};
|
||||
_create = diskoLib.mkCreateOption {
|
||||
inherit config options;
|
||||
default = {}: "";
|
||||
};
|
||||
_mount = diskoLib.mkMountOption {
|
||||
inherit config options;
|
||||
default = {}: {
|
||||
fs.${config.mountpoint} = ''
|
||||
if ! findmnt ${config.fsType} "${rootMountPoint}${config.mountpoint}" > /dev/null 2>&1; then
|
||||
mount -t ${config.fsType} ${config.device} "${rootMountPoint}${config.mountpoint}" \
|
||||
${lib.concatMapStringsSep " " (opt: "-o ${opt}") config.mountOptions} \
|
||||
-o X-mount.mkdir
|
||||
fi
|
||||
'';
|
||||
};
|
||||
};
|
||||
_config = lib.mkOption {
|
||||
internal = true;
|
||||
readOnly = true;
|
||||
default = [{
|
||||
fileSystems.${config.mountpoint} = {
|
||||
device = config.device;
|
||||
fsType = config.fsType;
|
||||
options = config.mountOptions;
|
||||
};
|
||||
}];
|
||||
description = "NixOS configuration";
|
||||
};
|
||||
_pkgs = lib.mkOption {
|
||||
internal = true;
|
||||
readOnly = true;
|
||||
type = lib.types.functionTo (lib.types.listOf lib.types.package);
|
||||
default = pkgs: [ ];
|
||||
description = "Packages";
|
||||
};
|
||||
};
|
||||
}
|
100
types/partition.nix
Normal file
100
types/partition.nix
Normal file
|
@ -0,0 +1,100 @@
|
|||
{ config, options, lib, diskoLib, ... }:
|
||||
{
|
||||
options = {
|
||||
type = lib.mkOption {
|
||||
type = lib.types.enum [ "partition" ];
|
||||
internal = true;
|
||||
description = "Type";
|
||||
};
|
||||
part-type = lib.mkOption {
|
||||
type = lib.types.enum [ "primary" "logical" "extended" ];
|
||||
default = "primary";
|
||||
description = "Partition type";
|
||||
};
|
||||
fs-type = lib.mkOption {
|
||||
type = lib.types.nullOr (lib.types.enum [ "btrfs" "ext2" "ext3" "ext4" "fat16" "fat32" "hfs" "hfs+" "linux-swap" "ntfs" "reiserfs" "udf" "xfs" ]);
|
||||
default = null;
|
||||
description = "Filesystem type to use";
|
||||
};
|
||||
name = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.str;
|
||||
description = "Name of the partition";
|
||||
};
|
||||
start = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "0%";
|
||||
description = "Start of the partition";
|
||||
};
|
||||
end = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "100%";
|
||||
description = "End of the partition";
|
||||
};
|
||||
index = lib.mkOption {
|
||||
type = lib.types.int;
|
||||
# TODO find a better way to get the index
|
||||
default = lib.toInt (lib.head (builtins.match ".*entry ([[:digit:]]+)]" config._module.args.name));
|
||||
description = "Index of the partition";
|
||||
};
|
||||
flags = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.str;
|
||||
default = [ ];
|
||||
description = "Partition flags";
|
||||
};
|
||||
bootable = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = false;
|
||||
description = "Whether to make the partition bootable";
|
||||
};
|
||||
content = diskoLib.partitionType;
|
||||
_meta = lib.mkOption {
|
||||
internal = true;
|
||||
readOnly = true;
|
||||
type = lib.types.functionTo diskoLib.jsonType;
|
||||
default = dev:
|
||||
lib.optionalAttrs (!isNull config.content) (config.content._meta dev);
|
||||
description = "Metadata";
|
||||
};
|
||||
_create = diskoLib.mkCreateOption {
|
||||
inherit config options;
|
||||
default = { dev, type }: ''
|
||||
${lib.optionalString (type == "gpt") ''
|
||||
parted -s ${dev} -- mkpart ${config.name} ${diskoLib.maybeStr config.fs-type} ${config.start} ${config.end}
|
||||
''}
|
||||
${lib.optionalString (type == "msdos") ''
|
||||
parted -s ${dev} -- mkpart ${config.part-type} ${diskoLib.maybeStr config.fs-type} ${diskoLib.maybeStr config.fs-type} ${config.start} ${config.end}
|
||||
''}
|
||||
# ensure /dev/disk/by-path/..-partN exists before continuing
|
||||
udevadm trigger --subsystem-match=block; udevadm settle
|
||||
${lib.optionalString (config.bootable) ''
|
||||
parted -s ${dev} -- set ${toString config.index} boot on
|
||||
''}
|
||||
${lib.concatMapStringsSep "" (flag: ''
|
||||
parted -s ${dev} -- set ${toString config.index} ${flag} on
|
||||
'') config.flags}
|
||||
# ensure further operations can detect new partitions
|
||||
udevadm trigger --subsystem-match=block; udevadm settle
|
||||
${lib.optionalString (!isNull config.content) (config.content._create {dev = (diskoLib.deviceNumbering dev config.index);})}
|
||||
'';
|
||||
};
|
||||
_mount = diskoLib.mkMountOption {
|
||||
inherit config options;
|
||||
default = { dev }:
|
||||
lib.optionalAttrs (!isNull config.content) (config.content._mount { dev = (diskoLib.deviceNumbering dev config.index); });
|
||||
};
|
||||
_config = lib.mkOption {
|
||||
internal = true;
|
||||
readOnly = true;
|
||||
default = dev:
|
||||
lib.optional (!isNull config.content) (config.content._config (diskoLib.deviceNumbering dev config.index));
|
||||
description = "NixOS configuration";
|
||||
};
|
||||
_pkgs = lib.mkOption {
|
||||
internal = true;
|
||||
readOnly = true;
|
||||
type = lib.types.functionTo (lib.types.listOf lib.types.package);
|
||||
default = pkgs: lib.optionals (!isNull config.content) (config.content._pkgs pkgs);
|
||||
description = "Packages";
|
||||
};
|
||||
};
|
||||
}
|
56
types/swap.nix
Normal file
56
types/swap.nix
Normal file
|
@ -0,0 +1,56 @@
|
|||
{ diskoLib, config, options, lib, ... }:
|
||||
{
|
||||
options = {
|
||||
type = lib.mkOption {
|
||||
type = lib.types.enum [ "swap" ];
|
||||
internal = true;
|
||||
description = "Type";
|
||||
};
|
||||
randomEncryption = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = false;
|
||||
description = "Whether to randomly encrypt the swap";
|
||||
};
|
||||
_meta = lib.mkOption {
|
||||
internal = true;
|
||||
readOnly = true;
|
||||
type = lib.types.functionTo diskoLib.jsonType;
|
||||
default = dev: { };
|
||||
description = "Metadata";
|
||||
};
|
||||
_create = diskoLib.mkCreateOption {
|
||||
inherit config options;
|
||||
default = { dev }: ''
|
||||
mkswap ${dev}
|
||||
'';
|
||||
};
|
||||
_mount = diskoLib.mkMountOption {
|
||||
inherit config options;
|
||||
default = { dev }: {
|
||||
fs.${dev} = ''
|
||||
if ! swapon --show | grep -q '^${dev} '; then
|
||||
swapon ${dev}
|
||||
fi
|
||||
'';
|
||||
};
|
||||
};
|
||||
_config = lib.mkOption {
|
||||
internal = true;
|
||||
readOnly = true;
|
||||
default = dev: [{
|
||||
swapDevices = [{
|
||||
device = dev;
|
||||
randomEncryption = config.randomEncryption;
|
||||
}];
|
||||
}];
|
||||
description = "NixOS configuration";
|
||||
};
|
||||
_pkgs = lib.mkOption {
|
||||
internal = true;
|
||||
readOnly = true;
|
||||
type = lib.types.functionTo (lib.types.listOf lib.types.package);
|
||||
default = pkgs: [ pkgs.gnugrep pkgs.util-linux ];
|
||||
description = "Packages";
|
||||
};
|
||||
};
|
||||
}
|
63
types/table.nix
Normal file
63
types/table.nix
Normal file
|
@ -0,0 +1,63 @@
|
|||
{ config, options, lib, diskoLib, subTypes, ... }:
|
||||
{
|
||||
options = {
|
||||
type = lib.mkOption {
|
||||
type = lib.types.enum [ "table" ];
|
||||
internal = true;
|
||||
description = "Partition table";
|
||||
};
|
||||
format = lib.mkOption {
|
||||
type = lib.types.enum [ "gpt" "msdos" ];
|
||||
default = "gpt";
|
||||
description = "The kind of partition table";
|
||||
};
|
||||
partitions = lib.mkOption {
|
||||
type = lib.types.listOf subTypes.partition;
|
||||
default = [ ];
|
||||
description = "List of partitions to add to the partition table";
|
||||
};
|
||||
_meta = lib.mkOption {
|
||||
internal = true;
|
||||
readOnly = true;
|
||||
type = lib.types.functionTo diskoLib.jsonType;
|
||||
default = dev:
|
||||
diskoLib.deepMergeMap (partition: partition._meta dev) config.partitions;
|
||||
description = "Metadata";
|
||||
};
|
||||
_create = diskoLib.mkCreateOption {
|
||||
inherit config options;
|
||||
default = { dev }: ''
|
||||
parted -s ${dev} -- mklabel ${config.format}
|
||||
${lib.concatMapStrings (partition: partition._create {inherit dev; type = config.format;} ) config.partitions}
|
||||
'';
|
||||
};
|
||||
_mount = diskoLib.mkMountOption {
|
||||
inherit config options;
|
||||
default = { dev }:
|
||||
let
|
||||
partMounts = diskoLib.deepMergeMap (partition: partition._mount { inherit dev; }) config.partitions;
|
||||
in
|
||||
{
|
||||
dev = ''
|
||||
${lib.concatMapStrings (x: x.dev or "") (lib.attrValues partMounts)}
|
||||
'';
|
||||
fs = partMounts.fs or { };
|
||||
};
|
||||
};
|
||||
_config = lib.mkOption {
|
||||
internal = true;
|
||||
readOnly = true;
|
||||
default = dev:
|
||||
map (partition: partition._config dev) config.partitions;
|
||||
description = "NixOS configuration";
|
||||
};
|
||||
_pkgs = lib.mkOption {
|
||||
internal = true;
|
||||
readOnly = true;
|
||||
type = lib.types.functionTo (lib.types.listOf lib.types.package);
|
||||
default = pkgs:
|
||||
[ pkgs.parted pkgs.systemdMinimal ] ++ lib.flatten (map (partition: partition._pkgs pkgs) config.partitions);
|
||||
description = "Packages";
|
||||
};
|
||||
};
|
||||
}
|
47
types/zfs.nix
Normal file
47
types/zfs.nix
Normal file
|
@ -0,0 +1,47 @@
|
|||
{ config, options, lib, diskoLib, ... }:
|
||||
{
|
||||
options = {
|
||||
type = lib.mkOption {
|
||||
type = lib.types.enum [ "zfs" ];
|
||||
internal = true;
|
||||
description = "Type";
|
||||
};
|
||||
pool = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "Name of the ZFS pool";
|
||||
};
|
||||
_meta = lib.mkOption {
|
||||
internal = true;
|
||||
readOnly = true;
|
||||
type = lib.types.functionTo diskoLib.jsonType;
|
||||
default = dev: {
|
||||
deviceDependencies.zpool.${config.pool} = [ dev ];
|
||||
};
|
||||
description = "Metadata";
|
||||
};
|
||||
_create = diskoLib.mkCreateOption {
|
||||
inherit config options;
|
||||
default = { dev }: ''
|
||||
echo "${dev}" >> $disko_devices_dir/zfs_${config.pool}
|
||||
'';
|
||||
};
|
||||
_mount = diskoLib.mkMountOption {
|
||||
inherit config options;
|
||||
default = { dev }:
|
||||
{ };
|
||||
};
|
||||
_config = lib.mkOption {
|
||||
internal = true;
|
||||
readOnly = true;
|
||||
default = dev: [ ];
|
||||
description = "NixOS configuration";
|
||||
};
|
||||
_pkgs = lib.mkOption {
|
||||
internal = true;
|
||||
readOnly = true;
|
||||
type = lib.types.functionTo (lib.types.listOf lib.types.package);
|
||||
default = pkgs: [ pkgs.zfs ];
|
||||
description = "Packages";
|
||||
};
|
||||
};
|
||||
}
|
103
types/zfs_dataset.nix
Normal file
103
types/zfs_dataset.nix
Normal file
|
@ -0,0 +1,103 @@
|
|||
{ config, options, lib, diskoLib, optionTypes, rootMountPoint, ... }:
|
||||
{
|
||||
options = {
|
||||
name = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = config._module.args.name;
|
||||
description = "Name of the dataset";
|
||||
};
|
||||
type = lib.mkOption {
|
||||
type = lib.types.enum [ "zfs_dataset" ];
|
||||
default = "zfs_dataset";
|
||||
internal = true;
|
||||
description = "Type";
|
||||
};
|
||||
zfs_type = lib.mkOption {
|
||||
type = lib.types.enum [ "filesystem" "volume" ];
|
||||
description = "The type of the dataset";
|
||||
};
|
||||
options = lib.mkOption {
|
||||
type = lib.types.attrsOf lib.types.str;
|
||||
default = { };
|
||||
description = "Options to set for the dataset";
|
||||
};
|
||||
mountOptions = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.str;
|
||||
default = [ "defaults" ];
|
||||
description = "Mount options";
|
||||
};
|
||||
|
||||
# filesystem options
|
||||
mountpoint = lib.mkOption {
|
||||
type = lib.types.nullOr optionTypes.absolute-pathname;
|
||||
default = null;
|
||||
description = "Path to mount the dataset to";
|
||||
};
|
||||
|
||||
# volume options
|
||||
size = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.str; # TODO size
|
||||
default = null;
|
||||
description = "Size of the dataset";
|
||||
};
|
||||
|
||||
content = diskoLib.partitionType;
|
||||
_meta = lib.mkOption {
|
||||
internal = true;
|
||||
readOnly = true;
|
||||
type = lib.types.functionTo diskoLib.jsonType;
|
||||
default = dev:
|
||||
lib.optionalAttrs (!isNull config.content) (config.content._meta dev);
|
||||
description = "Metadata";
|
||||
};
|
||||
_create = diskoLib.mkCreateOption {
|
||||
inherit config options;
|
||||
default = { zpool }: ''
|
||||
zfs create ${zpool}/${config.name} \
|
||||
${lib.concatStringsSep " " (lib.mapAttrsToList (n: v: "-o ${n}=${v}") config.options)} \
|
||||
${lib.optionalString (config.zfs_type == "volume") "-V ${config.size}"}
|
||||
${lib.optionalString (config.zfs_type == "volume") ''
|
||||
udevadm trigger --subsystem-match=block; udevadm settle
|
||||
${lib.optionalString (!isNull config.content) (config.content._create {dev = "/dev/zvol/${zpool}/${config.name}";})}
|
||||
''}
|
||||
'';
|
||||
};
|
||||
_mount = diskoLib.mkMountOption {
|
||||
inherit config options;
|
||||
default = { zpool }:
|
||||
lib.optionalAttrs (config.zfs_type == "volume" && !isNull config.content) (config.content._mount { dev = "/dev/zvol/${zpool}/${config.name}"; }) //
|
||||
lib.optionalAttrs (config.zfs_type == "filesystem" && config.options.mountpoint or "" != "none") {
|
||||
fs.${config.mountpoint} = ''
|
||||
if ! findmnt ${zpool}/${config.name} "${rootMountPoint}${config.mountpoint}" > /dev/null 2>&1; then
|
||||
mount ${zpool}/${config.name} "${rootMountPoint}${config.mountpoint}" \
|
||||
-o X-mount.mkdir \
|
||||
${lib.concatMapStringsSep " " (opt: "-o ${opt}") config.mountOptions} \
|
||||
${lib.optionalString ((config.options.mountpoint or "") != "legacy") "-o zfsutil"} \
|
||||
-t zfs
|
||||
fi
|
||||
'';
|
||||
};
|
||||
};
|
||||
_config = lib.mkOption {
|
||||
internal = true;
|
||||
readOnly = true;
|
||||
default = zpool:
|
||||
(lib.optional (config.zfs_type == "volume" && !isNull config.content) (config.content._config "/dev/zvol/${zpool}/${config.name}")) ++
|
||||
(lib.optional (config.zfs_type == "filesystem" && config.options.mountpoint or "" != "none") {
|
||||
fileSystems.${config.mountpoint} = {
|
||||
device = "${zpool}/${config.name}";
|
||||
fsType = "zfs";
|
||||
options = config.mountOptions ++ lib.optional ((config.options.mountpoint or "") != "legacy") "zfsutil";
|
||||
};
|
||||
});
|
||||
description = "NixOS configuration";
|
||||
};
|
||||
_pkgs = lib.mkOption {
|
||||
internal = true;
|
||||
readOnly = true;
|
||||
type = lib.types.functionTo (lib.types.listOf lib.types.package);
|
||||
default = pkgs: [ pkgs.util-linux ] ++ lib.optionals (!isNull config.content) (config.content._pkgs pkgs);
|
||||
description = "Packages";
|
||||
};
|
||||
};
|
||||
}
|
110
types/zpool.nix
Normal file
110
types/zpool.nix
Normal file
|
@ -0,0 +1,110 @@
|
|||
{ config, options, lib, diskoLib, optionTypes, subTypes, rootMountPoint, ... }:
|
||||
{
|
||||
options = {
|
||||
name = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = config._module.args.name;
|
||||
description = "Name of the ZFS pool";
|
||||
};
|
||||
type = lib.mkOption {
|
||||
type = lib.types.enum [ "zpool" ];
|
||||
default = "zpool";
|
||||
internal = true;
|
||||
description = "Type";
|
||||
};
|
||||
mode = lib.mkOption {
|
||||
type = lib.types.str; # TODO zfs modes
|
||||
default = "";
|
||||
description = "Mode of the ZFS pool";
|
||||
};
|
||||
options = lib.mkOption {
|
||||
type = lib.types.attrsOf lib.types.str;
|
||||
default = { };
|
||||
description = "Options for the ZFS pool";
|
||||
};
|
||||
rootFsOptions = lib.mkOption {
|
||||
type = lib.types.attrsOf lib.types.str;
|
||||
default = { };
|
||||
description = "Options for the root filesystem";
|
||||
};
|
||||
mountpoint = lib.mkOption {
|
||||
type = lib.types.nullOr optionTypes.absolute-pathname;
|
||||
default = null;
|
||||
description = "The mountpoint of the pool";
|
||||
};
|
||||
mountOptions = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.str;
|
||||
default = [ "defaults" ];
|
||||
description = "Options to pass to mount";
|
||||
};
|
||||
datasets = lib.mkOption {
|
||||
type = lib.types.attrsOf subTypes.zfs_dataset;
|
||||
description = "List of datasets to define";
|
||||
};
|
||||
_meta = lib.mkOption {
|
||||
internal = true;
|
||||
readOnly = true;
|
||||
type = diskoLib.jsonType;
|
||||
default =
|
||||
diskoLib.deepMergeMap (dataset: dataset._meta [ "zpool" config.name ]) (lib.attrValues config.datasets);
|
||||
description = "Metadata";
|
||||
};
|
||||
_create = diskoLib.mkCreateOption {
|
||||
inherit config options;
|
||||
default = {}: ''
|
||||
zpool create ${config.name} \
|
||||
${config.mode} \
|
||||
${lib.concatStringsSep " " (lib.mapAttrsToList (n: v: "-o ${n}=${v}") config.options)} \
|
||||
${lib.concatStringsSep " " (lib.mapAttrsToList (n: v: "-O ${n}=${v}") config.rootFsOptions)} \
|
||||
$(tr '\n' ' ' < $disko_devices_dir/zfs_${config.name})
|
||||
${lib.concatMapStrings (dataset: dataset._create {zpool = config.name;}) (lib.attrValues config.datasets)}
|
||||
'';
|
||||
};
|
||||
_mount = diskoLib.mkMountOption {
|
||||
inherit config options;
|
||||
default = {}:
|
||||
let
|
||||
datasetMounts = diskoLib.deepMergeMap (dataset: dataset._mount { zpool = config.name; }) (lib.attrValues config.datasets);
|
||||
in
|
||||
{
|
||||
dev = ''
|
||||
zpool list '${config.name}' >/dev/null 2>/dev/null || zpool import '${config.name}'
|
||||
${lib.concatMapStrings (x: x.dev or "") (lib.attrValues datasetMounts)}
|
||||
'';
|
||||
fs = datasetMounts.fs // lib.optionalAttrs (!isNull config.mountpoint) {
|
||||
${config.mountpoint} = ''
|
||||
if ! findmnt ${config.name} "${rootMountPoint}${config.mountpoint}" > /dev/null 2>&1; then
|
||||
mount ${config.name} "${rootMountPoint}${config.mountpoint}" \
|
||||
${lib.optionalString ((config.options.mountpoint or "") != "legacy") "-o zfsutil"} \
|
||||
${lib.concatMapStringsSep " " (opt: "-o ${opt}") config.mountOptions} \
|
||||
-o X-mount.mkdir \
|
||||
-t zfs
|
||||
fi
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
_config = lib.mkOption {
|
||||
internal = true;
|
||||
readOnly = true;
|
||||
default = [
|
||||
(map (dataset: dataset._config config.name) (lib.attrValues config.datasets))
|
||||
(lib.optional (!isNull config.mountpoint) {
|
||||
fileSystems.${config.mountpoint} = {
|
||||
device = config.name;
|
||||
fsType = "zfs";
|
||||
options = config.mountOptions ++ lib.optional ((config.options.mountpoint or "") != "legacy") "zfsutil";
|
||||
};
|
||||
})
|
||||
];
|
||||
description = "NixOS configuration";
|
||||
};
|
||||
_pkgs = lib.mkOption {
|
||||
internal = true;
|
||||
readOnly = true;
|
||||
type = lib.types.functionTo (lib.types.listOf lib.types.package);
|
||||
default = pkgs: [ pkgs.util-linux ] ++ lib.flatten (map (dataset: dataset._pkgs pkgs) (lib.attrValues config.datasets));
|
||||
description = "Packages";
|
||||
};
|
||||
};
|
||||
}
|
Loading…
Reference in a new issue