···
1
+
{ config, lib, pkgs, ... }: with lib;
3
+
# See http://christophe.varoqui.free.fr/usage.html and
4
+
# https://github.com/opensvc/multipath-tools/blob/master/multipath/multipath.conf.5
7
+
cfg = config.services.multipath;
9
+
indentLines = n: str: concatStringsSep "\n" (
10
+
map (line: "${fixedWidthString n " " " "}${line}") (
11
+
filter ( x: x != "" ) ( splitString "\n" str )
15
+
addCheckDesc = desc: elemType: check: types.addCheck elemType check
16
+
// { description = "${elemType.description} (with check: ${desc})"; };
17
+
hexChars = stringToCharacters "0123456789abcdef";
18
+
isHexString = s: all (c: elem c hexChars) (stringToCharacters (toLower s));
19
+
hexStr = addCheckDesc "hexadecimal string" types.str isHexString;
23
+
options.services.multipath = with types; {
25
+
enable = mkEnableOption "the device mapper multipath (DM-MP) daemon";
27
+
package = mkOption {
29
+
description = "multipath-tools package to use";
30
+
default = pkgs.multipath-tools;
31
+
defaultText = "pkgs.multipath-tools";
34
+
devices = mkOption {
36
+
example = literalExpression ''
39
+
vendor = "\"COMPELNT\"";
40
+
product = "\"Compellent Vol\"";
41
+
path_checker = "tur";
42
+
no_path_retry = "queue";
43
+
max_sectors_kb = 256;
48
+
This option allows you to define arrays for use in multipath
51
+
type = listOf (submodule {
56
+
example = "COMPELNT";
57
+
description = "Regular expression to match the vendor name";
60
+
product = mkOption {
62
+
example = "Compellent Vol";
63
+
description = "Regular expression to match the product name";
66
+
revision = mkOption {
69
+
description = "Regular expression to match the product revision";
72
+
product_blacklist = mkOption {
75
+
description = "Products with the given vendor matching this string are blacklisted";
78
+
alias_prefix = mkOption {
81
+
description = "The user_friendly_names prefix to use for this device type, instead of the default mpath";
84
+
vpd_vendor = mkOption {
87
+
description = "The vendor specific vpd page information, using the vpd page abbreviation";
90
+
hardware_handler = mkOption {
91
+
type = nullOr (enum [ "emc" "rdac" "hp_sw" "alua" "ana" ]);
93
+
description = "The hardware handler to use for this device type";
96
+
# Optional arguments
97
+
path_grouping_policy = mkOption {
98
+
type = nullOr (enum [ "failover" "multibus" "group_by_serial" "group_by_prio" "group_by_node_name" ]);
99
+
default = null; # real default: "failover"
100
+
description = "The default path grouping policy to apply to unspecified multipaths";
103
+
uid_attribute = mkOption {
106
+
description = "The udev attribute providing a unique path identifier (WWID)";
109
+
getuid_callout = mkOption {
113
+
(Superseded by uid_attribute) The default program and args to callout
114
+
to obtain a unique path identifier. Should be specified with an absolute path.
118
+
path_selector = mkOption {
119
+
type = nullOr (enum [
120
+
''"round-robin 0"''
121
+
''"queue-length 0"''
122
+
''"service-time 0"''
123
+
''"historical-service-time 0"''
125
+
default = null; # real default: "service-time 0"
126
+
description = "The default path selector algorithm to use; they are offered by the kernel multipath target";
129
+
path_checker = mkOption {
130
+
type = enum [ "readsector0" "tur" "emc_clariion" "hp_sw" "rdac" "directio" "cciss_tur" "none" ];
132
+
description = "The default method used to determine the paths state";
136
+
type = nullOr (enum [
137
+
"none" "const" "sysfs" "emc" "alua" "ontap" "rdac" "hp_sw" "hds"
138
+
"random" "weightedpath" "path_latency" "ana" "datacore" "iet"
140
+
default = null; # real default: "const"
141
+
description = "The name of the path priority routine";
144
+
prio_args = mkOption {
147
+
description = "Arguments to pass to to the prio function";
150
+
features = mkOption {
153
+
description = "Specify any device-mapper features to be used";
156
+
failback = mkOption {
158
+
default = null; # real default: "manual"
159
+
description = "Tell multipathd how to manage path group failback. Quote integers as strings";
162
+
rr_weight = mkOption {
163
+
type = nullOr (enum [ "priorities" "uniform" ]);
164
+
default = null; # real default: "uniform"
166
+
If set to priorities the multipath configurator will assign path weights
167
+
as "path prio * rr_min_io".
171
+
no_path_retry = mkOption {
173
+
default = null; # real default: "fail"
174
+
description = "Specify what to do when all paths are down. Quote integers as strings";
177
+
rr_min_io = mkOption {
179
+
default = null; # real default: 1000
181
+
Number of I/O requests to route to a path before switching to the next in the
182
+
same path group. This is only for Block I/O (BIO) based multipath and
183
+
only apply to round-robin path_selector.
187
+
rr_min_io_rq = mkOption {
189
+
default = null; # real default: 1
191
+
Number of I/O requests to route to a path before switching to the next in the
192
+
same path group. This is only for Request based multipath and
193
+
only apply to round-robin path_selector.
197
+
fast_io_fail_tmo = mkOption {
199
+
default = null; # real default: 5
201
+
Specify the number of seconds the SCSI layer will wait after a problem has been
202
+
detected on a FC remote port before failing I/O to devices on that remote port.
203
+
This should be smaller than dev_loss_tmo. Setting this to "off" will disable
204
+
the timeout. Quote integers as strings.
208
+
dev_loss_tmo = mkOption {
210
+
default = null; # real default: 600
212
+
Specify the number of seconds the SCSI layer will wait after a problem has
213
+
been detected on a FC remote port before removing it from the system. This
214
+
can be set to "infinity" which sets it to the max value of 2147483647
215
+
seconds, or 68 years. It will be automatically adjusted to the overall
216
+
retry interval no_path_retry * polling_interval
217
+
if a number of retries is given with no_path_retry and the
218
+
overall retry interval is longer than the specified dev_loss_tmo value.
219
+
The Linux kernel will cap this value to 600 if fast_io_fail_tmo
224
+
flush_on_last_del = mkOption {
225
+
type = nullOr (enum [ "yes" "no" ]);
226
+
default = null; # real default: "no"
228
+
If set to "yes" multipathd will disable queueing when the last path to a
229
+
device has been deleted.
233
+
user_friendly_names = mkOption {
234
+
type = nullOr (enum [ "yes" "no" ]);
235
+
default = null; # real default: "no"
237
+
If set to "yes", using the bindings file /etc/multipath/bindings
238
+
to assign a persistent and unique alias to the multipath, in the
239
+
form of mpath. If set to "no" use the WWID as the alias. In either
240
+
case this be will be overridden by any specific aliases in the
241
+
multipaths section.
245
+
retain_attached_hw_handler = mkOption {
246
+
type = nullOr (enum [ "yes" "no" ]);
247
+
default = null; # real default: "yes"
249
+
(Obsolete for kernels >= 4.3) If set to "yes" and the SCSI layer has
250
+
already attached a hardware_handler to the device, multipath will not
251
+
force the device to use the hardware_handler specified by mutipath.conf.
252
+
If the SCSI layer has not attached a hardware handler, multipath will
253
+
continue to use its configured hardware handler.
255
+
Important Note: Linux kernel 4.3 or newer always behaves as if
256
+
"retain_attached_hw_handler yes" was set.
260
+
detect_prio = mkOption {
261
+
type = nullOr (enum [ "yes" "no" ]);
262
+
default = null; # real default: "yes"
264
+
If set to "yes", multipath will try to detect if the device supports
265
+
SCSI-3 ALUA. If so, the device will automatically use the sysfs
266
+
prioritizer if the required sysf attributes access_state and
267
+
preferred_path are supported, or the alua prioritizer if not. If set
268
+
to "no", the prioritizer will be selected as usual.
272
+
detect_checker = mkOption {
273
+
type = nullOr (enum [ "yes" "no" ]);
274
+
default = null; # real default: "yes"
276
+
If set to "yes", multipath will try to detect if the device supports
277
+
SCSI-3 ALUA. If so, the device will automatically use the tur checker.
278
+
If set to "no", the checker will be selected as usual.
282
+
deferred_remove = mkOption {
283
+
type = nullOr (enum [ "yes" "no" ]);
284
+
default = null; # real default: "no"
286
+
If set to "yes", multipathd will do a deferred remove instead of a
287
+
regular remove when the last path device has been deleted. This means
288
+
that if the multipath device is still in use, it will be freed when
289
+
the last user closes it. If path is added to the multipath device
290
+
before the last user closes it, the deferred remove will be canceled.
294
+
san_path_err_threshold = mkOption {
298
+
If set to a value greater than 0, multipathd will watch paths and check
299
+
how many times a path has been failed due to errors.If the number of
300
+
failures on a particular path is greater then the san_path_err_threshold,
301
+
then the path will not reinstate till san_path_err_recovery_time. These
302
+
path failures should occur within a san_path_err_forget_rate checks, if
303
+
not we will consider the path is good enough to reinstantate.
307
+
san_path_err_forget_rate = mkOption {
311
+
If set to a value greater than 0, multipathd will check whether the path
312
+
failures has exceeded the san_path_err_threshold within this many checks
313
+
i.e san_path_err_forget_rate. If so we will not reinstante the path till
314
+
san_path_err_recovery_time.
318
+
san_path_err_recovery_time = mkOption {
322
+
If set to a value greater than 0, multipathd will make sure that when
323
+
path failures has exceeded the san_path_err_threshold within
324
+
san_path_err_forget_rate then the path will be placed in failed state
325
+
for san_path_err_recovery_time duration. Once san_path_err_recovery_time
326
+
has timeout we will reinstante the failed path. san_path_err_recovery_time
327
+
value should be in secs.
331
+
marginal_path_err_sample_time = mkOption {
334
+
description = "One of the four parameters of supporting path check based on accounting IO error such as intermittent error";
337
+
marginal_path_err_rate_threshold = mkOption {
340
+
description = "The error rate threshold as a permillage (1/1000)";
343
+
marginal_path_err_recheck_gap_time = mkOption {
346
+
description = "One of the four parameters of supporting path check based on accounting IO error such as intermittent error";
349
+
marginal_path_double_failed_time = mkOption {
352
+
description = "One of the four parameters of supporting path check based on accounting IO error such as intermittent error";
355
+
delay_watch_checks = mkOption {
358
+
description = "This option is deprecated, and mapped to san_path_err_forget_rate";
361
+
delay_wait_checks = mkOption {
364
+
description = "This option is deprecated, and mapped to san_path_err_recovery_time";
367
+
skip_kpartx = mkOption {
368
+
type = nullOr (enum [ "yes" "no" ]);
369
+
default = null; # real default: "no"
370
+
description = "If set to yes, kpartx will not automatically create partitions on the device";
373
+
max_sectors_kb = mkOption {
376
+
description = "Sets the max_sectors_kb device parameter on all path devices and the multipath device to the specified value";
379
+
ghost_delay = mkOption {
382
+
description = "Sets the number of seconds that multipath will wait after creating a device with only ghost paths before marking it ready for use in systemd";
385
+
all_tg_pt = mkOption {
388
+
description = "Set the 'all targets ports' flag when registering keys with mpathpersist";
395
+
defaults = mkOption {
399
+
This section defines default values for attributes which are used
400
+
whenever no values are given in the appropriate device or multipath
405
+
blacklist = mkOption {
409
+
This section defines which devices should be excluded from the
410
+
multipath topology discovery.
414
+
blacklist_exceptions = mkOption {
418
+
This section defines which devices should be included in the
419
+
multipath topology discovery, despite being listed in the
424
+
overrides = mkOption {
428
+
This section defines values for attributes that should override the
429
+
device-specific settings for all devices.
433
+
extraConfig = mkOption {
436
+
description = "Lines to append to default multipath.conf";
439
+
extraConfigFile = mkOption {
442
+
description = "Append an additional file's contents to /etc/multipath.conf";
445
+
pathGroups = mkOption {
446
+
example = literalExpression ''
449
+
wwid = "360080e500043b35c0123456789abcdef";
451
+
array = "bigarray.example.com";
452
+
fsType = "zfs"; # optional
453
+
options = "ro"; # optional
458
+
This option allows you to define multipath groups as described
459
+
in http://christophe.varoqui.free.fr/usage.html.
461
+
type = listOf (submodule {
467
+
description = "The name of the multipath device";
472
+
example = "360080e500043b35c0123456789abcdef";
473
+
description = "The identifier for the multipath device";
479
+
example = "bigarray.example.com";
480
+
description = "The DNS name of the storage array";
483
+
fsType = mkOption {
487
+
description = "Type of the filesystem";
490
+
options = mkOption {
494
+
description = "Options used to mount the file system";
503
+
config = mkIf cfg.enable {
504
+
environment.etc."multipath.conf".text =
506
+
inherit (cfg) defaults blacklist blacklist_exceptions overrides;
508
+
mkDeviceBlock = cfg: let
509
+
nonNullCfg = lib.filterAttrs (k: v: v != null) cfg;
510
+
attrs = lib.mapAttrsToList (name: value: " ${name} ${toString value}") nonNullCfg;
513
+
${lib.concatStringsSep "\n" attrs}
516
+
devices = lib.concatMapStringsSep "\n" mkDeviceBlock cfg.devices;
518
+
mkMultipathBlock = m: ''
521
+
alias ${toString m.alias}
524
+
multipaths = lib.concatMapStringsSep "\n" mkMultipathBlock cfg.pathGroups;
528
+
${indentLines 2 devices}
531
+
${optionalString (!isNull defaults) ''
533
+
${indentLines 2 defaults}
534
+
multipath_dir ${cfg.package}/lib/multipath
537
+
${optionalString (!isNull blacklist) ''
539
+
${indentLines 2 blacklist}
542
+
${optionalString (!isNull blacklist_exceptions) ''
543
+
blacklist_exceptions {
544
+
${indentLines 2 blacklist_exceptions}
547
+
${optionalString (!isNull overrides) ''
549
+
${indentLines 2 overrides}
553
+
${indentLines 2 multipaths}
557
+
systemd.packages = [ cfg.package ];
559
+
environment.systemPackages = [ cfg.package ];
560
+
boot.kernelModules = [ "dm-multipath" "dm-service-time" ];
562
+
# We do not have systemd in stage-1 boot so must invoke `multipathd`
563
+
# with the `-1` argument which disables systemd calls. Invoke `multipath`
564
+
# to display the multipath mappings in the output of `journalctl -b`.
565
+
boot.initrd.kernelModules = [ "dm-multipath" "dm-service-time" ];
566
+
boot.initrd.postDeviceCommands = ''
567
+
modprobe -a dm-multipath dm-service-time
569
+
(set -x && sleep 1 && multipath -ll)