nixpkgs/modules/services/monitoring/ups.nix

276 lines
7.7 KiB
Nix
Raw Normal View History

{config, pkgs, ...}:
# TODO: This is not secure, have a look at the file docs/security.txt inside
# the project sources.
with pkgs.lib;
let
cfg = config.power.ups;
in
let
upsOptions = {name, config, ...}:
{
options = {
# This can be infered from the UPS model by looking at
# ${config.nixpkgs.config.nix.storeDir}/nut/share/driver.list
driver = mkOption {
type = types.uniq types.string;
description = ''
Specify the program to run to talk to this UPS. apcsmart,
bestups, and sec are some examples.
'';
};
port = mkOption {
type = types.uniq types.string;
description = ''
The serial port where your UPS is connected. /dev/ttyS0 is
usually the first port on Linux boxes, for example.
'';
};
shutdownOrder = mkOption {
default = 0;
type = types.uniq 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
0s, then the 1s, 2s, and so on. To exclude a UPS from the
shutdown sequence, set this to -1.
'';
};
maxStartDelay = mkOption {
default = null;
type = types.uniq (types.nullOr 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
controls how long upsdrvctl will wait for the driver to finish
starting. This keeps your system from getting stuck due to a
broken driver or UPS.
'';
};
description = mkOption {
default = "";
type = types.string;
description = ''
Description of the UPS.
'';
};
directives = mkOption {
default = [];
type = types.listOf types.string;
description = ''
List of configuration directives for this UPS.
'';
};
summary = mkOption {
default = "";
type = types.string;
description = ''
Lines which would be added inside ups.conf for handling this UPS.
'';
};
};
config = {
directives = mkHeader ([
"driver = ${config.driver}"
"port = ${config.port}"
''desc = "${config.description}"''
"sdorder = ${toString config.shutdownOrder}"
] ++ (optional (config.maxStartDelay != null)
"maxstartdelay = ${toString config.maxStartDelay}")
);
summary =
concatStringsSep "\n "
(["[${name}]"] ++ config.directives);
};
};
in
{
options = {
# powerManagement.powerDownCommands
power.ups = {
enable = mkOption {
default = false;
type = with types; bool;
description = ''
Enables support for Power Devices, such as Uninterruptible Power
Supplies, Power Distribution Units and Solar Controllers.
'';
};
# This option is not used yet.
mode = mkOption {
default = "standalone";
type = types.uniq types.string;
description = ''
The MODE determines which part of the NUT is to be started, and
which configuration files must be modified.
The values of MODE can be:
- none: NUT is not configured, or use the Integrated Power
Management, or use some external system to startup NUT
components. So nothing is to be started.
- standalone: This mode address a local only configuration, with 1
UPS protecting the local system. This implies to start the 3 NUT
layers (driver, upsd and upsmon) and the matching configuration
files. This mode can also address UPS redundancy.
- netserver: same as for the standalone configuration, but also
need some more ACLs and possibly a specific LISTEN directive in
upsd.conf. Since this MODE is opened to the network, a special
care should be applied to security concerns.
- netclient: this mode only requires upsmon.
'';
};
schedulerRules = mkOption {
example = "/etc/nixos/upssched.conf";
type = types.uniq types.string;
description = ''
File which contains the rules to handle UPS events.
'';
};
maxStartDelay = mkOption {
default = 45;
type = types.uniq 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
controls how long upsdrvctl will wait for the driver to finish
starting. This keeps your system from getting stuck due to a
broken driver or UPS.
'';
};
ups = mkOption {
default = {};
# see nut/etc/ups.conf.sample
description = ''
This is where you configure all the UPSes that this system will be
monitoring directly. These are usually attached to serial ports,
but USB devices are also supported.
'';
type = types.attrsOf types.optionSet;
options = [ upsOptions ];
};
};
};
config = mkIf cfg.enable {
environment.systemPackages = [ pkgs.nut ];
jobs.upsmon = {
description = "Uninterruptible Power Supplies (Monitor)";
startOn = "ip-up";
daemonType = "fork";
exec = ''${pkgs.nut}/sbin/upsmon'';
environment.NUT_CONFPATH = "/etc/nut/";
environment.NUT_STATEPATH = "/var/lib/nut/";
};
jobs.upsd = {
description = "Uninterruptible Power Supplies (Daemon)";
startOn = "started network-interfaces and upsmon";
daemonType = "fork";
# TODO: replace 'root' by another username.
exec = ''${pkgs.nut}/sbin/upsd -u root'';
environment.NUT_CONFPATH = "/etc/nut/";
environment.NUT_STATEPATH = "/var/lib/nut/";
};
jobs.upsdrv = {
description = "Uninterruptible Power Supplies (Register all UPS)";
startOn = "started upsd";
# TODO: replace 'root' by another username.
exec = ''${pkgs.nut}/bin/upsdrvctl -u root start'';
task = true;
environment.NUT_CONFPATH = "/etc/nut/";
environment.NUT_STATEPATH = "/var/lib/nut/";
};
environment.etc = [
{ source = pkgs.writeText "nut.conf"
''
MODE = ${cfg.mode}
'';
target = "nut/nut.conf";
}
{ source = pkgs.writeText "ups.conf"
''
maxstartdelay = ${toString cfg.maxStartDelay}
${flip concatStringsSep (flip map (attrValues cfg.ups) (ups: ups.summary)) "
"}
'';
target = "nut/ups.conf";
}
{ source = cfg.schedulerRules;
target = "nut/upssched.conf";
}
# These file are containing private informations and thus should not
# be stored inside the Nix store.
/*
{ source = ;
target = "nut/upsd.conf";
}
{ source = ;
target = "nut/upsd.users";
}
{ source = ;
target = "nut/upsmon.conf;
}
*/
];
power.ups.schedulerRules = mkDefault "${pkgs.nut}/etc/upssched.conf.sample";
system.activationScripts.upsSetup = stringAfter [ "users" "groups" ]
''
# Used to store pid files of drivers.
mkdir -p /var/state/ups
'';
/*
users.extraUsers = [
{ name = "nut";
uid = 84;
home = "/var/lib/nut";
createHome = true;
group = "nut";
description = "UPnP A/V Media Server user";
}
];
users.extraGroups = [
{ name = "nut";
gid = 84;
}
];
*/
};
}