diff --git a/README.md b/README.md index 960c5caa..9907bc0f 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,27 @@ This is the repository for all user contributed stuff -* contrib/tools/ is for 3rd-party tools. It usually serves as an incubator of trunk/contrib. -* contrib/plugins/ is for 3rd-party plugins. It will serve as the backend of exchange.munin-monitoring.org when it is operational again. -* contrib/templates/ is for 3rd-party templates. Feel free to update templates, or even to create new ones. Bonus points for mobile-friendly ones :) +# contrib/plugins/ - 3rd-party plugins + +**This is usually where you want to begin your journey.** + +Here you'll find all the plugins coming from http://exchange.munin-monitoring.org/. +It as evolved since then, but + +# contrib/templates/ - 3rd-party templates + +Feel free to update templates here, or even to create new ones. + +Bonus points for mobile-friendly ones :) + +Note that the one named `official` is a loose-synced copy of the one in SVN trunk. +It should serves as a base for small editions that can be resynced in SVN trunk, so for that : + +* don't copy the whole template +* directly edit files in this directory + +# contrib/tools/ - 3rd-party tools + +Here, you can put just any kind of tool. Please use this directory instead of a random place on the internet. +It makes things way more easy to search for others. + +And, it serves as an incubator of SVN `trunk/contrib` :-) diff --git a/plugins/other/ejabberd_ b/plugins/other/ejabberd_ index 0fee887e..b7757d42 100755 --- a/plugins/other/ejabberd_ +++ b/plugins/other/ejabberd_ @@ -43,7 +43,6 @@ shopt -s extglob EJCTL=$(which ejabberdctl) EJVER=$($EJCTL status | awk '/^ejabberd / {print $2}') -MUNIN_DEBUG=${MUNIN_DEBUG:-0} if [ "$1" == "autoconf" ]; then if [ -x $EJCTL > /dev/null ]; then diff --git a/plugins/other/riak_memory b/plugins/other/riak_memory index 25fe4764..c1bf08b5 100755 --- a/plugins/other/riak_memory +++ b/plugins/other/riak_memory @@ -5,7 +5,7 @@ # sample config in /etc/munin/plugin-conf.d/riak # # [riak_*] -# RIAK_URL=http://127.0.0.1:8091/stats +# env.RIAK_URL http://127.0.0.1:8091/stats # any questions to fygrave at o0o dot nu # # This plugin monitors memory allocation on each node. diff --git a/plugins/other/riak_node b/plugins/other/riak_node index 0ac851f8..1d3dcc89 100755 --- a/plugins/other/riak_node +++ b/plugins/other/riak_node @@ -4,7 +4,7 @@ # sample config in /etc/munin/plugin-conf.d/riak # # [riak_*] -# RIAK_URL=http://127.0.0.1:8091/stats +# env.RIAK_URL http://127.0.0.1:8091/stats # any questions to fygrave at o0o dot nu # # This plugin monitors put/get rate at each node. diff --git a/plugins/other/shoutcast2 b/plugins/other/shoutcast2 index 5e0ed963..250c7ebb 100755 --- a/plugins/other/shoutcast2 +++ b/plugins/other/shoutcast2 @@ -104,7 +104,7 @@ else // this is for munin's configuration tool // could do something more complicated here -if(count($argv) == 2 && $arv[1] == 'authconf') { +if(count($argv) == 2 && $argv[1] == 'autoconf') { exit('yes'); } diff --git a/plugins/san/emc_comprehensive/emc_comprehensive b/plugins/san/emc_comprehensive/emc_comprehensive new file mode 100755 index 00000000..174944aa --- /dev/null +++ b/plugins/san/emc_comprehensive/emc_comprehensive @@ -0,0 +1,325 @@ +#! /usr/bin/perl + +use strict; +use warnings; + +use IO::File; +use Data::Dumper; +use POSIX qw(strftime); + +$ENV{LANG} = "C"; + +my $spool_fetch_epoch = (shift || 0); + +# Don't reply for too old $spool_fetch_epoch) +# Max 7 days +$spool_fetch_epoch = time - 3600 * 24 * 7 if $spool_fetch_epoch < time - 3600 * 24 * 7; + +my $max_epoch = ($ENV{MAX_EPOCH} || 0); +my %fieldset; + +my @files = sort glob($ENV{FILES} || "*.tsv*"); +FILE: for my $file (@files) { + my $values = {}; + my $nb_epochs = 0; my $first_epoch; + + # Read file + { + # Skipping if the file is too old + # (more that one day older than asked) + next if file_mtime($file) < $spool_fetch_epoch - (3600 *24); + + print STDERR "opening $file\n" if $ENV{DEBUG}; + my $fh = new IO::File( ($file =~ m/\.gz$/) ? "gunzip < $file |" : $file); + next unless $fh; + + $_ = <$fh>; chomp; + my @headers = split(/\t/); + + # Ignore 3 first fields (Object Name, Epoch & Owner Array Name) + shift @headers; shift @headers; shift @headers; + + my $nb_headers = $#headers; + LINE: while(<$fh>) { + chomp; + my @row = split(/\t/, $_); + my $object_name = shift @row; + my $epoch = shift @row; + my $owner_array_name = shift @row; + + # Ignore if too old + next if ($epoch <= $spool_fetch_epoch); + + # Don't do too much work : 4h each time is enough + $first_epoch ||= $epoch; + next if $epoch > $first_epoch + 60 * 60 * 4; + + + # Store Values + for (my $idx = 0; $idx < $nb_headers; $idx ++) { + my $value = $row[$idx]; + my $field_name = $headers[$idx]; + + # Ignore empty values + next unless (defined $value && $value ne ""); + + # Ignore non numeric values + next unless $value =~ m/^[0-9.]+$/; + + # Ignore Optimal/NonOptimal valuse + next unless ($fieldset{$field_name} || $field_name !~ /[oO]ptimal/); + $fieldset{$field_name} = 1 unless $fieldset{$field_name}; + + if ($ENV{DEBUG}) { + no warnings; + print "object_name:$object_name, field_name:$field_name, epoch:$epoch, value:$value\n"; + } + + + $values->{$object_name}->{$field_name} .= "$epoch:$value "; + $nb_epochs ++; + } + } + } + + # Don't emit anything if the file didn't contain any useful value + next unless $nb_epochs; + + # Restitution MultiGraph + print <{$object_names->[0]} }; + for my $field (@fields) { + my $graph_name = hash_field_name($field); + print "multigraph san.$category.$graph_name\n"; + print "graph_title $field\n"; + + for my $object_name (@$object_names) { + my $label = &$convert_to_label($object_name); + my $field_name = hash_field_name(&$convert_to_field($object_name)); + print "$field_name.label $label\n"; + print "$field_name.info $object_name\n"; + for my $value (split(/ /, $values->{$object_name}->{$field})) { + print $field_name , ".value " , $value , "\n"; + } + } + } +} + +sub hash_field_name +{ + my $name = shift; + $name = lc($name); + $name =~ s/[^a-z0-9]+/_/g; + $name =~ s/^_//; + $name =~ s/_$//; + return $name; +} + +sub trim +{ + my $line = shift; + $line =~ s/^ +//; + $line =~ s/ +$//; + return $line; +} + +sub file_mtime +{ + my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size, $atime,$mtime,$ctime,$blksize,$blocks) = stat(shift); + return $mtime; +} + +__DATA__ + +sb + + +multigraph san.cpu +graph_title Usage CPU +cpu_sp_a.value $epoch:$cpu_sp_a +cpu_sp_b.value $epoch:$cpu_sp_b +multigraph san.resptime +graph_title Response Time +resp_time_a.value $epoch:$resp_time_a +resp_time_b.value $epoch:$resp_time_b +multigraph san.iops +graph_title IOPS +read_io_a.value $epoch:$read_io_a +write_io_a.value $epoch:$write_io_a +read_io_b.value $epoch:$read_io_b +write_io_b.value $epoch:$write_io_b +multigraph san.io +graph_title IO +read_mb_a.value $epoch:$read_mb_a +write_mb_a.value $epoch:$write_mb_a +read_mb_b.value $epoch:$read_mb_b +write_mb_b.value $epoch:$write_mb_b +multigraph san.cache +graph_title Cache +cache_mb_flush_a.value $epoch:$cache_mb_flush_a +write_cache_mb_flush_a.value $epoch:$write_cache_mb_flush_a +cache_mb_flush_b.value $epoch:$cache_mb_flush_b +write_cache_mb_flush_b.value $epoch:$write_cache_mb_flush_b +EOF +; + } +} + +print ".\n"; + +my %MONTHS = get_months(); +sub convert_to_epoch +{ + # converts "05/12/2011 03:57" to EPOCH + my ($date, $time) = split(/ /); + my ($mday, $mon, $year) = split(/\//, $date); + my ($hour, $min, $sec) = split(/:/, $time); + $sec ||= 0; + + use Time::Local; + $mon = $MONTHS{lc($mon)} unless $mon =~ m/\d+/; + my $epoch = timelocal($sec,$min,$hour,$mday,$mon-1,$year); + return $epoch; +} + +sub get_months { + return ( + "jan" => 0, + "fev" => 1, + "mar" => 2, + "apr" => 3, + "may" => 4, + "jun" => 5, + "jul" => 6, + "aug" => 7, + "sep" => 8, + "oct" => 9, + "nov" => 10, + "dec" => 11, + ); +} + +sub trim { + shift; + s/^\s+//; + s/\s+$//; + return $_; +} + +__DATA__ + +05/12/2011 03:57 + + + print <{"SP A"}->{"Utilization (%)"} +cpu_sp_b.value $values->{"SP B"}->{"Utilization (%)"} +multigraph san.bw +graph_title Total Bandwidth (MB/s) +iops_a.label Total Throughput (IO/s) for SP A +iops_b.label Total Throughput (IO/s) for SP B +iops_a.value $values->{"SP A"}->{"Total Throughput (IO/s)"} +iops_b.value $values->{"SP B"}->{"Total Throughput (IO/s)"} +multigraph san.iops +graph_title Total Throughput (IO/s) +iops_a.label Total Throughput (IO/s) for SP A +iops_b.label Total Throughput (IO/s) for SP B +iops_a.value $values->{"SP A"}->{"Total Throughput (IO/s)"} +iops_b.value $values->{"SP B"}->{"Total Throughput (IO/s)"} +EOF +; diff --git a/tools/munin-node-from-hell/README.rst b/tools/munin-node-from-hell/README.rst index 5731e589..9cc61d74 100644 --- a/tools/munin-node-from-hell/README.rst +++ b/tools/munin-node-from-hell/README.rst @@ -10,8 +10,8 @@ stuff we do at http://hostedmunin.com/ . Use it as you feel fit :) Current features controlled via config file: * Respond slowly or never to queries. -* Have plugins that always are in warning or alarm. -* Extensive number of plugins. +* Have plugins that always are in warning or critical. +* Extensive number of plugins running at once. * Run on multiple ports at the same time, to test huge amounts of clients. @@ -21,8 +21,8 @@ Usage munin-node-from-hell takes two arguments; the mode and which config file to use. Mode is either --run or --muninconf. -This software is meant to run as an ordinary unix user, please don't run -it as root without some thought. +This software is meant to run as an ordinary Unix user, please don't run +it as root. You probably want: diff --git a/tools/munin-node-from-hell/basic.conf b/tools/munin-node-from-hell/basic.conf new file mode 100644 index 00000000..64c83add --- /dev/null +++ b/tools/munin-node-from-hell/basic.conf @@ -0,0 +1,15 @@ +# +# Initialise plugins that test the basic functions of Munin. +# +# + +[instance:basic] +pluginprofile = basic +port = 4000 + +[pluginprofile:basic] +plugins = always_warning, always_critical, graph_area + +[base] +# when building an example config with --muninconf, what hostname to output. +hostname = localhost diff --git a/tools/munin-node-from-hell/muninnode-from-hell b/tools/munin-node-from-hell/muninnode-from-hell index 187c4138..30b05473 100755 --- a/tools/munin-node-from-hell/muninnode-from-hell +++ b/tools/munin-node-from-hell/muninnode-from-hell @@ -1,4 +1,5 @@ #!/usr/bin/python +# .- coding: utf-8 -. # # Artificial munin node that behaves in all the ways you would like # ordinary nodes _not_ to behave. @@ -84,25 +85,90 @@ class tarpit(MuninPlugin): modules["tarpit"] = tarpit() class always_warning(MuninPlugin): + conftext = """graph_title Always in LEVEL +graph_vlabel Level +graph_scale no +graph_info A simple graph that is always in LEVEL +graph_category always_LEVEL +generic.label Level +generic.info Level usually above warning level +generic.warning 5 +generic.critical 10""" + def fetch(self, conf): return "generic.value 10" def config(self, conf): - return """graph_title Always in warning -graph_vlabel Level -graph_scale no -graph_info A simple graph that is always in warning or alarm -graph_category active_notification -generic.label Level -generic.info Level usually above warning level -generic.warn 5 -generic.crit 10""" + return self.conftext.replace("LEVEL","warning") modules["always_warning"] = always_warning() -class always_alarm(always_warning): +class always_critical(always_warning): def fetch(self, conf): return "generic.value 20" -modules["always_alarm"] = always_alarm() + + def config(self, conf): + return self.conftext.replace("LEVEL","critical") +modules["always_critical"] = always_critical() + +class graph_area(MuninPlugin): + "A plugin that uses STACK and AREA. From proc_pri. Use: testing the grapher" + def fetch(self, conf): + return """high.value 3 +low.value 2 +locked.value 1""" + + def config(self, conf): + return """graph_title AREA and STACK +graph_order low high locked +graph_category graphtest +graph_info This graph shows nuber of processes at each priority +graph_args --base 1000 -l 0 +graph_vlabel Number of processes +high.label high priority +high.draw STACK +high.info The number of high-priority processes (tasks) +low.label low priority +low.draw AREA +low.info The number of low-priority processes (tasks) +locked.label locked in memory +locked.draw STACK +locked.info The number of processes that have pages locked into memory (for real-time and custom IO) +""" +modules["graph_area"] = graph_area() + +class utf8_graphcat(MuninPlugin): + "A plugin with a graph category which has UTF-8 in it" + def fetch(self, conf): + load = open("/proc/loadavg", "r").read() + load, rest = load.split(" ", 1) + load = float(load) + return "apples.value %.2f" % load + + def config(self, conf): + return """graph_title Example UTF-8 graph +graph_vlabel apples +graph_category foo™ +apples.label apples +graph_info Apples eaten +apples.info Apples eaten""" +modules["utf8_graphcat"] = utf8_graphcat() + +class utf8_graphname(MuninPlugin): + "A plugin with a UTF-8 name" + def fetch(self, conf): + load = open("/proc/loadavg", "r").read() + load, rest = load.split(" ", 1) + load = float(load) + return "apples.value %.2f" % load + + def config(self, conf): + return """graph_title Example UTF-8 graph +graph_vlabel apples +graph_category system +apples.label apples +graph_info Apples eaten +apples.info Apples eaten""" +modules["utf8_™graphname"] = utf8_graphname() class ArgumentTCPserver(SocketServer.ThreadingTCPServer): @@ -122,7 +188,7 @@ class MuninHandler(SocketServer.StreamRequestHandler): """ def handle(self): - print "%s: Connection from %s:%s. server args is %s" \ + if self.server.args.get("verbose"): print "%s: Connection from %s:%s. server args is %s" \ % (self.server.args["name"], self.client_address[0], self.client_address[1], self.server.args) # slow path hostname = self.server.args["name"] @@ -198,15 +264,19 @@ def start_servers(instances): def usage(): - print "Usage: %s [--run] [--muninconf] " % sys.argv[0] + print "Usage: %s [--run] [--verbose] [--muninconf] " % sys.argv[0] def main(): if len(sys.argv) <= 2: usage() sys.exit(1) + verbose = False + if "--verbose" in sys.argv: + verbose = True + config = ConfigParser.RawConfigParser() - config.read(sys.argv[2]) + config.read(sys.argv[-1]) instancekeys = [ key for key in config.sections() if key.startswith("instance:") ] servers = {} @@ -260,6 +330,8 @@ def main(): instanceconfig[k] = v instanceconfig["plugins"] = plugins + if "--verbose" in sys.argv: + instanceconfig["verbose"] = True instanceconfig["name"] = "%s-%s" % (instancename, portinstance) instanceconfig["expanded_port"] = portinstance @@ -275,6 +347,7 @@ def main(): if "--run" in sys.argv: + if verbose: print "Starting up.." servers = start_servers(instances) try: diff --git a/tools/munin-node-from-hell/notifications.conf b/tools/munin-node-from-hell/notifications.conf index 5a7b2c91..61f05118 100644 --- a/tools/munin-node-from-hell/notifications.conf +++ b/tools/munin-node-from-hell/notifications.conf @@ -12,7 +12,7 @@ port = 3000 #port = 4940 #sleepyness = 30 [pluginprofile:notif] -plugins = always_warning, always_alarm +plugins = always_warning, always_critical [base] # when building an example config with --muninconf, what hostname to output. diff --git a/tools/munin-node-from-hell/tarpit.conf b/tools/munin-node-from-hell/tarpit.conf index 3a0995de..ecc6cc29 100644 --- a/tools/munin-node-from-hell/tarpit.conf +++ b/tools/munin-node-from-hell/tarpit.conf @@ -6,7 +6,7 @@ pluginprofile = tarpit port = 3000 [pluginprofile:tarpit] -plugins = tarpit, load, locks +plugins = load, locks, tarpit, load, locks [pluginprofile:base] plugins = load, locks, locks, load, load, locks, locks, load, load, load