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"