Merge: zmap: 4.2.0 -> 4.3.1 (#364578)
[NixPkgs.git] / nixos / modules / services / networking / multipath.nix
blob5fef5725f9e00192c5e9c6bc9d334bbf1a39470b
2   config,
3   lib,
4   pkgs,
5   ...
6 }:
7 with lib;
9 # See http://christophe.varoqui.free.fr/usage.html and
10 # https://github.com/opensvc/multipath-tools/blob/master/multipath/multipath.conf.5
12 let
13   cfg = config.services.multipath;
15   indentLines =
16     n: str:
17     concatStringsSep "\n" (
18       map (line: "${fixedWidthString n " " " "}${line}") (filter (x: x != "") (splitString "\n" str))
19     );
21   addCheckDesc =
22     desc: elemType: check:
23     types.addCheck elemType check // { description = "${elemType.description} (with check: ${desc})"; };
24   hexChars = stringToCharacters "0123456789abcdef";
25   isHexString = s: all (c: elem c hexChars) (stringToCharacters (toLower s));
26   hexStr = addCheckDesc "hexadecimal string" types.str isHexString;
31   options.services.multipath = with types; {
33     enable = mkEnableOption "the device mapper multipath (DM-MP) daemon";
35     package = mkPackageOption pkgs "multipath-tools" { };
37     devices = mkOption {
38       default = [ ];
39       example = literalExpression ''
40         [
41           {
42             vendor = "\"COMPELNT\"";
43             product = "\"Compellent Vol\"";
44             path_checker = "tur";
45             no_path_retry = "queue";
46             max_sectors_kb = 256;
47           }, ...
48         ]
49       '';
50       description = ''
51         This option allows you to define arrays for use in multipath
52         groups.
53       '';
54       type = listOf (submodule {
55         options = {
57           vendor = mkOption {
58             type = str;
59             example = "COMPELNT";
60             description = "Regular expression to match the vendor name";
61           };
63           product = mkOption {
64             type = str;
65             example = "Compellent Vol";
66             description = "Regular expression to match the product name";
67           };
69           revision = mkOption {
70             type = nullOr str;
71             default = null;
72             description = "Regular expression to match the product revision";
73           };
75           product_blacklist = mkOption {
76             type = nullOr str;
77             default = null;
78             description = "Products with the given vendor matching this string are blacklisted";
79           };
81           alias_prefix = mkOption {
82             type = nullOr str;
83             default = null;
84             description = "The user_friendly_names prefix to use for this device type, instead of the default mpath";
85           };
87           vpd_vendor = mkOption {
88             type = nullOr str;
89             default = null;
90             description = "The vendor specific vpd page information, using the vpd page abbreviation";
91           };
93           hardware_handler = mkOption {
94             type = nullOr (enum [
95               "emc"
96               "rdac"
97               "hp_sw"
98               "alua"
99               "ana"
100             ]);
101             default = null;
102             description = "The hardware handler to use for this device type";
103           };
105           # Optional arguments
106           path_grouping_policy = mkOption {
107             type = nullOr (enum [
108               "failover"
109               "multibus"
110               "group_by_serial"
111               "group_by_prio"
112               "group_by_node_name"
113             ]);
114             default = null; # real default: "failover"
115             description = "The default path grouping policy to apply to unspecified multipaths";
116           };
118           uid_attribute = mkOption {
119             type = nullOr str;
120             default = null;
121             description = "The udev attribute providing a unique path identifier (WWID)";
122           };
124           getuid_callout = mkOption {
125             type = nullOr str;
126             default = null;
127             description = ''
128               (Superseded by uid_attribute) The default program and args to callout
129               to obtain a unique path identifier. Should be specified with an absolute path.
130             '';
131           };
133           path_selector = mkOption {
134             type = nullOr (enum [
135               ''"round-robin 0"''
136               ''"queue-length 0"''
137               ''"service-time 0"''
138               ''"historical-service-time 0"''
139             ]);
140             default = null; # real default: "service-time 0"
141             description = "The default path selector algorithm to use; they are offered by the kernel multipath target";
142           };
144           path_checker = mkOption {
145             type = enum [
146               "readsector0"
147               "tur"
148               "emc_clariion"
149               "hp_sw"
150               "rdac"
151               "directio"
152               "cciss_tur"
153               "none"
154             ];
155             default = "tur";
156             description = "The default method used to determine the paths state";
157           };
159           prio = mkOption {
160             type = nullOr (enum [
161               "none"
162               "const"
163               "sysfs"
164               "emc"
165               "alua"
166               "ontap"
167               "rdac"
168               "hp_sw"
169               "hds"
170               "random"
171               "weightedpath"
172               "path_latency"
173               "ana"
174               "datacore"
175               "iet"
176             ]);
177             default = null; # real default: "const"
178             description = "The name of the path priority routine";
179           };
181           prio_args = mkOption {
182             type = nullOr str;
183             default = null;
184             description = "Arguments to pass to to the prio function";
185           };
187           features = mkOption {
188             type = nullOr str;
189             default = null;
190             description = "Specify any device-mapper features to be used";
191           };
193           failback = mkOption {
194             type = nullOr str;
195             default = null; # real default: "manual"
196             description = "Tell multipathd how to manage path group failback. Quote integers as strings";
197           };
199           rr_weight = mkOption {
200             type = nullOr (enum [
201               "priorities"
202               "uniform"
203             ]);
204             default = null; # real default: "uniform"
205             description = ''
206               If set to priorities the multipath configurator will assign path weights
207               as "path prio * rr_min_io".
208             '';
209           };
211           no_path_retry = mkOption {
212             type = nullOr str;
213             default = null; # real default: "fail"
214             description = "Specify what to do when all paths are down. Quote integers as strings";
215           };
217           rr_min_io = mkOption {
218             type = nullOr int;
219             default = null; # real default: 1000
220             description = ''
221               Number of I/O requests to route to a path before switching to the next in the
222               same path group. This is only for Block I/O (BIO) based multipath and
223               only apply to round-robin path_selector.
224             '';
225           };
227           rr_min_io_rq = mkOption {
228             type = nullOr int;
229             default = null; # real default: 1
230             description = ''
231               Number of I/O requests to route to a path before switching to the next in the
232               same path group. This is only for Request based multipath and
233               only apply to round-robin path_selector.
234             '';
235           };
237           fast_io_fail_tmo = mkOption {
238             type = nullOr str;
239             default = null; # real default: 5
240             description = ''
241               Specify the number of seconds the SCSI layer will wait after a problem has been
242               detected on a FC remote port before failing I/O to devices on that remote port.
243               This should be smaller than dev_loss_tmo. Setting this to "off" will disable
244               the timeout. Quote integers as strings.
245             '';
246           };
248           dev_loss_tmo = mkOption {
249             type = nullOr str;
250             default = null; # real default: 600
251             description = ''
252               Specify the number of seconds the SCSI layer will wait after a problem has
253               been detected on a FC remote port before removing it from the system. This
254               can be set to "infinity" which sets it to the max value of 2147483647
255               seconds, or 68 years. It will be automatically adjusted to the overall
256               retry interval no_path_retry * polling_interval
257               if a number of retries is given with no_path_retry and the
258               overall retry interval is longer than the specified dev_loss_tmo value.
259               The Linux kernel will cap this value to 600 if fast_io_fail_tmo
260               is not set.
261             '';
262           };
264           flush_on_last_del = mkOption {
265             type = nullOr (enum [
266               "yes"
267               "no"
268             ]);
269             default = null; # real default: "no"
270             description = ''
271               If set to "yes" multipathd will disable queueing when the last path to a
272               device has been deleted.
273             '';
274           };
276           user_friendly_names = mkOption {
277             type = nullOr (enum [
278               "yes"
279               "no"
280             ]);
281             default = null; # real default: "no"
282             description = ''
283               If set to "yes", using the bindings file /etc/multipath/bindings
284               to assign a persistent and unique alias to the multipath, in the
285               form of mpath. If set to "no" use the WWID as the alias. In either
286               case this be will be overridden by any specific aliases in the
287               multipaths section.
288             '';
289           };
291           detect_prio = mkOption {
292             type = nullOr (enum [
293               "yes"
294               "no"
295             ]);
296             default = null; # real default: "yes"
297             description = ''
298               If set to "yes", multipath will try to detect if the device supports
299               SCSI-3 ALUA. If so, the device will automatically use the sysfs
300               prioritizer if the required sysf attributes access_state and
301               preferred_path are supported, or the alua prioritizer if not. If set
302               to "no", the prioritizer will be selected as usual.
303             '';
304           };
306           detect_checker = mkOption {
307             type = nullOr (enum [
308               "yes"
309               "no"
310             ]);
311             default = null; # real default: "yes"
312             description = ''
313               If set to "yes", multipath will try to detect if the device supports
314               SCSI-3 ALUA. If so, the device will automatically use the tur checker.
315               If set to "no", the checker will be selected as usual.
316             '';
317           };
319           deferred_remove = mkOption {
320             type = nullOr (enum [
321               "yes"
322               "no"
323             ]);
324             default = null; # real default: "no"
325             description = ''
326               If set to "yes", multipathd will do a deferred remove instead of a
327               regular remove when the last path device has been deleted. This means
328               that if the multipath device is still in use, it will be freed when
329               the last user closes it. If path is added to the multipath device
330               before the last user closes it, the deferred remove will be canceled.
331             '';
332           };
334           san_path_err_threshold = mkOption {
335             type = nullOr str;
336             default = null;
337             description = ''
338               If set to a value greater than 0, multipathd will watch paths and check
339               how many times a path has been failed due to errors.If the number of
340               failures on a particular path is greater then the san_path_err_threshold,
341               then the path will not reinstate till san_path_err_recovery_time. These
342               path failures should occur within a san_path_err_forget_rate checks, if
343               not we will consider the path is good enough to reinstantate.
344             '';
345           };
347           san_path_err_forget_rate = mkOption {
348             type = nullOr str;
349             default = null;
350             description = ''
351               If set to a value greater than 0, multipathd will check whether the path
352               failures has exceeded the san_path_err_threshold within this many checks
353               i.e san_path_err_forget_rate. If so we will not reinstante the path till
354               san_path_err_recovery_time.
355             '';
356           };
358           san_path_err_recovery_time = mkOption {
359             type = nullOr str;
360             default = null;
361             description = ''
362               If set to a value greater than 0, multipathd will make sure that when
363               path failures has exceeded the san_path_err_threshold within
364               san_path_err_forget_rate then the path will be placed in failed state
365               for san_path_err_recovery_time duration. Once san_path_err_recovery_time
366               has timeout we will reinstante the failed path. san_path_err_recovery_time
367               value should be in secs.
368             '';
369           };
371           marginal_path_err_sample_time = mkOption {
372             type = nullOr int;
373             default = null;
374             description = "One of the four parameters of supporting path check based on accounting IO error such as intermittent error";
375           };
377           marginal_path_err_rate_threshold = mkOption {
378             type = nullOr int;
379             default = null;
380             description = "The error rate threshold as a permillage (1/1000)";
381           };
383           marginal_path_err_recheck_gap_time = mkOption {
384             type = nullOr str;
385             default = null;
386             description = "One of the four parameters of supporting path check based on accounting IO error such as intermittent error";
387           };
389           marginal_path_double_failed_time = mkOption {
390             type = nullOr str;
391             default = null;
392             description = "One of the four parameters of supporting path check based on accounting IO error such as intermittent error";
393           };
395           delay_watch_checks = mkOption {
396             type = nullOr str;
397             default = null;
398             description = "This option is deprecated, and mapped to san_path_err_forget_rate";
399           };
401           delay_wait_checks = mkOption {
402             type = nullOr str;
403             default = null;
404             description = "This option is deprecated, and mapped to san_path_err_recovery_time";
405           };
407           skip_kpartx = mkOption {
408             type = nullOr (enum [
409               "yes"
410               "no"
411             ]);
412             default = null; # real default: "no"
413             description = "If set to yes, kpartx will not automatically create partitions on the device";
414           };
416           max_sectors_kb = mkOption {
417             type = nullOr int;
418             default = null;
419             description = "Sets the max_sectors_kb device parameter on all path devices and the multipath device to the specified value";
420           };
422           ghost_delay = mkOption {
423             type = nullOr int;
424             default = null;
425             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";
426           };
428           all_tg_pt = mkOption {
429             type = nullOr str;
430             default = null;
431             description = "Set the 'all targets ports' flag when registering keys with mpathpersist";
432           };
434         };
435       });
436     };
438     defaults = mkOption {
439       type = nullOr str;
440       default = null;
441       description = ''
442         This section defines default values for attributes which are used
443         whenever no values are given in the appropriate device or multipath
444         sections.
445       '';
446     };
448     blacklist = mkOption {
449       type = nullOr str;
450       default = null;
451       description = ''
452         This section defines which devices should be excluded from the
453         multipath topology discovery.
454       '';
455     };
457     blacklist_exceptions = mkOption {
458       type = nullOr str;
459       default = null;
460       description = ''
461         This section defines which devices should be included in the
462         multipath topology discovery, despite being listed in the
463         blacklist section.
464       '';
465     };
467     overrides = mkOption {
468       type = nullOr str;
469       default = null;
470       description = ''
471         This section defines values for attributes that should override the
472         device-specific settings for all devices.
473       '';
474     };
476     extraConfig = mkOption {
477       type = nullOr str;
478       default = null;
479       description = "Lines to append to default multipath.conf";
480     };
482     extraConfigFile = mkOption {
483       type = nullOr str;
484       default = null;
485       description = "Append an additional file's contents to /etc/multipath.conf";
486     };
488     pathGroups = mkOption {
489       example = literalExpression ''
490         [
491           {
492             wwid = "360080e500043b35c0123456789abcdef";
493             alias = 10001234;
494             array = "bigarray.example.com";
495             fsType = "zfs"; # optional
496             options = "ro"; # optional
497           }, ...
498         ]
499       '';
500       description = ''
501         This option allows you to define multipath groups as described
502         in http://christophe.varoqui.free.fr/usage.html.
503       '';
504       type = listOf (submodule {
505         options = {
507           alias = mkOption {
508             type = int;
509             example = 1001234;
510             description = "The name of the multipath device";
511           };
513           wwid = mkOption {
514             type = hexStr;
515             example = "360080e500043b35c0123456789abcdef";
516             description = "The identifier for the multipath device";
517           };
519           array = mkOption {
520             type = str;
521             default = null;
522             example = "bigarray.example.com";
523             description = "The DNS name of the storage array";
524           };
526           fsType = mkOption {
527             type = nullOr str;
528             default = null;
529             example = "zfs";
530             description = "Type of the filesystem";
531           };
533           options = mkOption {
534             type = nullOr str;
535             default = null;
536             example = "ro";
537             description = "Options used to mount the file system";
538           };
540         };
541       });
542     };
544   };
546   config = mkIf cfg.enable {
547     environment.etc."multipath.conf".text =
548       let
549         inherit (cfg)
550           defaults
551           blacklist
552           blacklist_exceptions
553           overrides
554           ;
556         mkDeviceBlock =
557           cfg:
558           let
559             nonNullCfg = lib.filterAttrs (k: v: v != null) cfg;
560             attrs = lib.mapAttrsToList (name: value: "  ${name} ${toString value}") nonNullCfg;
561           in
562           ''
563             device {
564             ${lib.concatStringsSep "\n" attrs}
565             }
566           '';
567         devices = lib.concatMapStringsSep "\n" mkDeviceBlock cfg.devices;
569         mkMultipathBlock = m: ''
570           multipath {
571             wwid ${m.wwid}
572             alias ${toString m.alias}
573           }
574         '';
575         multipaths = lib.concatMapStringsSep "\n" mkMultipathBlock cfg.pathGroups;
577       in
578       ''
579         devices {
580         ${indentLines 2 devices}
581         }
583         ${optionalString (defaults != null) ''
584           defaults {
585           ${indentLines 2 defaults}
586           }
587         ''}
588         ${optionalString (blacklist != null) ''
589           blacklist {
590           ${indentLines 2 blacklist}
591           }
592         ''}
593         ${optionalString (blacklist_exceptions != null) ''
594           blacklist_exceptions {
595           ${indentLines 2 blacklist_exceptions}
596           }
597         ''}
598         ${optionalString (overrides != null) ''
599           overrides {
600           ${indentLines 2 overrides}
601           }
602         ''}
603         multipaths {
604         ${indentLines 2 multipaths}
605         }
606       '';
608     systemd.packages = [ cfg.package ];
610     environment.systemPackages = [ cfg.package ];
611     boot.kernelModules = [
612       "dm-multipath"
613       "dm-service-time"
614     ];
616     # We do not have systemd in stage-1 boot so must invoke `multipathd`
617     # with the `-1` argument which disables systemd calls. Invoke `multipath`
618     # to display the multipath mappings in the output of `journalctl -b`.
619     # TODO: Implement for systemd stage 1
620     boot.initrd.kernelModules = [
621       "dm-multipath"
622       "dm-service-time"
623     ];
624     boot.initrd.postDeviceCommands = mkIf (!config.boot.initrd.systemd.enable) ''
625       modprobe -a dm-multipath dm-service-time
626       multipathd -s
627       (set -x && sleep 1 && multipath -ll)
628     '';
629   };