diff --git a/nixos/modules/services/monitoring/ups.nix b/nixos/modules/services/monitoring/ups.nix index b86e93a661ca..0d157afd51a0 100644 --- a/nixos/modules/services/monitoring/ups.nix +++ b/nixos/modules/services/monitoring/ups.nix @@ -1,9 +1,6 @@ { config, lib, pkgs, ... }: - # TODO: This is not secure, have a look at the file docs/security.txt inside # the project sources. -with lib; - let cfg = config.power.ups; defaultPort = 3493; @@ -31,24 +28,24 @@ let normalizedValue = lib.mapAttrs (key: val: if lib.isList val - then forEach val (elem: if lib.isList elem then elem else [elem]) + then lib.forEach val (elem: if lib.isList elem then elem else [elem]) else if val == null then [] else [[val]] ) value; - mkValueString = concatMapStringsSep " " (v: - let str = generators.mkValueStringDefault {} v; + mkValueString = lib.concatMapStringsSep " " (v: + let str = lib.generators.mkValueStringDefault {} v; in # Quote the value if it has spaces and isn't already quoted. - if (hasInfix " " str) && !(hasPrefix "\"" str && hasSuffix "\"" str) + if (lib.hasInfix " " str) && !(lib.hasPrefix "\"" str && lib.hasSuffix "\"" str) then "\"${str}\"" else str ); in pkgs.writeText name (lib.generators.toKeyValue { - mkKeyValue = generators.mkKeyValueDefault { inherit mkValueString; } " "; + mkKeyValue = lib.generators.mkKeyValueDefault { inherit mkValueString; } " "; listsAsDuplicateKeys = true; } normalizedValue); @@ -57,7 +54,7 @@ let installSecrets = source: target: secrets: pkgs.writeShellScript "installSecrets.sh" '' install -m0600 -D ${source} "${target}" - ${concatLines (forEach secrets (name: '' + ${lib.concatLines (lib.forEach secrets (name: '' ${pkgs.replace-secret}/bin/replace-secret \ '@${name}@' \ "$CREDENTIALS_DIRECTORY/${name}" \ @@ -71,16 +68,16 @@ let upsdUsers = pkgs.writeText "upsd.users" (let # This looks like INI, but it's not quite because the # 'upsmon' option lacks a '='. See: man upsd.users - userConfig = name: user: concatStringsSep "\n " (concatLists [ + userConfig = name: user: lib.concatStringsSep "\n " (lib.concatLists [ [ "[${name}]" "password = \"@upsdusers_password_${name}@\"" ] - (optional (user.upsmon != null) "upsmon ${user.upsmon}") - (forEach user.actions (action: "actions = ${action}")) - (forEach user.instcmds (instcmd: "instcmds = ${instcmd}")) + (lib.optional (user.upsmon != null) "upsmon ${user.upsmon}") + (lib.forEach user.actions (action: "actions = ${action}")) + (lib.forEach user.instcmds (instcmd: "instcmds = ${instcmd}")) ]); - in concatStringsSep "\n\n" (mapAttrsToList userConfig cfg.users)); + in lib.concatStringsSep "\n\n" (lib.mapAttrsToList userConfig cfg.users)); upsOptions = {name, config, ...}: @@ -88,25 +85,25 @@ let options = { # This can be inferred from the UPS model by looking at # /nix/store/nut/share/driver.list - driver = mkOption { - type = types.str; + driver = lib.mkOption { + type = lib.types.str; description = '' Specify the program to run to talk to this UPS. apcsmart, bestups, and sec are some examples. ''; }; - port = mkOption { - type = types.str; + port = lib.mkOption { + type = lib.types.str; description = '' The serial port to which your UPS is connected. /dev/ttyS0 is usually the first port on Linux boxes, for example. ''; }; - shutdownOrder = mkOption { + shutdownOrder = lib.mkOption { default = 0; - type = types.int; + type = lib.types.int; description = '' When you have multiple UPSes on your system, you usually need to turn them off in a certain order. upsdrvctl shuts down all the @@ -115,9 +112,9 @@ let ''; }; - maxStartDelay = mkOption { + maxStartDelay = lib.mkOption { default = null; - type = types.uniq (types.nullOr types.int); + type = lib.types.uniq (lib.types.nullOr lib.types.int); description = '' This can be set as a global variable above your first UPS definition and it can also be set in a UPS section. This value @@ -127,25 +124,25 @@ let ''; }; - description = mkOption { + description = lib.mkOption { default = ""; - type = types.str; + type = lib.types.str; description = '' Description of the UPS. ''; }; - directives = mkOption { + directives = lib.mkOption { default = []; - type = types.listOf types.str; + type = lib.types.listOf lib.types.str; description = '' List of configuration directives for this UPS. ''; }; - summary = mkOption { + summary = lib.mkOption { default = ""; - type = types.lines; + type = lib.types.lines; description = '' Lines which would be added inside ups.conf for handling this UPS. ''; @@ -154,33 +151,33 @@ let }; config = { - directives = mkOrder 10 ([ + directives = lib.mkOrder 10 ([ "driver = ${config.driver}" "port = ${config.port}" ''desc = "${config.description}"'' "sdorder = ${toString config.shutdownOrder}" - ] ++ (optional (config.maxStartDelay != null) + ] ++ (lib.optional (config.maxStartDelay != null) "maxstartdelay = ${toString config.maxStartDelay}") ); summary = - concatStringsSep "\n " + lib.concatStringsSep "\n " (["[${name}]"] ++ config.directives); }; }; listenOptions = { options = { - address = mkOption { - type = types.str; + address = lib.mkOption { + type = lib.types.str; description = '' Address of the interface for `upsd` to listen on. See `man upsd.conf` for details. ''; }; - port = mkOption { - type = types.port; + port = lib.mkOption { + type = lib.types.port; default = defaultPort; description = '' TCP port for `upsd` to listen on. @@ -192,14 +189,14 @@ let upsdOptions = { options = { - enable = mkOption { - type = types.bool; - defaultText = literalMD "`true` if `mode` is one of `standalone`, `netserver`"; + enable = lib.mkOption { + type = lib.types.bool; + defaultText = lib.literalMD "`true` if `mode` is one of `standalone`, `netserver`"; description = "Whether to enable `upsd`."; }; - listen = mkOption { - type = with types; listOf (submodule listenOptions); + listen = lib.mkOption { + type = with lib.types; listOf (submodule listenOptions); default = []; example = [ { @@ -216,8 +213,8 @@ let ''; }; - extraConfig = mkOption { - type = types.lines; + extraConfig = lib.mkOption { + type = lib.types.lines; default = ""; description = '' Additional lines to add to `upsd.conf`. @@ -226,15 +223,15 @@ let }; config = { - enable = mkDefault (elem cfg.mode [ "standalone" "netserver" ]); + enable = lib.mkDefault (lib.elem cfg.mode [ "standalone" "netserver" ]); }; }; monitorOptions = { name, config, ... }: { options = { - system = mkOption { - type = types.str; + system = lib.mkOption { + type = lib.types.str; default = name; description = '' Identifier of the UPS to monitor, in this form: `[@[:]]` @@ -242,8 +239,8 @@ let ''; }; - powerValue = mkOption { - type = types.int; + powerValue = lib.mkOption { + type = lib.types.int; default = 1; description = '' Number of power supplies that the UPS feeds on this system. @@ -251,17 +248,17 @@ let ''; }; - user = mkOption { - type = types.str; + user = lib.mkOption { + type = lib.types.str; description = '' Username from `upsd.users` for accessing this UPS. See `upsmon.conf` for details. ''; }; - passwordFile = mkOption { - type = types.str; - defaultText = literalMD "power.ups.users.\${user}.passwordFile"; + passwordFile = lib.mkOption { + type = lib.types.str; + defaultText = lib.literalMD "power.ups.users.\${user}.passwordFile"; description = '' The full path to a file containing the password from `upsd.users` for accessing this UPS. The password file @@ -270,8 +267,8 @@ let ''; }; - type = mkOption { - type = types.str; + type = lib.mkOption { + type = lib.types.str; default = "master"; description = '' The relationship with `upsd`. @@ -281,30 +278,30 @@ let }; config = { - passwordFile = mkDefault cfg.users.${config.user}.passwordFile; + passwordFile = lib.mkDefault cfg.users.${config.user}.passwordFile; }; }; upsmonOptions = { options = { - enable = mkOption { - type = types.bool; - defaultText = literalMD "`true` if `mode` is one of `standalone`, `netserver`, `netclient`"; + enable = lib.mkOption { + type = lib.types.bool; + defaultText = lib.literalMD "`true` if `mode` is one of `standalone`, `netserver`, `netclient`"; description = "Whether to enable `upsmon`."; }; - monitor = mkOption { - type = with types; attrsOf (submodule monitorOptions); + monitor = lib.mkOption { + type = with lib.types; attrsOf (submodule monitorOptions); default = {}; description = '' Set of UPS to monitor. See `man upsmon.conf` for details. ''; }; - settings = mkOption { + settings = lib.mkOption { type = nutFormat.type; default = {}; - defaultText = literalMD '' + defaultText = lib.literalMD '' { MINSUPPLIES = 1; RUN_AS_USER = "root"; @@ -313,7 +310,7 @@ let } ''; description = "Additional settings to add to `upsmon.conf`."; - example = literalMD '' + example = lib.literalMD '' { MINSUPPLIES = 2; NOTIFYFLAG = [ @@ -326,29 +323,29 @@ let }; config = { - enable = mkDefault (elem cfg.mode [ "standalone" "netserver" "netclient" ]); + enable = lib.mkDefault (lib.elem cfg.mode [ "standalone" "netserver" "netclient" ]); settings = { RUN_AS_USER = "root"; # TODO: replace 'root' by another username. - MINSUPPLIES = mkDefault 1; - NOTIFYCMD = mkDefault "${pkgs.nut}/bin/upssched"; - SHUTDOWNCMD = mkDefault "${pkgs.systemd}/bin/shutdown now"; - MONITOR = flip mapAttrsToList cfg.upsmon.monitor (name: monitor: with monitor; [ system powerValue user "\"@upsmon_password_${name}@\"" type ]); + MINSUPPLIES = lib.mkDefault 1; + NOTIFYCMD = lib.mkDefault "${pkgs.nut}/bin/upssched"; + SHUTDOWNCMD = lib.mkDefault "${pkgs.systemd}/bin/shutdown now"; + MONITOR = lib.flip lib.mapAttrsToList cfg.upsmon.monitor (name: monitor: with monitor; [ system powerValue user "\"@upsmon_password_${name}@\"" type ]); }; }; }; userOptions = { options = { - passwordFile = mkOption { - type = types.str; + passwordFile = lib.mkOption { + type = lib.types.str; description = '' The full path to a file that contains the user's (clear text) password. The password file is read on service start. ''; }; - actions = mkOption { - type = with types; listOf str; + actions = lib.mkOption { + type = with lib.types; listOf str; default = []; description = '' Allow the user to do certain things with upsd. @@ -356,8 +353,8 @@ let ''; }; - instcmds = mkOption { - type = with types; listOf str; + instcmds = lib.mkOption { + type = with lib.types; listOf str; default = []; description = '' Let the user initiate specific instant commands. Use "ALL" to grant all commands automatically. For the full list of what your UPS supports, use "upscmd -l". @@ -365,8 +362,8 @@ let ''; }; - upsmon = mkOption { - type = with types; nullOr (enum [ "primary" "secondary" ]); + upsmon = lib.mkOption { + type = with lib.types; nullOr (enum [ "primary" "secondary" ]); default = null; description = '' Add the necessary actions for a upsmon process to work. @@ -384,14 +381,14 @@ in # powerManagement.powerDownCommands power.ups = { - enable = mkEnableOption '' + enable = lib.mkEnableOption '' support for Power Devices, such as Uninterruptible Power Supplies, Power Distribution Units and Solar Controllers ''; - mode = mkOption { + mode = lib.mkOption { default = "standalone"; - type = types.enum [ "none" "standalone" "netserver" "netclient" ]; + type = lib.types.enum [ "none" "standalone" "netserver" "netclient" ]; description = '' The MODE determines which part of the NUT is to be started, and which configuration files must be modified. @@ -416,25 +413,25 @@ in ''; }; - schedulerRules = mkOption { + schedulerRules = lib.mkOption { example = "/etc/nixos/upssched.conf"; - type = types.str; + type = lib.types.str; description = '' File which contains the rules to handle UPS events. ''; }; - openFirewall = mkOption { - type = types.bool; + openFirewall = lib.mkOption { + type = lib.types.bool; default = false; description = '' Open ports in the firewall for `upsd`. ''; }; - maxStartDelay = mkOption { + maxStartDelay = lib.mkOption { default = 45; - type = types.int; + type = lib.types.int; description = '' This can be set as a global variable above your first UPS definition and it can also be set in a UPS section. This value @@ -444,23 +441,23 @@ in ''; }; - upsmon = mkOption { + upsmon = lib.mkOption { default = {}; description = '' Options for the `upsmon.conf` configuration file. ''; - type = types.submodule upsmonOptions; + type = lib.types.submodule upsmonOptions; }; - upsd = mkOption { + upsd = lib.mkOption { default = {}; description = '' Options for the `upsd.conf` configuration file. ''; - type = types.submodule upsdOptions; + type = lib.types.submodule upsdOptions; }; - ups = mkOption { + ups = lib.mkOption { default = {}; # see nut/etc/ups.conf.sample description = '' @@ -468,27 +465,27 @@ in monitoring directly. These are usually attached to serial ports, but USB devices are also supported. ''; - type = with types; attrsOf (submodule upsOptions); + type = with lib.types; attrsOf (submodule upsOptions); }; - users = mkOption { + users = lib.mkOption { default = {}; description = '' Users that can access upsd. See `man upsd.users`. ''; - type = with types; attrsOf (submodule userOptions); + type = with lib.types; attrsOf (submodule userOptions); }; }; }; - config = mkIf cfg.enable { + config = lib.mkIf cfg.enable { assertions = [ (let - totalPowerValue = foldl' add 0 (map (monitor: monitor.powerValue) (attrValues cfg.upsmon.monitor)); + totalPowerValue = lib.foldl' lib.add 0 (map (monitor: monitor.powerValue) (lib.attrValues cfg.upsmon.monitor)); minSupplies = cfg.upsmon.settings.MINSUPPLIES; - in mkIf cfg.upsmon.enable { + in lib.mkIf cfg.upsmon.enable { assertion = totalPowerValue >= minSupplies; message = '' `power.ups.upsmon`: Total configured power value (${toString totalPowerValue}) must be at least MINSUPPLIES (${toString minSupplies}). @@ -498,15 +495,15 @@ in environment.systemPackages = [ pkgs.nut ]; - networking.firewall = mkIf cfg.openFirewall { + networking.firewall = lib.mkIf cfg.openFirewall { allowedTCPPorts = if cfg.upsd.listen == [] then [ defaultPort ] - else unique (forEach cfg.upsd.listen (listen: listen.port)); + else lib.unique (lib.forEach cfg.upsd.listen (listen: listen.port)); }; systemd.services.upsmon = let - secrets = mapAttrsToList (name: monitor: "upsmon_password_${name}") cfg.upsmon.monitor; + secrets = lib.mapAttrsToList (name: monitor: "upsmon_password_${name}") cfg.upsmon.monitor; createUpsmonConf = installSecrets upsmonConf "/run/nut/upsmon.conf" secrets; in { enable = cfg.upsmon.enable; @@ -518,14 +515,14 @@ in ExecStartPre = "${createUpsmonConf}"; ExecStart = "${pkgs.nut}/sbin/upsmon"; ExecReload = "${pkgs.nut}/sbin/upsmon -c reload"; - LoadCredential = mapAttrsToList (name: monitor: "upsmon_password_${name}:${monitor.passwordFile}") cfg.upsmon.monitor; + LoadCredential = lib.mapAttrsToList (name: monitor: "upsmon_password_${name}:${monitor.passwordFile}") cfg.upsmon.monitor; }; environment.NUT_CONFPATH = "/etc/nut"; environment.NUT_STATEPATH = "/var/lib/nut"; }; systemd.services.upsd = let - secrets = mapAttrsToList (name: user: "upsdusers_password_${name}") cfg.users; + secrets = lib.mapAttrsToList (name: user: "upsdusers_password_${name}") cfg.users; createUpsdUsers = installSecrets upsdUsers "/run/nut/upsd.users" secrets; in { enable = cfg.upsd.enable; @@ -538,7 +535,7 @@ in # TODO: replace 'root' by another username. ExecStart = "${pkgs.nut}/sbin/upsd -u root"; ExecReload = "${pkgs.nut}/sbin/upsd -c reload"; - LoadCredential = mapAttrsToList (name: user: "upsdusers_password_${name}:${user.passwordFile}") cfg.users; + LoadCredential = lib.mapAttrsToList (name: user: "upsdusers_password_${name}:${user.passwordFile}") cfg.users; }; environment.NUT_CONFPATH = "/etc/nut"; environment.NUT_STATEPATH = "/var/lib/nut"; @@ -574,11 +571,11 @@ in '' maxstartdelay = ${toString cfg.maxStartDelay} - ${concatStringsSep "\n\n" (forEach (attrValues cfg.ups) (ups: ups.summary))} + ${lib.concatStringsSep "\n\n" (lib.forEach (lib.attrValues cfg.ups) (ups: ups.summary))} ''; "nut/upsd.conf".source = pkgs.writeText "upsd.conf" '' - ${concatStringsSep "\n" (forEach cfg.upsd.listen (listen: "LISTEN ${listen.address} ${toString listen.port}"))} + ${lib.concatStringsSep "\n" (lib.forEach cfg.upsd.listen (listen: "LISTEN ${listen.address} ${toString listen.port}"))} ${cfg.upsd.extraConfig} ''; "nut/upssched.conf".source = cfg.schedulerRules; @@ -586,7 +583,7 @@ in "nut/upsmon.conf".source = "/run/nut/upsmon.conf"; }; - power.ups.schedulerRules = mkDefault "${pkgs.nut}/etc/upssched.conf.sample"; + power.ups.schedulerRules = lib.mkDefault "${pkgs.nut}/etc/upssched.conf.sample"; systemd.tmpfiles.rules = [ "d /var/state/ups -"