blob: 8f69feed9032086b22596bd8b77236ac0c64dea6 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
|
{ config, pkgs, lib, ... }:
let
inherit (builtins)
attrNames
;
inherit (lib)
concatStringsSep filterAttrs foldl genAttrs mapAttrs' mapAttrsToList
mkOption nameValuePair removePrefix replaceStrings
;
inherit (lib.types)
attrsOf enum ints nonEmptyListOf path submodule
;
groups = filterAttrs (n: _: n != "_module") config.nixsap.system.lvm.raid0;
createLV = vg: lv: s: opts:
let
new = toString s;
stripes = toString opts.stripes;
sizeSpec = if opts.units == "%"
then "--extents ${new}%VG"
else "--size ${new}${opts.units}";
scale = {
"%" = "* 100 / $(vgs --unit b --noheadings --nosuffix --options vg_size ${vg})";
"M" = "/ ${toString (1000 * 1000)}";
"m" = "/ ${toString (1024 * 1024)}";
"G" = "/ ${toString (1000 * 1000 * 1000)}";
"g" = "/ ${toString (1024 * 1024 * 1024)}";
"T" = "/ ${toString (1000 * 1000 * 1000 * 1000)}";
"t" = "/ ${toString (1024 * 1024 * 1024 * 1024)}";
};
in pkgs.writeBashScript "raid0-create-${vg}-${lv}" ''
set -eu
device=/dev/${vg}/${lv}
lv_size=$(lvs --unit b --noheadings --nosuffix --options lv_size "$device" || echo 0)
old=$(( lv_size ${scale."${opts.units}"} ))
if (( ${new} == old )) ; then
exit 0
elif (( old == 0 )); then
lvcreate ${vg} --name ${lv} ${sizeSpec} --stripes ${stripes}
elif (( ${new} < old )) ; then
echo "Cannot shrink volume $device from $old ${opts.units} to ${new} ${opts.units}" >&2
exit 1
else
lvextend "$device" ${sizeSpec}
resize2fs "$device"
fi
'';
createVG = vg: pv: pkgs.writeBashScript "raid0-create-vg-${vg}" ''
set -eu
for pv in ${toString pv}; do
type=$(blkid -p -s TYPE -o value "$pv" || true)
if [ "$type" != LVM2_member ]; then
pvcreate "$pv"
if ! vgs ${vg}; then
vgcreate ${vg} "$pv"
else
vgextend ${vg} "$pv"
fi
fi
done
'';
mkRaidService = vg: opts:
let
ExecStart = pkgs.writeBashScript "raid0-${vg}" ''
set -eu
${createVG vg opts.physical}
${concatStringsSep "\n" (
mapAttrsToList (v: s:
"${createLV vg (baseNameOf v) s opts}")
opts.fileSystems
)}
vgchange -ay ${vg}
udevadm trigger --action=add
'';
in nameValuePair "raid0-${vg}" rec {
wantedBy = map (v: "dev-${vg}-${baseNameOf v}.device") (attrNames opts.fileSystems);
requires = map (pv: replaceStrings ["/"] ["-"] (removePrefix "/" pv) + ".device") opts.physical;
after = requires;
before = wantedBy;
unitConfig.DefaultDependencies = false;
path = with pkgs; [ utillinux lvm2 e2fsprogs ];
serviceConfig = {
inherit ExecStart;
RemainAfterExit = true;
Type = "oneshot";
};
};
in {
options.nixsap.system = {
lvm.raid0 = mkOption {
description = "Set of LVM2 volume groups";
default = {};
type = attrsOf (submodule {
options = {
stripes = mkOption {
description = "Number of stripes";
type = ints.positive;
example = 2;
};
physical = mkOption {
description = "List of physical devices (must be even for stripes)";
example = [ "/dev/sdb" "/dev/sdc" ];
type = nonEmptyListOf path;
};
fileSystems = mkOption {
description = "Filesystems and their sizes";
type = attrsOf ints.positive;
example = { "/mariadb/db" = 100; };
};
units = mkOption {
description = "Units of size";
type = enum [ "%" "m" "g" "t" "M" "G" "T"];
};
};
});
};
};
config = {
systemd.services = mapAttrs' mkRaidService groups;
fileSystems = foldl (a: b: a//b) {} (
mapAttrsToList (vg: opts: genAttrs (attrNames opts.fileSystems)
(fs: {
fsType = "ext4";
autoFormat = true;
device = "/dev/${vg}/${baseNameOf fs}";
})
) groups
);
};
}
|