diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index b835907c82f..06dbdc215a5 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -128,6 +128,7 @@ ./services/monitoring/dd-agent.nix ./services/monitoring/graphite.nix ./services/monitoring/monit.nix + ./services/monitoring/munin.nix ./services/monitoring/nagios/default.nix ./services/monitoring/smartd.nix ./services/monitoring/statsd.nix diff --git a/nixos/modules/services/monitoring/munin.nix b/nixos/modules/services/monitoring/munin.nix new file mode 100644 index 00000000000..fea52fa5608 --- /dev/null +++ b/nixos/modules/services/monitoring/munin.nix @@ -0,0 +1,216 @@ +{ config, 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 pkgs.lib; + +let + nodeCfg = config.services.munin-node; + cronCfg = config.services.munin-cron; + + muninPlugins = pkgs.stdenv.mkDerivation { + name = "munin-available-plugins"; + buildCommand = '' + mkdir -p $out + + cp --preserve=mode ${pkgs.munin}/lib/plugins/* $out/ + + for file in $out/*; do + case "$file" in + plugin.sh) continue;; + esac + + # read magic makers from the file + family=$(sed -nr 's/.*#%#\s+family\s*=\s*(\S+)\s*/\1/p' $file) + cap=$(sed -nr 's/.*#%#\s+capabilities\s*=\s*(.+)/\1/p' $file) + + wrapProgram $file \ + --set PATH "/run/current-system/sw/bin:/run/current-system/sw/sbin" \ + --set MUNIN_LIBDIR "${pkgs.munin}/lib" \ + --set MUNIN_PLUGSTATE "/var/run/munin" + + # munin uses markers to tell munin-node-configure what a plugin can do + echo "#%# family=$family" >> $file + echo "#%# capabilities=$cap" >> $file + done + + # NOTE: we disable disktstats because plugin seems to fail and it hangs html generation (100% CPU + memory leak) + rm -f $out/diskstats + ''; + buildInputs = [ pkgs.makeWrapper ]; + }; + + 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 ^127\.0\.0\.1$ + + ${nodeCfg.extraConfig} + ''; +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 . + ''; + }; + + extraConfig = mkOption { + default = ""; + description = '' + munin-node.conf extra configuration. See + + ''; + }; + + # 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 /var/www/munin/, configure your + favourite webserver to serve static files. + ''; + example = literalExample '' + services = { + munin-node.enable = true; + munin-cron = { + enable = true; + hosts = ''' + [''${config.networking.hostName}] + address localhost + '''; + extraGlobalConfig = ''' + contact.email.command mail -s "Munin notification for ''${var:host}" someone@example.com + '''; + }; + }; + ''; + }; + + extraGlobalConfig = mkOption { + default = ""; + description = '' + munin.conf extra global configuration. + See . + Useful to setup notifications, see + + ''; + }; + + 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 + + ''; + }; + + }; + + }; + + config = mkMerge [ (mkIf (nodeCfg.enable || cronCfg.enable) { + + environment.systemPackages = [ pkgs.munin ]; + + users.extraUsers = [{ + name = "munin"; + description = "Munin monitoring user"; + group = "munin"; + }]; + + users.extraGroups = [{ + name = "munin"; + }]; + + }) (mkIf nodeCfg.enable { + + systemd.services.munin-node = { + description = "Munin node, the agent process"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + path = [ pkgs.munin ]; + environment.MUNIN_PLUGSTATE = "/var/run/munin"; + serviceConfig = { + ExecStart = "${pkgs.munin}/sbin/munin-node --config ${nodeConf} --servicedir /etc/munin/plugins/"; + }; + }; + + system.activationScripts.munin-node = '' + echo "updating munin plugins..." + + export PATH="/run/current-system/sw/bin:/run/current-system/sw/sbin"; + mkdir -p /etc/munin/plugins + rm -rf /etc/munin/plugins/* + ${pkgs.munin}/sbin/munin-node-configure --shell --families contrib,auto,manual --config ${nodeConf} --libdir=${muninPlugins} --servicedir=/etc/munin/plugins 2>/dev/null | ${pkgs.bash}/bin/bash + ''; + + }) (mkIf cronCfg.enable { + + services.cron.systemCronJobs = [ + "*/5 * * * * munin ${pkgs.munin}/bin/munin-cron --config ${muninConf}" + ]; + + system.activationScripts.munin-cron = stringAfter [ "users" "groups" ] '' + mkdir -p /var/{run,log,www,lib}/munin + chown -R munin:munin /var/{run,log,www,lib}/munin + ''; + + })]; +} diff --git a/pkgs/servers/monitoring/munin/adding_servicedir_munin-node.patch b/pkgs/servers/monitoring/munin/adding_servicedir_munin-node.patch new file mode 100644 index 00000000000..856f3d73011 --- /dev/null +++ b/pkgs/servers/monitoring/munin/adding_servicedir_munin-node.patch @@ -0,0 +1,84 @@ +From 75a3ec48814e7b9a9b22259a04009076363be3f1 Mon Sep 17 00:00:00 2001 +From: Igor Kolar +Date: Thu, 17 Oct 2013 00:48:23 +0200 +Subject: [PATCH 1/2] node: added --servicedir switch to munin-node + +This code is copied over from munin-node-config, that already does the same +--- + node/sbin/munin-node | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/node/sbin/munin-node b/node/sbin/munin-node +index 7b2e180..0a93450 100755 +--- a/node/sbin/munin-node ++++ b/node/sbin/munin-node +@@ -35,7 +35,7 @@ use Munin::Node::OS; + use Munin::Node::Service; + use Munin::Node::Server; + +-my $servicedir; ++my $servicedir = "$Munin::Common::Defaults::MUNIN_CONFDIR/plugins"; + my $sconfdir = "$Munin::Common::Defaults::MUNIN_CONFDIR/plugin-conf.d"; + my $conffile = "$Munin::Common::Defaults::MUNIN_CONFDIR/munin-node.conf"; + my $DEBUG = 0; +@@ -101,6 +101,7 @@ sub parse_args + + print_usage_and_exit() unless GetOptions( + "config=s" => \$conffile, ++ "servicedir=s" => \$servicedir, + "debug!" => \$DEBUG, + "pidebug!" => \$PIDEBUG, + "paranoia!" => \$paranoia, +@@ -166,6 +167,10 @@ and returning the output they produce. + + Use EfileE as configuration file. [@@CONFDIR@@/munin-node.conf] + ++=item B<< --servicedir >> ++ ++Override plugin directory [@@CONFDIR@@/plugins/] ++ + =item B< --[no]paranoia > + + Only run plugins owned by root. Check permissions as well. [--noparanoia] +-- +1.8.4 + + +From b8e17cbe73ae4c71b93ff5687ba86db1d0c1f5bd Mon Sep 17 00:00:00 2001 +From: Steve Schnepp +Date: Thu, 17 Oct 2013 11:52:10 +0200 +Subject: [PATCH 2/2] node: untaint the service-dir args + +--- + node/sbin/munin-node | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/node/sbin/munin-node b/node/sbin/munin-node +index 0a93450..909c8c4 100755 +--- a/node/sbin/munin-node ++++ b/node/sbin/munin-node +@@ -99,9 +99,10 @@ sub parse_args + { + my @ORIG_ARGV = @ARGV; + ++ my $servicedir_cmdline; + print_usage_and_exit() unless GetOptions( + "config=s" => \$conffile, +- "servicedir=s" => \$servicedir, ++ "servicedir=s" => \$servicedir_cmdline, + "debug!" => \$DEBUG, + "pidebug!" => \$PIDEBUG, + "paranoia!" => \$paranoia, +@@ -109,6 +110,9 @@ sub parse_args + "help" => \&print_usage_and_exit, + ); + ++ # We untaint the args brutally, since the sysadm should know what he does ++ $servicedir = $1 if defined $servicedir_cmdline && $servicedir_cmdline =~ m/(.*)/; ++ + # Reset ARGV (for HUPing) + @ARGV = @ORIG_ARGV; + +-- +1.8.4 + diff --git a/pkgs/servers/monitoring/munin/default.nix b/pkgs/servers/monitoring/munin/default.nix index d7da0f658e2..4f16ab35527 100644 --- a/pkgs/servers/monitoring/munin/default.nix +++ b/pkgs/servers/monitoring/munin/default.nix @@ -1,9 +1,5 @@ { stdenv, fetchurl, makeWrapper, which, coreutils, rrdtool, perl, perlPackages -, python, ruby, openjdk }: - -# TODO: split into server/node derivations - -# FIXME: munin tries to write log files and web graphs to its installation path. +, python, ruby, openjdk, nettools }: stdenv.mkDerivation rec { version = "2.0.17"; @@ -19,6 +15,7 @@ stdenv.mkDerivation rec { which coreutils rrdtool + nettools perl perlPackages.ModuleBuild perlPackages.HTMLTemplate @@ -36,17 +33,49 @@ stdenv.mkDerivation rec { perlPackages.NetServer perlPackages.ListMoreUtils perlPackages.TimeHiRes + perlPackages.LWPUserAgent + perlPackages.DBDPg python ruby openjdk + # tests + perlPackages.TestLongString + perlPackages.TestDifferences + perlPackages.TestDeep + perlPackages.TestMockModule + perlPackages.TestMockObject + perlPackages.FileSlurp + perlPackages.IOStringy + ]; + + # TODO: tests are failing http://munin-monitoring.org/ticket/1390#comment:1 + # NOTE: important, test command always exits with 0, think of a way to abort the build once tests pass + doCheck = false; + + checkPhase = '' + export PERL5LIB="$PERL5LIB:${rrdtool}/lib/perl" + LC_ALL=C make -j1 test + ''; + + patches = [ + # https://rt.cpan.org/Public/Bug/Display.html?id=75112 + ./dont_preserve_source_dir_permissions.patch + + # https://github.com/munin-monitoring/munin/pull/134 + ./adding_servicedir_munin-node.patch ]; preBuild = '' + substituteInPlace "Makefile" \ + --replace "/bin/pwd" "pwd" + + # munin checks at build time if user/group exists, unpure sed -i '/CHECKUSER/d' Makefile sed -i '/CHOWN/d' Makefile sed -i '/CHECKGROUP/d' Makefile - substituteInPlace "Makefile" \ - --replace "/usr/pwd" "pwd" + + # munin hardcodes PATH, we need it to obey $PATH + sed -i '/ENV{PATH}/d' node/lib/Munin/Node/Service.pm ''; # DESTDIR shouldn't be needed (and shouldn't have worked), but munin @@ -60,7 +89,7 @@ stdenv.mkDerivation rec { PYTHON=${python}/bin/python RUBY=${ruby}/bin/ruby JAVARUN=${openjdk}/bin/java - HOSTNAME=default + PLUGINUSER=munin ''; postFixup = '' @@ -78,7 +107,8 @@ stdenv.mkDerivation rec { case "$file" in *.jar) continue;; esac - wrapProgram "$file" --set PERL5LIB $out/lib/perl5/site_perl:${perlPackages.Log4Perl}/lib/perl5/site_perl:${perlPackages.IOSocketInet6}/lib/perl5/site_perl:${perlPackages.Socket6}/lib/perl5/site_perl:${perlPackages.URI}/lib/perl5/site_perl:${perlPackages.DBFile}/lib/perl5/site_perl:${perlPackages.DateManip}/lib/perl5/site_perl:${perlPackages.HTMLTemplate}/lib/perl5/site_perl:${perlPackages.FileCopyRecursive}/lib/perl5/site_perl:${perlPackages.FCGI}/lib/perl5/site_perl:${perlPackages.NetSNMP}/lib/perl5/site_perl:${perlPackages.NetServer}/lib/perl5/site_perl:${perlPackages.ListMoreUtils}/lib/perl5/site_perl:${perlPackages.TimeHiRes}/lib/perl5/site_perl:${rrdtool}/lib/perl + wrapProgram "$file" \ + --set PERL5LIB "$out/lib/perl5/site_perl:${perlPackages.Log4Perl}/lib/perl5/site_perl:${perlPackages.IOSocketInet6}/lib/perl5/site_perl:${perlPackages.Socket6}/lib/perl5/site_perl:${perlPackages.URI}/lib/perl5/site_perl:${perlPackages.DBFile}/lib/perl5/site_perl:${perlPackages.DateManip}/lib/perl5/site_perl:${perlPackages.HTMLTemplate}/lib/perl5/site_perl:${perlPackages.FileCopyRecursive}/lib/perl5/site_perl:${perlPackages.FCGI}/lib/perl5/site_perl:${perlPackages.NetSNMP}/lib/perl5/site_perl:${perlPackages.NetServer}/lib/perl5/site_perl:${perlPackages.ListMoreUtils}/lib/perl5/site_perl:${perlPackages.TimeHiRes}/lib/perl5/site_perl:${rrdtool}/lib/perl:${perlPackages.DBDPg}/lib/perl5/site_perl:${perlPackages.LWPUserAgent}/lib/perl5/site_perl" done ''; diff --git a/pkgs/servers/monitoring/munin/dont_preserve_source_dir_permissions.patch b/pkgs/servers/monitoring/munin/dont_preserve_source_dir_permissions.patch new file mode 100644 index 00000000000..78eac728305 --- /dev/null +++ b/pkgs/servers/monitoring/munin/dont_preserve_source_dir_permissions.patch @@ -0,0 +1,18 @@ +# https://rt.cpan.org/Public/Bug/Display.html?id=75112 +diff --git a/master/lib/Munin/Master/HTMLOld.pm b/master/lib/Munin/Master/HTMLOld.pm +index 2b6e71f..c0aa2c0 100644 +--- a/master/lib/Munin/Master/HTMLOld.pm ++++ b/master/lib/Munin/Master/HTMLOld.pm +@@ -711,10 +711,12 @@ sub emit_main_index { + + sub copy_web_resources { + my ($staticdir, $htmldir) = @_; ++ local $File::Copy::Recursive::KeepMode = 0; + unless(dircopy($staticdir, "$htmldir/static")){ + ERROR "[ERROR] Could not copy contents from $staticdir to $htmldir"; + die "[ERROR] Could not copy contents from $staticdir to $htmldir"; + } ++ local $File::Copy::Recursive::KeepMode = 1; + } + + sub instanciate_comparison_templates {