9 # See http://christophe.varoqui.free.fr/usage.html and
10 # https://github.com/opensvc/multipath-tools/blob/master/multipath/multipath.conf.5
13 cfg = config.services.multipath;
17 concatStringsSep "\n" (
18 map (line: "${fixedWidthString n " " " "}${line}") (filter (x: x != "") (splitString "\n" str))
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" { };
39 example = literalExpression ''
42 vendor = "\"COMPELNT\"";
43 product = "\"Compellent Vol\"";
45 no_path_retry = "queue";
51 This option allows you to define arrays for use in multipath
54 type = listOf (submodule {
60 description = "Regular expression to match the vendor name";
65 example = "Compellent Vol";
66 description = "Regular expression to match the product name";
72 description = "Regular expression to match the product revision";
75 product_blacklist = mkOption {
78 description = "Products with the given vendor matching this string are blacklisted";
81 alias_prefix = mkOption {
84 description = "The user_friendly_names prefix to use for this device type, instead of the default mpath";
87 vpd_vendor = mkOption {
90 description = "The vendor specific vpd page information, using the vpd page abbreviation";
93 hardware_handler = mkOption {
102 description = "The hardware handler to use for this device type";
106 path_grouping_policy = mkOption {
107 type = nullOr (enum [
114 default = null; # real default: "failover"
115 description = "The default path grouping policy to apply to unspecified multipaths";
118 uid_attribute = mkOption {
121 description = "The udev attribute providing a unique path identifier (WWID)";
124 getuid_callout = mkOption {
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.
133 path_selector = mkOption {
134 type = nullOr (enum [
138 ''"historical-service-time 0"''
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";
144 path_checker = mkOption {
156 description = "The default method used to determine the paths state";
160 type = nullOr (enum [
177 default = null; # real default: "const"
178 description = "The name of the path priority routine";
181 prio_args = mkOption {
184 description = "Arguments to pass to to the prio function";
187 features = mkOption {
190 description = "Specify any device-mapper features to be used";
193 failback = mkOption {
195 default = null; # real default: "manual"
196 description = "Tell multipathd how to manage path group failback. Quote integers as strings";
199 rr_weight = mkOption {
200 type = nullOr (enum [
204 default = null; # real default: "uniform"
206 If set to priorities the multipath configurator will assign path weights
207 as "path prio * rr_min_io".
211 no_path_retry = mkOption {
213 default = null; # real default: "fail"
214 description = "Specify what to do when all paths are down. Quote integers as strings";
217 rr_min_io = mkOption {
219 default = null; # real default: 1000
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.
227 rr_min_io_rq = mkOption {
229 default = null; # real default: 1
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.
237 fast_io_fail_tmo = mkOption {
239 default = null; # real default: 5
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.
248 dev_loss_tmo = mkOption {
250 default = null; # real default: 600
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
264 flush_on_last_del = mkOption {
265 type = nullOr (enum [
269 default = null; # real default: "no"
271 If set to "yes" multipathd will disable queueing when the last path to a
272 device has been deleted.
276 user_friendly_names = mkOption {
277 type = nullOr (enum [
281 default = null; # real default: "no"
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
291 detect_prio = mkOption {
292 type = nullOr (enum [
296 default = null; # real default: "yes"
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.
306 detect_checker = mkOption {
307 type = nullOr (enum [
311 default = null; # real default: "yes"
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.
319 deferred_remove = mkOption {
320 type = nullOr (enum [
324 default = null; # real default: "no"
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.
334 san_path_err_threshold = mkOption {
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.
347 san_path_err_forget_rate = mkOption {
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.
358 san_path_err_recovery_time = mkOption {
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.
371 marginal_path_err_sample_time = mkOption {
374 description = "One of the four parameters of supporting path check based on accounting IO error such as intermittent error";
377 marginal_path_err_rate_threshold = mkOption {
380 description = "The error rate threshold as a permillage (1/1000)";
383 marginal_path_err_recheck_gap_time = mkOption {
386 description = "One of the four parameters of supporting path check based on accounting IO error such as intermittent error";
389 marginal_path_double_failed_time = mkOption {
392 description = "One of the four parameters of supporting path check based on accounting IO error such as intermittent error";
395 delay_watch_checks = mkOption {
398 description = "This option is deprecated, and mapped to san_path_err_forget_rate";
401 delay_wait_checks = mkOption {
404 description = "This option is deprecated, and mapped to san_path_err_recovery_time";
407 skip_kpartx = mkOption {
408 type = nullOr (enum [
412 default = null; # real default: "no"
413 description = "If set to yes, kpartx will not automatically create partitions on the device";
416 max_sectors_kb = mkOption {
419 description = "Sets the max_sectors_kb device parameter on all path devices and the multipath device to the specified value";
422 ghost_delay = mkOption {
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";
428 all_tg_pt = mkOption {
431 description = "Set the 'all targets ports' flag when registering keys with mpathpersist";
438 defaults = mkOption {
442 This section defines default values for attributes which are used
443 whenever no values are given in the appropriate device or multipath
448 blacklist = mkOption {
452 This section defines which devices should be excluded from the
453 multipath topology discovery.
457 blacklist_exceptions = mkOption {
461 This section defines which devices should be included in the
462 multipath topology discovery, despite being listed in the
467 overrides = mkOption {
471 This section defines values for attributes that should override the
472 device-specific settings for all devices.
476 extraConfig = mkOption {
479 description = "Lines to append to default multipath.conf";
482 extraConfigFile = mkOption {
485 description = "Append an additional file's contents to /etc/multipath.conf";
488 pathGroups = mkOption {
489 example = literalExpression ''
492 wwid = "360080e500043b35c0123456789abcdef";
494 array = "bigarray.example.com";
495 fsType = "zfs"; # optional
496 options = "ro"; # optional
501 This option allows you to define multipath groups as described
502 in http://christophe.varoqui.free.fr/usage.html.
504 type = listOf (submodule {
510 description = "The name of the multipath device";
515 example = "360080e500043b35c0123456789abcdef";
516 description = "The identifier for the multipath device";
522 example = "bigarray.example.com";
523 description = "The DNS name of the storage array";
530 description = "Type of the filesystem";
537 description = "Options used to mount the file system";
546 config = mkIf cfg.enable {
547 environment.etc."multipath.conf".text =
559 nonNullCfg = lib.filterAttrs (k: v: v != null) cfg;
560 attrs = lib.mapAttrsToList (name: value: " ${name} ${toString value}") nonNullCfg;
564 ${lib.concatStringsSep "\n" attrs}
567 devices = lib.concatMapStringsSep "\n" mkDeviceBlock cfg.devices;
569 mkMultipathBlock = m: ''
572 alias ${toString m.alias}
575 multipaths = lib.concatMapStringsSep "\n" mkMultipathBlock cfg.pathGroups;
580 ${indentLines 2 devices}
583 ${optionalString (defaults != null) ''
585 ${indentLines 2 defaults}
588 ${optionalString (blacklist != null) ''
590 ${indentLines 2 blacklist}
593 ${optionalString (blacklist_exceptions != null) ''
594 blacklist_exceptions {
595 ${indentLines 2 blacklist_exceptions}
598 ${optionalString (overrides != null) ''
600 ${indentLines 2 overrides}
604 ${indentLines 2 multipaths}
608 systemd.packages = [ cfg.package ];
610 environment.systemPackages = [ cfg.package ];
611 boot.kernelModules = [
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 = [
624 boot.initrd.postDeviceCommands = mkIf (!config.boot.initrd.systemd.enable) ''
625 modprobe -a dm-multipath dm-service-time
627 (set -x && sleep 1 && multipath -ll)