nixpkgs/nixos/modules/services/monitoring/munin.nix
Kjetil Orbekk bd3e49a80e nixos/munin: fix broken plugin wrappers
The munin-node service used wrapProgram to inject environment variables.
This doesn't work because munin plugins depend on argv[0], which is
overwritten when the executable is a script with a shebang line (example
below).

This commit removes the wrappers and instead passes the required
environment variables to munin-node.

Eliminating the wrappers resulted in some broken plugins, e.g., meminfo
and hddtemp_smartctl. That was fixed with the per-plugin configuration.

Example:

  The plugin if_eth0 is a symlink to /.../plugins/if_, which uses $0
  to determine that it should monitor traffic on the eth0 interface.

  if_ is a wrapped program, and runs `exec -a "$0" .if_-wrapped`

  .if_-wrapped has a "#!/nix/.../bash" line, which results in bash
  changing $0, and as a result the plugin thinks my interface
  is called "-wrapped".
2017-11-25 11:23:56 +01:00

220 lines
6.0 KiB
Nix

{ config, lib, pkgs, ... }:
# TODO: support munin-async
# TODO: LWP/Pg perl libs aren't recognized
# TODO: support fastcgi
# http://munin-monitoring.org/wiki/CgiHowto2
# spawn-fcgi -s /var/run/munin/fastcgi-graph.sock -U www-data -u munin -g munin /usr/lib/munin/cgi/munin-cgi-graph
# spawn-fcgi -s /var/run/munin/fastcgi-html.sock -U www-data -u munin -g munin /usr/lib/munin/cgi/munin-cgi-html
# https://paste.sh/vofcctHP#-KbDSXVeWoifYncZmLfZzgum
# nginx http://munin.readthedocs.org/en/latest/example/webserver/nginx.html
with lib;
let
nodeCfg = config.services.munin-node;
cronCfg = config.services.munin-cron;
muninConf = pkgs.writeText "munin.conf"
''
dbdir /var/lib/munin
htmldir /var/www/munin
logdir /var/log/munin
rundir /var/run/munin
${cronCfg.extraGlobalConfig}
${cronCfg.hosts}
'';
nodeConf = pkgs.writeText "munin-node.conf"
''
log_level 3
log_file Sys::Syslog
port 4949
host *
background 0
user root
group root
host_name ${config.networking.hostName}
setsid 0
# wrapped plugins by makeWrapper being with dots
ignore_file ^\.
allow ^::1$
allow ^127\.0\.0\.1$
${nodeCfg.extraConfig}
'';
pluginConf = pkgs.writeText "munin-plugin-conf"
''
[hddtemp_smartctl]
user root
group root
[meminfo]
user root
group root
[ipmi*]
user root
group root
'';
pluginConfDir = pkgs.stdenv.mkDerivation {
name = "munin-plugin-conf.d";
buildCommand = ''
mkdir $out
ln -s ${pluginConf} $out/nixos-config
'';
};
in
{
options = {
services.munin-node = {
enable = mkOption {
default = false;
description = ''
Enable Munin Node agent. Munin node listens on 0.0.0.0 and
by default accepts connections only from 127.0.0.1 for security reasons.
See <link xlink:href='http://munin-monitoring.org/wiki/munin-node.conf' />.
'';
};
extraConfig = mkOption {
default = "";
type = types.lines;
description = ''
<filename>munin-node.conf</filename> extra configuration. See
<link xlink:href='http://munin-monitoring.org/wiki/munin-node.conf' />
'';
};
# TODO: add option to add additional plugins
};
services.munin-cron = {
enable = mkOption {
default = false;
description = ''
Enable munin-cron. Takes care of all heavy lifting to collect data from
nodes and draws graphs to html. Runs munin-update, munin-limits,
munin-graphs and munin-html in that order.
HTML output is in <filename>/var/www/munin/</filename>, configure your
favourite webserver to serve static files.
'';
};
extraGlobalConfig = mkOption {
default = "";
description = ''
<filename>munin.conf</filename> extra global configuration.
See <link xlink:href='http://munin-monitoring.org/wiki/munin.conf' />.
Useful to setup notifications, see
<link xlink:href='http://munin-monitoring.org/wiki/HowToContact' />
'';
example = ''
contact.email.command mail -s "Munin notification for ''${var:host}" someone@example.com
'';
};
hosts = mkOption {
example = ''
[''${config.networking.hostName}]
address localhost
'';
description = ''
Definitions of hosts of nodes to collect data from. Needs at least one
hosts for cron to succeed. See
<link xlink:href='http://munin-monitoring.org/wiki/munin.conf' />
'';
};
};
};
config = mkMerge [ (mkIf (nodeCfg.enable || cronCfg.enable) {
environment.systemPackages = [ pkgs.munin ];
users.extraUsers = [{
name = "munin";
description = "Munin monitoring user";
group = "munin";
uid = config.ids.uids.munin;
}];
users.extraGroups = [{
name = "munin";
gid = config.ids.gids.munin;
}];
}) (mkIf nodeCfg.enable {
systemd.services.munin-node = {
description = "Munin Node";
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
path = with pkgs; [ munin smartmontools "/run/current-system/sw" "/run/wrappers" ];
environment.MUNIN_LIBDIR = "${pkgs.munin}/lib";
environment.MUNIN_PLUGSTATE = "/var/run/munin";
environment.MUNIN_LOGDIR = "/var/log/munin";
preStart = ''
echo "updating munin plugins..."
mkdir -p /etc/munin/plugins
rm -rf /etc/munin/plugins/*
${pkgs.munin}/bin/munin-node-configure --suggest --shell --families contrib,auto,manual --config ${nodeConf} --libdir=${pkgs.munin}/lib/plugins --servicedir=/etc/munin/plugins --sconfdir=${pluginConfDir} 2>/dev/null | ${pkgs.bash}/bin/bash
# NOTE: we disable disktstats because plugin seems to fail and it hangs html generation (100% CPU + memory leak)
rm /etc/munin/plugins/diskstats || true
'';
serviceConfig = {
ExecStart = "${pkgs.munin}/sbin/munin-node --config ${nodeConf} --servicedir /etc/munin/plugins/ --sconfdir=${pluginConfDir}";
};
};
# munin_stats plugin breaks as of 2.0.33 when this doesn't exist
systemd.tmpfiles.rules = [ "d /var/run/munin 0755 munin munin -" ];
}) (mkIf cronCfg.enable {
systemd.timers.munin-cron = {
description = "batch Munin master programs";
wantedBy = [ "timers.target" ];
timerConfig.OnCalendar = "*:0/5";
};
systemd.services.munin-cron = {
description = "batch Munin master programs";
unitConfig.Documentation = "man:munin-cron(8)";
serviceConfig = {
Type = "oneshot";
User = "munin";
ExecStart = "${pkgs.munin}/bin/munin-cron --config ${muninConf}";
};
};
systemd.tmpfiles.rules = [
"d /var/run/munin 0755 munin munin -"
"d /var/log/munin 0755 munin munin -"
"d /var/www/munin 0755 munin munin -"
"d /var/lib/munin 0755 munin munin -"
];
})];
}