0
0
Fork 0
mirror of https://github.com/NixOS/nixpkgs.git synced 2025-07-13 21:50:33 +03:00

nixos/power.ups: remove with lib;

This commit is contained in:
Felix Buehler 2024-08-27 20:43:33 +02:00
parent 8cf91e2c5b
commit d27bffefab

View file

@ -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: `<upsname>[@<hostname>[:<port>]]`
@ -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 -"