diff --git a/nixos/modules/services/monitoring/munin.nix b/nixos/modules/services/monitoring/munin.nix
index 2b265d5b5a9..f6798632724 100644
--- a/nixos/modules/services/monitoring/munin.nix
+++ b/nixos/modules/services/monitoring/munin.nix
@@ -4,7 +4,7 @@
# TODO: LWP/Pg perl libs aren't recognized
# TODO: support fastcgi
-# http://munin-monitoring.org/wiki/CgiHowto2
+# http://guide.munin-monitoring.org/en/latest/example/webserver/apache-cgi.html
# spawn-fcgi -s /run/munin/fastcgi-graph.sock -U www-data -u munin -g munin /usr/lib/munin/cgi/munin-cgi-graph
# spawn-fcgi -s /run/munin/fastcgi-html.sock -U www-data -u munin -g munin /usr/lib/munin/cgi/munin-cgi-html
# https://paste.sh/vofcctHP#-KbDSXVeWoifYncZmLfZzgum
@@ -24,6 +24,8 @@ let
logdir /var/log/munin
rundir /run/munin
+ ${lib.optionalString (cronCfg.extraCSS != "") "staticdir ${customStaticDir}"}
+
${cronCfg.extraGlobalConfig}
${cronCfg.hosts}
@@ -63,6 +65,11 @@ let
[ipmi*]
user root
group root
+
+ [munin*]
+ env.UPDATE_STATSFILE /var/lib/munin/munin-update.stats
+
+ ${nodeCfg.extraPluginConfig}
'';
pluginConfDir = pkgs.stdenv.mkDerivation {
@@ -72,6 +79,54 @@ let
ln -s ${pluginConf} $out/nixos-config
'';
};
+
+ # Copy one Munin plugin into the Nix store with a specific name.
+ # This is suitable for use with plugins going directly into /etc/munin/plugins,
+ # i.e. munin.extraPlugins.
+ internOnePlugin = name: path:
+ "cp -a '${path}' '${name}'";
+
+ # Copy an entire tree of Munin plugins into a single directory in the Nix
+ # store, with no renaming.
+ # This is suitable for use with munin-node-configure --suggest, i.e.
+ # munin.extraAutoPlugins.
+ internManyPlugins = name: path:
+ "find '${path}' -type f -perm /a+x -exec cp -a -t . '{}' '+'";
+
+ # Use the appropriate intern-fn to copy the plugins into the store and patch
+ # them afterwards in an attempt to get them to run on NixOS.
+ internAndFixPlugins = name: intern-fn: paths:
+ pkgs.runCommand name {} ''
+ mkdir -p "$out"
+ cd "$out"
+ ${lib.concatStringsSep "\n"
+ (lib.attrsets.mapAttrsToList intern-fn paths)}
+ chmod -R u+w .
+ find . -type f -exec sed -E -i '
+ s,(/usr)?/s?bin/,/run/current-system/sw/bin/,g
+ ' '{}' '+'
+ '';
+
+ # TODO: write a derivation for munin-contrib, so that for contrib plugins
+ # you can just refer to them by name rather than needing to include a copy
+ # of munin-contrib in your nixos configuration.
+ extraPluginDir = internAndFixPlugins "munin-extra-plugins.d"
+ internOnePlugin nodeCfg.extraPlugins;
+
+ extraAutoPluginDir = internAndFixPlugins "munin-extra-auto-plugins.d"
+ internManyPlugins
+ (builtins.listToAttrs
+ (map
+ (path: { name = baseNameOf path; value = path; })
+ nodeCfg.extraAutoPlugins));
+
+ customStaticDir = pkgs.runCommand "munin-custom-static-data" {} ''
+ cp -a "${pkgs.munin}/etc/opt/munin/static" "$out"
+ cd "$out"
+ chmod -R u+w .
+ echo "${cronCfg.extraCSS}" >> style.css
+ echo "${cronCfg.extraCSS}" >> style-new.css
+ '';
in
{
@@ -82,11 +137,12 @@ in
enable = mkOption {
default = false;
+ type = types.bool;
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 .
+ See .
'';
};
@@ -95,18 +151,108 @@ in
type = types.lines;
description = ''
munin-node.conf extra configuration. See
-
+
'';
};
- # TODO: add option to add additional plugins
+ extraPluginConfig = mkOption {
+ default = "";
+ type = types.lines;
+ description = ''
+ plugin-conf.d extra plugin configuration. See
+
+ '';
+ example = ''
+ [fail2ban_*]
+ user root
+ '';
+ };
+ extraPlugins = mkOption {
+ default = {};
+ type = with types; attrsOf path;
+ description = ''
+ Additional Munin plugins to activate. Keys are the name of the plugin
+ symlink, values are the path to the underlying plugin script. You
+ can use the same plugin script multiple times (e.g. for wildcard
+ plugins).
+
+ Note that these plugins do not participate in autoconfiguration. If
+ you want to autoconfigure additional plugins, use
+ .
+
+ Plugins enabled in this manner take precedence over autoconfigured
+ plugins.
+
+ Plugins will be copied into the Nix store, and it will attempt to
+ modify them to run properly by fixing hardcoded references to
+ /bin, /usr/bin,
+ /sbin, and /usr/sbin.
+ '';
+ example = literalExample ''
+ {
+ zfs_usage_bigpool = /src/munin-contrib/plugins/zfs/zfs_usage_;
+ zfs_usage_smallpool = /src/munin-contrib/plugins/zfs/zfs_usage_;
+ zfs_list = /src/munin-contrib/plugins/zfs/zfs_list;
+ };
+ '';
+ };
+
+ extraAutoPlugins = mkOption {
+ default = [];
+ type = with types; listOf path;
+ description = ''
+ Additional Munin plugins to autoconfigure, using
+ munin-node-configure --suggest. These should be
+ the actual paths to the plugin files (or directories containing them),
+ not just their names.
+
+ If you want to manually enable individual plugins instead, use
+ .
+
+ Note that only plugins that have the 'autoconfig' capability will do
+ anything if listed here, since plugins that cannot autoconfigure
+ won't be automatically enabled by
+ munin-node-configure.
+
+ Plugins will be copied into the Nix store, and it will attempt to
+ modify them to run properly by fixing hardcoded references to
+ /bin, /usr/bin,
+ /sbin, and /usr/sbin.
+ '';
+ example = literalExample ''
+ [
+ /src/munin-contrib/plugins/zfs
+ /src/munin-contrib/plugins/ssh
+ ];
+ '';
+ };
+
+ disabledPlugins = mkOption {
+ # TODO: figure out why Munin isn't writing the log file and fix it.
+ # In the meantime this at least suppresses a useless graph full of
+ # NaNs in the output.
+ default = [ "munin_stats" ];
+ type = with types; listOf string;
+ description = ''
+ Munin plugins to disable, even if
+ munin-node-configure --suggest tries to enable
+ them. To disable a wildcard plugin, use an actual wildcard, as in
+ the example.
+
+ munin_stats is disabled by default as it tries to read
+ /var/log/munin/munin-update.log for timing
+ information, and the NixOS build of Munin does not write this file.
+ '';
+ example = [ "diskstats" "zfs_usage_*" ];
+ };
};
services.munin-cron = {
enable = mkOption {
default = false;
+ type = types.bool;
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,
@@ -119,11 +265,12 @@ in
extraGlobalConfig = mkOption {
default = "";
+ type = types.lines;
description = ''
munin.conf extra global configuration.
- See .
+ See .
Useful to setup notifications, see
-
+
'';
example = ''
contact.email.command mail -s "Munin notification for ''${var:host}" someone@example.com
@@ -131,14 +278,34 @@ in
};
hosts = mkOption {
+ default = "";
+ type = types.lines;
+ description = ''
+ Definitions of hosts of nodes to collect data from. Needs at least one
+ host for cron to succeed. See
+
+ '';
example = ''
[''${config.networking.hostName}]
address localhost
'';
+ };
+
+ extraCSS = mkOption {
+ default = "";
+ type = types.lines;
description = ''
- Definitions of hosts of nodes to collect data from. Needs at least one
- hosts for cron to succeed. See
-
+ Custom styling for the HTML that munin-cron generates. This will be
+ appended to the CSS files used by munin-cron and will thus take
+ precedence over the builtin styles.
+ '';
+ example = ''
+ /* A simple dark theme. */
+ html, body { background: #222222; }
+ #header, #footer { background: #333333; }
+ img.i, img.iwarn, img.icrit, img.iunkn {
+ filter: invert(100%) hue-rotate(-30deg);
+ }
'';
};
@@ -155,6 +322,7 @@ in
description = "Munin monitoring user";
group = "munin";
uid = config.ids.uids.munin;
+ home = "/var/lib/munin";
}];
users.groups = [{
@@ -173,14 +341,27 @@ in
environment.MUNIN_PLUGSTATE = "/run/munin";
environment.MUNIN_LOGDIR = "/var/log/munin";
preStart = ''
- echo "updating munin plugins..."
+ echo "Updating munin plugins..."
mkdir -p /etc/munin/plugins
rm -rf /etc/munin/plugins/*
+
+ # Autoconfigure builtin 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
+ # Autoconfigure extra plugins
+ ${pkgs.munin}/bin/munin-node-configure --suggest --shell --families contrib,auto,manual --config ${nodeConf} --libdir=${extraAutoPluginDir} --servicedir=/etc/munin/plugins --sconfdir=${pluginConfDir} 2>/dev/null | ${pkgs.bash}/bin/bash
+
+ ${lib.optionalString (nodeCfg.extraPlugins != {}) ''
+ # Link in manually enabled plugins
+ ln -f -s -t /etc/munin/plugins ${extraPluginDir}/*
+ ''}
+
+ ${lib.optionalString (nodeCfg.disabledPlugins != []) ''
+ # Disable plugins
+ cd /etc/munin/plugins
+ rm -f ${toString nodeCfg.disabledPlugins}
+ ''}
'';
serviceConfig = {
ExecStart = "${pkgs.munin}/sbin/munin-node --config ${nodeConf} --servicedir /etc/munin/plugins/ --sconfdir=${pluginConfDir}";
@@ -192,6 +373,10 @@ in
}) (mkIf cronCfg.enable {
+ # Munin is hardcoded to use DejaVu Mono and the graphs come out wrong if
+ # it's not available.
+ fonts.fonts = [ pkgs.dejavu_fonts ];
+
systemd.timers.munin-cron = {
description = "batch Munin master programs";
wantedBy = [ "timers.target" ];
diff --git a/nixos/tests/munin.nix b/nixos/tests/munin.nix
index 9f66005292a..95cecf17b8c 100644
--- a/nixos/tests/munin.nix
+++ b/nixos/tests/munin.nix
@@ -15,9 +15,7 @@ import ./make-test.nix ({ pkgs, ...} : {
munin-node = {
enable = true;
# disable a failing plugin to prevent irrelevant error message, see #23049
- extraConfig = ''
- ignore_file ^apc_nis$
- '';
+ disabledPlugins = [ "apc_nis" ];
};
munin-cron = {
enable = true;
diff --git a/pkgs/servers/monitoring/munin/default.nix b/pkgs/servers/monitoring/munin/default.nix
index 032789ef05d..5153fcc5ef6 100644
--- a/pkgs/servers/monitoring/munin/default.nix
+++ b/pkgs/servers/monitoring/munin/default.nix
@@ -3,14 +3,14 @@
}:
stdenv.mkDerivation rec {
- version = "2.0.37";
+ version = "2.0.43";
name = "munin-${version}";
src = fetchFromGitHub {
owner = "munin-monitoring";
repo = "munin";
rev = version;
- sha256 = "10niyzckx90dwdr4d7vj07d1qjy3nk7xzp30nqnlxzbaww7n5v78";
+ sha256 = "1ydhf9hcb3n5h0ss5f1zf9yz4r4njqxazlz931ixvx5gyhj9gq5l";
};
buildInputs = [
@@ -75,6 +75,7 @@ stdenv.mkDerivation rec {
];
preBuild = ''
+ echo "${version}" > RELEASE
substituteInPlace "Makefile" \
--replace "/bin/pwd" "pwd" \
--replace "HTMLOld.3pm" "HTMLOld.3"