From c2eeee3b8820cd1a45fa5591aa46e9b09f8321ea Mon Sep 17 00:00:00 2001 From: Steve Schnepp Date: Thu, 9 Feb 2012 18:55:13 +0100 Subject: [PATCH 01/42] - indentation --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 9907bc0f..84652e99 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,13 @@ -This is the repository for all user contributed stuff +# This is the repository for all user contributed stuff -# contrib/plugins/ - 3rd-party plugins +## 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 +## contrib/templates/ - 3rd-party templates Feel free to update templates here, or even to create new ones. @@ -19,7 +19,7 @@ It should serves as a base for small editions that can be resynced in SVN trunk, * don't copy the whole template * directly edit files in this directory -# contrib/tools/ - 3rd-party tools +## 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. From c87e34beea0a49bfcba7cd25940c364e897b4736 Mon Sep 17 00:00:00 2001 From: Azelphur Date: Tue, 28 Feb 2012 02:58:55 +0000 Subject: [PATCH 02/42] Added game module --- plugins/game/game | 216 ++++++++++++++++++++++++++++++++++++ plugins/game/munin-game.ini | 28 +++++ 2 files changed, 244 insertions(+) create mode 100644 plugins/game/game create mode 100644 plugins/game/munin-game.ini diff --git a/plugins/game/game b/plugins/game/game new file mode 100644 index 00000000..db6f485f --- /dev/null +++ b/plugins/game/game @@ -0,0 +1,216 @@ +#!/usr/bin/php + $value) { + if ($section != 'settings') + $servers[$section] = array($value['game'], $value['address'], $value['port']); + } + + // Create a new GameQ object and pass it a list of servers + $gq = new GameQ(); + $gq->addServers($servers); + + // Set timeout from the config file + $gq->setOption('timeout', $ini_array['settings']['timeout']); + $gq->setOption('sock_count', $ini_array['settings']['sock_count']); + $gq->setOption('sock_start', $ini_array['settings']['sock_start']); + + // This filter makes sure a subset of data is always available, next to the normal data + $gq->setFilter('normalise'); + + // Send request(s) + $results = $gq->requestData(); + + return $results; +} + +// Parse command line arguments if required. +if (isset($_SERVER['argv'][1])) { + if ($_SERVER['argv'][1] == 'config') { + // Load our config.ini + $ini_array = parse_ini_file($config, true); + + // Load games.ini so we can show pretty game names + $games = parse_ini_file('gameq/GameQ/games.ini', true); + + // Query the game servers + $results = queryServers($ini_array); + // Cache the query in the state file + $fp = fopen($state, 'w+') or die("I could not open state file."); + fwrite($fp, serialize($results)); + fclose($fp); + + + // Loop through each server, printing graphs. + foreach ($results as $name => $server) { + // If sub graphs and the game total graphs are enabled, make this be a sub-graph. + if ($ini_array['settings']['show_game_total'] && $ini_array['settings']['enable_sub_graphs']) + $machine_name = "gameserver_" . $ini_array[$name]['game'] . ".$name"; + else + $machine_name = "gameserver_" . $ini_array[$name]['game'] . "_$name"; + $title = $ini_array[$name]['name'] . " players"; + $info = "The number of players connected to the " . $games[$ini_array[$name]['game']]['name'] . " server"; + printMultigraph($ini_array, $machine_name, $title, $info, $server['gq_maxplayers']); + } + + // Game total graphs. + if ($ini_array['settings']['show_game_total']) { + $game_total_max = array(); + + // Count players connected to each game + foreach ($results as $name => $server) { + if (!isset($game_total_max[$ini_array[$name]['game']])) + $game_total_max[$ini_array[$name]['game']] = $server['gq_maxplayers']; + else + $game_total_max[$ini_array[$name]['game']] += $server['gq_maxplayers']; + } + + // Print all the game total graphs. + foreach ($game_total_max as $game => $total) + { + $machine_name = "gameserver_" . $game; + $title = "Total " . $games[$game]['name'] . " players"; + $info = "Total players connected to all " . $games[$game]['name'] . " servers"; + printMultigraph($ini_array, $machine_name, $title, $info, $total); + } + } + + // Global total graph + if ($ini_array['settings']['show_global_total']) { + $total_max = 0; + // Add up all the players connected to all the servers + foreach ($results as $name => $server) { + $total_max += $server['gq_maxplayers']; + } + + printMultigraph($ini_array, "gameserver", "Total Players", "Total players connected to all servers", $total_max); + } + } + if ($_SERVER['argv'][1] == 'autoconf') { + if (!@include("gameq/GameQ.php")) + p("no (GameQ Library not found)"); + elseif (!file_exists($config)) + p("no ($config not found)"); + else + p("yes"); + } + die(); +} + +// No command line arguments, print values. + +// Load our config.ini +$ini_array = parse_ini_file($config, true); + +// Load games.ini so we can show pretty game names +$games = parse_ini_file('gameq/GameQ/games.ini', true); + +$results = unserialize(file_get_contents($state)); + +// Print individual game values +foreach ($results as $name => $server){ + if ($ini_array['settings']['show_game_total'] && $ini_array['settings']['enable_sub_graphs']) + $machine_name = "gameserver_" . $ini_array[$name]['game'] . ".$name"; + else + $machine_name = "gameserver_" . $ini_array[$name]['game'] . "_$name"; + printValue($machine_name, $server['gq_numplayers']); +} + +// Print game total values +if ($ini_array['settings']['show_game_total']) { + $game_total = array(); + foreach ($results as $name => $server) { + // Add up counts for the total graph + if (!isset($game_total_max[$ini_array[$name]['game']])) + $game_total[$ini_array[$name]['game']] = $server['gq_numplayers']; + else + $game_total[$ini_array[$name]['game']] += $server['gq_numplayers']; + } + foreach ($game_total as $game => $total) + { + $machine_name = "gameserver_" . $game; + printValue($machine_name, $total); + } +} + +// Are global totals enabled? +if ($ini_array['settings']['show_global_total']) { + $total = 0; + foreach ($results as $name => $server) { + $total += $server['gq_numplayers']; + } + printValue("gameserver", $total); +} + + +?> diff --git a/plugins/game/munin-game.ini b/plugins/game/munin-game.ini new file mode 100644 index 00000000..d03d1d0a --- /dev/null +++ b/plugins/game/munin-game.ini @@ -0,0 +1,28 @@ +[settings] +timeout = 1000 ; Timeout in ms when attempting to connect to servers +sock_count = 64 ; Maximum number of sockets that are used simultaneously +sock_start = 0 ; Start of port range to use + +; Note that changing any of the below options may change your graph names, which may reset your statistics. +show_global_total = True ; Show a graph that shows the total players connected to all servers? +show_game_total = True ; Show a graph that shows the total players connected to all servers of the same type? +enable_sub_graphs = True ; Make each game be a sub-graph of it's game total graph. + +; The following variables allow you to override the style of graphs. +; The server graphs are named gameserver_gamename.machinename, for example gameserver_tf2.myamazingserver +; The game total graphs are named gameserver_gamename, for example gameserver_tf2 +; The global total graph is named gameserver. +;graph_name_colour = 000000 ; Override the color of graph_name, for example gameserver_et.myetserver_color +;graph_name_draw = LINE1 ; Override the draw method of graph_name, for example gameserver_ + +[mytf2server] ; Machine name of the server +name = "My TF2 Server" ; Display name of the server +game = tf2 ; Game type, see GameQ/games.ini inside the GameQ library for a list of valid options. +address = 1.2.3.4 ; Server IP or hostname. +port = 27015 ; Server port + +[myetserver] +name = "My Enemy Territory Server" +game = et +address = 1.2.3.4 +port = 27960 From a9cd70c4ce5b692a1797fccb95eb9e7e87d04b3a Mon Sep 17 00:00:00 2001 From: Kenyon Ralph Date: Mon, 27 Feb 2012 23:29:24 -0800 Subject: [PATCH 03/42] move game plugin to game directory --- plugins/{game => games}/game | 0 plugins/{game => games}/munin-game.ini | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename plugins/{game => games}/game (100%) rename plugins/{game => games}/munin-game.ini (100%) diff --git a/plugins/game/game b/plugins/games/game similarity index 100% rename from plugins/game/game rename to plugins/games/game diff --git a/plugins/game/munin-game.ini b/plugins/games/munin-game.ini similarity index 100% rename from plugins/game/munin-game.ini rename to plugins/games/munin-game.ini From 8e3033a0fa903a7d92e178d17d044efe09f25162 Mon Sep 17 00:00:00 2001 From: Kenyon Ralph Date: Mon, 27 Feb 2012 23:51:52 -0800 Subject: [PATCH 04/42] remove plugins included in the main munin distribution --- plugins/system/buddyinfo | 146 ----- plugins/system/cpu | 974 ---------------------------------- plugins/system/cpu_by_process | 135 ----- plugins/system/cpufreq | 55 -- plugins/system/cpufreq-avg | 144 ----- plugins/system/cpufreq-info | 100 ---- plugins/system/cpufreq_ | 193 ------- plugins/system/cpuspeed | 34 -- plugins/system/cpuspeed2 | 45 -- plugins/system/cpuspeed_sane | 60 --- plugins/system/uptime | 50 -- plugins/system/users | 30 -- plugins/system/usersv2 | 61 --- 13 files changed, 2027 deletions(-) delete mode 100755 plugins/system/buddyinfo delete mode 100755 plugins/system/cpu delete mode 100755 plugins/system/cpu_by_process delete mode 100755 plugins/system/cpufreq delete mode 100755 plugins/system/cpufreq-avg delete mode 100755 plugins/system/cpufreq-info delete mode 100755 plugins/system/cpufreq_ delete mode 100755 plugins/system/cpuspeed delete mode 100755 plugins/system/cpuspeed2 delete mode 100755 plugins/system/cpuspeed_sane delete mode 100755 plugins/system/uptime delete mode 100755 plugins/system/users delete mode 100755 plugins/system/usersv2 diff --git a/plugins/system/buddyinfo b/plugins/system/buddyinfo deleted file mode 100755 index fe8b454d..00000000 --- a/plugins/system/buddyinfo +++ /dev/null @@ -1,146 +0,0 @@ -#! /usr/bin/perl -w - -=head1 NAME - -buddyinfo - Plugin to monitor memory fragmentation on Linux systems. - -=head1 APPLICABLE SYSTEMS - -Linux 2.6 - -=head1 CONFIGURATION - -None needed. - -=head1 INTERPRETATION - -Linux manages virtual memory on a page granularity. There are some operations -however that require physically contiguous pages to be allocated by the kernel. -Such allocations may fail if the memory gets fragmented and even though there -are enough pages free, but they are not contiguous. - -This plugin monitors the amount of contiguous areas, called higher order pages. -The order means the exponent of two of the size of the area, so order 2 means -2^2 = 4 pages. - -=head1 SEE ALSO - -See C in the Linux source tree for the -description of the buddyinfo file. - -=head1 MAGIC MARKERS - - #%# family=manual - #%# capabilities=autoconf - -=head1 AUTHOR - -Gábor Gombás - -=head1 LICENSE - -GPLv2 or later - -=cut - -use strict; -use Munin::Plugin; -use POSIX; -use Carp; - -need_multigraph(); - -if ($ARGV[0] and $ARGV[0] eq 'autoconf') { - if (-f "/proc/buddyinfo") { - print "yes\n"; - } - else { - print "no (/proc/buddyinfo is missing)\n"; - } - exit 0; -} - -# The most common page size is 4k, but it is not universal -my $pagesize = POSIX::sysconf(&POSIX::_SC_PAGESIZE) / 1024; - -my $zones = {}; - -open(FH, '< /proc/buddyinfo') - or croak "Failed to open '/proc/buddyinfo': $!"; -while (my $line = ) { - chomp $line; - - $line =~ m/Node (\d+), zone\s+(\S+)\s+(\S.*)$/; - my $name = "Node $1, zone $2"; - my @cnt = split(/ +/, $3); - $zones->{$name} = \@cnt; -} -close FH; - -my $totals = []; -foreach my $zone (keys %$zones) { - for my $i (0 .. $#{$zones->{$zone}}) { - $totals->[$i] += $zones->{$zone}->[$i] - } -} - -sub do_config { - print "multigraph buddyinfo\n"; - print "graph_title Memory fragmentation\n"; - print "graph_args --base 1024 --lower-limit 0\n"; - print "graph_vlabel pages free\n"; - print "graph_category system\n"; - print "graph_info This graph shows the number of free pages of different size\n"; - print "graph_order " . join(' ', map { "order$_" } (0 .. $#{$totals})) . "\n"; - for my $i (0 .. $#{$totals}) { - print "order$i.label Order $i\n"; - print "order$i.info Number of order $i (" . ($pagesize * 2 ** $i) . " KiB) pages\n"; - print "order$i.type GAUGE\n"; - print "order$i.draw LINE2\n"; - print "order$i.min 0\n"; - } - for my $zone (sort keys %$zones) { - my $zoneid = $zone; - $zoneid = clean_fieldname($zone); - - print "multigraph buddyinfo.$zoneid\n"; - print "graph_title Memory fragmentation in $zone\n"; - print "graph_args --base 1024 --lower-limit 0\n"; - print "graph_vlabel pages free\n"; - print "graph_category system\n"; - print "graph_info This graph shows the number of free pages in $zone\n"; - print "graph_order " . - join(' ', map { "order$_" } (0 .. $#{$zones->{$zone}})) . "\n"; - for my $i (0 .. $#{$zones->{$zone}}) { - print "order$i.label Order $i\n"; - print "order$i.info Number of order $i (" . - ($pagesize * 2 ** $i) . " KiB) pages\n"; - print "order$i.type GAUGE\n"; - print "order$i.draw LINE2\n"; - print "order$i.min 0\n"; - } - } -} - -sub do_fetch { - print "multigraph buddyinfo\n"; - for my $i (0 .. $#{$totals}) { - print "order$i.value " . $totals->[$i] . "\n"; - } - for my $zone (sort keys %$zones) { - my $zoneid = $zone; - $zoneid =~ tr/ ,/__/; - - print "multigraph buddyinfo.$zoneid\n"; - for my $i (0 .. $#{$zones->{$zone}}) { - print "order$i.value " . $zones->{$zone}->[$i] . "\n"; - } - } -} - -if ($ARGV[0] and $ARGV[0] eq 'config') { - do_config(); - exit 0; -} - -do_fetch(); diff --git a/plugins/system/cpu b/plugins/system/cpu deleted file mode 100755 index 20d43dcf..00000000 --- a/plugins/system/cpu +++ /dev/null @@ -1,974 +0,0 @@ -#!/usr/bin/perl -w -# -*- perl -*- - -=head1 NAME - -cpu - Plugin to monitor CPU usage and frequencies. - -=head1 APPLICABLE SYSTEMS - -All Linux systems, but read below section - -=head1 CONFIGURATION - -The plugin automatically selects which graphics drawing. -Charts related to the frequencies of processors depends on the settings of the kernel: -Power management and ACPI options -> CPU Frequency scaling -> CPU frequency translation statistics -> Advanced statistics - -=head2 WARNING AND CRITICAL SETTINGS - -You can set warning and critical levels for each of the data -series the plugin reports. The following environment variables are -used as default for all fields: - - env.warning - env.critical - -But each field (system user nice etc...) can be controlled separately: -For example: - - env.system_warning 70 - env.user_warning 70 - env.idle_critical 1 - -Also each field of each cpu can be controlled separately -For example: - - env.cpu1_system_warning 70 - env.cpu0_user_warning 70 - env.cpu0_idle_critical 1 - -Algoritm is easy: for example current graph limit is env.cpu0_idle_critical if defined env.cpu0_idle_critical or env.idle_critical if defined env.idle_critical -or env.critical if defined env.critical. Or no limit - -=head1 INTERPRETATION - -The plugin shows each cpu usage in percent, shows the CPU frequency, -frequency shift frequencies, the percentage of use of frequencies - -=head1 MAGIC MARKERS - - #%# family=auto - #%# capabilities=autoconf - - -=head1 VERSION - - 2.0 - -=head1 BUGS - -none known - -=head1 AUTHOR - -Gorlow Maxim aka Sheridan (email and jabber) - -=head1 LICENSE - -GPLv2 - -=cut - -use strict; -use warnings; -use Munin::Plugin; -use Data::Dumper; -my @cnames = qw(user nice system idle iowait irq softirq steal guest); -my $stat_file = "/proc/stat"; -my $freq_path = "/sys/devices/system/cpu"; -my $limits = {}; -my $cpuinfo = {}; $cpuinfo->{'cpu_count'} = 0; -my @stat_file_content = (); -my $freq_mul = 1000; # CPU frequency multiplier from kHz to Hz - - - -my $graphs = -{ - 'cpu_utilisation' => # multigraph - { - 'title' => 'CPU:t: utilisation', - 'args' => '--base 1000 -r --lower-limit 0 --upper-limit 100', - 'vlabel' => '%', - 'scale' => 'no', - 'info' => 'This graph shows how CPU:t: time is spent :i:' - }, - 'cpu_all' => # single - { - 'title' => 'All CPU utilisation', - 'args' => '--base 1000 -r --lower-limit 0 --upper-limit 100', - 'vlabel' => '%', - 'scale' => 'no', - 'info' => 'This graph shows how CPU time is spent on each processor', - 'category' => 'cpu' - }, - 'cpu_freq_trans' => # multi - { - 'title' => 'CPU frequency transitions', - 'args' => '--base 1000', - 'vlabel' => 'count', - 'scale' => 'no', - 'info' => 'This graph shows CPU transitions of each processor', - }, - 'cpu_freq' => # child of cpu_freq_trans - { - 'title' => 'CPU:t: frequency (total)', - 'args' => '--base 1000 -r --lower-limit 0 --upper-limit 100', - 'vlabel' => '% of total', - 'info' => 'This graph shows CPU:t: frequency :i:', - 'category' => 'cpu' - }, - 'cpu_freq_ps' => # child of cpu_freq_trans - { - 'title' => 'CPU:t: frequency (per secund)', - 'args' => '--base 1000 -r --lower-limit 0 --upper-limit 100', - 'vlabel' => '% per secund', - 'info' => 'This graph shows CPU:t: frequency per secund from last update :i:', - 'category' => 'cpu' - }, - 'cpu_freq_trans_table' => # child of cpu_freq_trans - { - 'title' => 'CPU:t: frequency switches (total)', - 'args' => '--base 1000 -r --lower-limit 0 --upper-limit 100', - 'vlabel' => '% of total', - 'scale' => 'no', - 'info' => 'This graph shows CPU:t: frequency switches :i:', - }, - 'cpu_freq_trans_table_ps' => # child of cpu_freq_trans - { - 'title' => 'CPU:t: frequency switches (per secund)', - 'args' => '--base 1000 -r --lower-limit 0 --upper-limit 100', - 'vlabel' => '% per secund', - 'scale' => 'no', - 'info' => 'This graph shows CPU:t: frequency switches per secund from last update :i:', - } -}; - -my $transparent = 'CC'; -my $fields = -{ - 'user' => - { - 'label' => 'User', - 'info' => 'Normal processes executing in user mode', - 'type' => 'GAUGE', - 'draw' => 'AREASTACK', - 'min' => 0, - 'max' => 100 - }, - 'nice' => - { - 'label' => 'Nice', - 'info' => 'Niced processes executing in user mode', - 'type' => 'GAUGE', - 'draw' => 'AREASTACK', - 'min' => 0, - 'max' => 100 - }, - 'system' => - { - 'label' => 'System', - 'info' => 'Processes executing in kernel mode', - 'type' => 'GAUGE', - 'draw' => 'AREASTACK', - 'min' => 0, - 'max' => 100 - }, - 'idle' => - { - 'label' => 'Idle', - 'info' => 'Twiddling thumbs', - 'type' => 'GAUGE', - 'draw' => 'AREASTACK', - 'colour'=> 'FFFFDD'.$transparent, - 'min' => 0, - 'max' => 100 - }, - 'iowait' => - { - 'label' => 'I/O wait', - 'info' => 'Waiting for I/O to complete', - 'type' => 'GAUGE', - 'draw' => 'AREASTACK', - 'min' => 0, - 'max' => 100 - }, - 'irq' => - { - 'label' => 'IRQ', - 'info' => 'Servicing interrupts', - 'type' => 'GAUGE', - 'draw' => 'AREASTACK', - 'min' => 0, - 'max' => 100 - }, - 'softirq' => - { - 'label' => 'Software IRQ', - 'info' => 'Servicing software interrupts', - 'type' => 'GAUGE', - 'draw' => 'AREASTACK', - 'min' => 0, - 'max' => 100 - }, - 'steal' => - { - 'label' => 'Steal', - 'info' => 'Involuntary wait', - 'type' => 'GAUGE', - 'draw' => 'AREASTACK', - 'min' => 0, - 'max' => 100 - }, - 'guest' => - { - 'label' => 'Guest', - 'info' => 'Running a guest', - 'type' => 'GAUGE', - 'draw' => 'AREASTACK', - 'min' => 0, - 'max' => 100 - }, - 'cpu_util' => - { - 'label' => ':t:', - 'info' => ':t: utilisation', - 'type' => 'GAUGE', - 'draw' => 'LINE0.5', - 'min' => 0, - 'max' => 100 - }, - 'freq_percent' => - { - 'label' => 'CPU:t: frequency', - 'info' => 'CPU:t: frequency percent', - 'type' => 'GAUGE', - 'draw' => 'LINE0.5', - 'min' => 0, - 'max' => 100 - }, - 'freq_hz' => - { - 'label' => ':t:', - 'info' => 'CPU :t: frequency', - 'type' => 'GAUGE', - 'draw' => 'AREASTACK', - 'min' => 0, - 'max' => 100 - }, - 'freq_trans' => - { - 'label' => ':t:', - 'info' => ':t: frequency transitions', - 'type' => 'GAUGE', - 'draw' => 'LINE0.5', - 'min' => 0 - }, - 'freq_trans_table' => - { - 'label' => ':t:', - 'info' => ':t: frequency switch', - 'type' => 'GAUGE', - 'draw' => 'AREASTACK', - 'min' => 0, - 'max' => 100 - } - -}; - - - -# ----------------- main ---------------- -load_cpuinfo(); -need_multigraph(); -remove_unavialabled_counters(); - -if (defined($ARGV[0]) and ($ARGV[0] eq 'autoconf')) -{ - printf("%s\n", -e $stat_file ? "yes" : "no (stats not exists)"); - exit (0); -} - -if (defined($ARGV[0]) and ($ARGV[0] eq 'config')) -{ - print_config(); - exit (0); -} - -print_values(); -#print Dumper prepare_graphs_fields(); -exit(0); - -# ----------------- sub's ---------------- - -# ----------------------- trim whitespace at begin and end of string ------------ -sub trim -{ - my($string)=@_; - for ($string) { s/^\s+//; s/\s+$//; } - return $string; -} - - -my $items_exists = {}; -sub check_exists -{ - my ($t, $cpu_num) = @_[0..1]; - if (defined($cpu_num)) - { - unless (exists($items_exists->{$t}{$cpu_num})) - { - if ($t eq 'freq_hz') { $items_exists->{$t}{$cpu_num} = ( -e sprintf("%s/cpu%s/cpufreq/scaling_min_freq" , $freq_path, $cpu_num) and - -e sprintf("%s/cpu%s/cpufreq/scaling_cur_freq" , $freq_path, $cpu_num) and - -e sprintf("%s/cpu%s/cpufreq/scaling_max_freq" , $freq_path, $cpu_num)); } - elsif ($t eq 'freq_trans') { $items_exists->{$t}{$cpu_num} = -e sprintf("%s/cpu%s/cpufreq/stats/time_in_state", $freq_path, $cpu_num); } - elsif ($t eq 'freq_times') { $items_exists->{$t}{$cpu_num} = -e sprintf("%s/cpu%s/cpufreq/stats/total_trans" , $freq_path, $cpu_num); } - elsif ($t eq 'freq_ttable') { $items_exists->{$t}{$cpu_num} = -e sprintf("%s/cpu%s/cpufreq/stats/trans_table" , $freq_path, $cpu_num); } - } - return $items_exists->{$t}{$cpu_num}; - } - else - { - unless(exists($items_exists->{$t}{'total'})) - { - my $c = 0; - for (my $i=0; $i < $cpuinfo->{'cpu_count'}; $i++) { $c++ if (check_exists($t, $i)); } - $items_exists->{$t}{'total'} = $c > 0; - } - return $items_exists->{$t}{'total'}; - } -} - -# ------------------------- remove unavialable fields from graph -------------------------- -sub remove_unavialabled_counters -{ - my @cpu = split(/\s+/, (grep(/cpu\s/, get_stat_file_content()))[0]); - my $counters_count = scalar(@cpu) - 3; - @cnames = @cnames[0..$counters_count]; -} - -# ----------------------- get sysfs file content ---------------- -my $fcontents = {}; -sub get_sys_file_content -{ - my $file = $_[0]; - return 'nan' if (-z $file); - unless (exists($fcontents->{$file})) - { - open (FH, '<', $file) or die "$! $file \n"; - $fcontents->{$file} = ; - close (FH); - chomp $fcontents->{$file}; - } - return $fcontents->{$file}; -} - -# -------------------------- loading cpu info --------------------------------- -sub load_cpuinfo -{ - my $file = "/proc/cpuinfo"; - open (FH, '<', $file) or die "$! $file \n"; - my $cpu_num = -1; - for my $line () - { - chomp $line; - $cpu_num++ if $line =~ m/^processor\s+:/; - $cpuinfo->{$cpu_num}{'name'} = trim((split(/:/,$line))[1]) if $line =~ m/^model name\s+:/; - $cpuinfo->{$cpu_num}{'bogomips'} = trim((split(/:/,$line))[1]) if $line =~ m/^bogomips\s+:/; - if (not exists($cpuinfo->{$cpu_num}{'info'}) and exists($cpuinfo->{$cpu_num}{'name'}) and exists($cpuinfo->{$cpu_num}{'bogomips'})) - { - $cpuinfo->{$cpu_num}{'info'} = sprintf("[%s (%s bogomips)]", $cpuinfo->{$cpu_num}{'name'}, $cpuinfo->{$cpu_num}{'bogomips'}) ; - } - } - close (FH); - $cpuinfo->{'cpu_count'} = $cpu_num+1; -} - - -# -------------------------- loading stat file lines --------------------------------- -sub get_stat_file_content -{ - if(scalar(@stat_file_content) == 0) - { - open (FH, '<', $stat_file) or die "$! $stat_file \n"; - for () - { - next unless $_ =~ m/cpu/; - chomp $_; - push(@stat_file_content, $_); - } - close (FH); - } - return @stat_file_content; -} - -# -------------------------------- replacing strings ------------------------ -sub replace_template -{ - my ($string, $needle, $replacement) = @_[0..2]; - $string =~ s/$needle/$replacement/g; - return $string; -} - -sub replace_templates -{ - my ($src, $replacement_t, $replacement_i) = @_[0..2]; - my $dst = {}; - for my $key ( keys %{$src} ) - { - $dst->{$key} = $src->{$key}; - if($key =~ m/label|info|title/) { $dst->{$key} = replace_template($dst->{$key}, ':t:', $replacement_t); } - if($key =~ m/info|title/) { $dst->{$key} = replace_template($dst->{$key}, ':i:', $replacement_i); } - } - return $dst; -} - -sub append_order -{ - my ($pg, $graph_name, $field_name) = @_[0..2]; - $pg->{$graph_name}{'graph'}{'order'} = exists($pg->{$graph_name}{'graph'}{'order'}) ? sprintf("%s %s", $pg->{$graph_name}{'graph'}{'order'}, $field_name) : $field_name; -} - -sub append_field -{ - my ($pg, $graph_name, $field_name, $field_src, $replacement_t, $replacement_i) = @_[0..5]; - $pg->{$graph_name}{'fields'}{$field_name} = replace_templates($fields->{$field_src}, $replacement_t, $replacement_i); - append_order($pg, $graph_name, $field_name); -} - -sub append_graph -{ - my ($pg, $graph_name, $category, $graph_src, $replacement_t, $replacement_i) = @_[0..5]; - $pg->{$graph_name}{'graph'} = replace_templates($graphs->{$graph_src}, $replacement_t, $replacement_i); - $pg->{$graph_name}{'graph'}{'category'} = $category; -} - -# ---------------------------------------- preparing data for graphs ------------------------------------ -sub prepare_graphs_fields -{ - my $pg = {}; - # ------------------ cpu_utilisation ----------------------------------- - # ---------- general ---------------------- - append_graph($pg, 'cpu_utilisation', 'cpu', 'cpu_utilisation', '', ''); - for my $cname (@cnames) { append_field($pg, 'cpu_utilisation', $cname, $cname, '', ''); append_utilisation_limits($pg, 'cpu_utilisation', $cname, undef); } - if(check_exists('freq_hz')) { for (my $i = 0; $i < $cpuinfo->{'cpu_count'}; $i++) { append_field($pg, 'cpu_utilisation', sprintf("fp_%s", $i), 'freq_percent', $i, ''); } } - # ---------------- childs ------------------- - if ($cpuinfo->{'cpu_count'} > 1) - { - for (my $i=0; $i < $cpuinfo->{'cpu_count'}; $i++) - { - my $graph_name = sprintf("cpu_utilisation.cpu%s", $i); - append_graph($pg, $graph_name, sprintf("CPU %s", $i), 'cpu_utilisation', $i, $cpuinfo->{$i}{'info'}); - for my $cname (@cnames) { append_field($pg, $graph_name, $cname, $cname, '', ''); append_utilisation_limits($pg, $graph_name, $cname, $i); } - if(check_exists('freq_hz', $i)) { append_field($pg, $graph_name, 'fp', 'freq_percent', '', ''); } - } - } - - - # --------------- cpu_frequency -------------------------------------------- - if(check_exists('freq_trans')) - { - # ------------ general -------------------- - # - cpu frequency transitions - - append_graph($pg, 'cpu_frequency', 'cpu', 'cpu_freq_trans', '', ''); - for (my $i = 0; $i < $cpuinfo->{'cpu_count'}; $i++) { if (check_exists('freq_trans', $i)) { append_field($pg, 'cpu_frequency', sprintf("cpu_%s", $i), 'freq_trans', sprintf("CPU%s", $i), ''); } } - append_field($pg, 'cpu_frequency', 'total', 'freq_trans', 'Total', ''); - # ---------------- childs ------------------- - if(check_exists('freq_times')) - { - my $frequences = get_frequency_times(); - for (my $i=0; $i < $cpuinfo->{'cpu_count'}; $i++) - { - if(check_exists('freq_times', $i)) - { - # - cpu frequencyes - - my $graph_name = sprintf("cpu_frequency.percent_cpu%s", $i); - append_graph($pg, $graph_name, sprintf("CPU %s", $i), 'cpu_freq', $i, $cpuinfo->{$i}{'info'}); - for my $freq (@{$frequences->{'names'}{$i}}) { append_field($pg, $graph_name, sprintf("hz_%s", $freq), 'freq_hz', scaleNumber($freq, 'Hz'), ''); } - # - cpu frequencyes per secund - - $graph_name = sprintf("cpu_frequency.percent_ps_cpu%s", $i); - append_graph($pg, $graph_name, sprintf("CPU %s", $i), 'cpu_freq_ps', $i, $cpuinfo->{$i}{'info'}); - for my $freq (@{$frequences->{'names'}{$i}}) { append_field($pg, $graph_name, sprintf("hz_%s", $freq), 'freq_hz', scaleNumber($freq, 'Hz'), ''); } - } - } - } - if(check_exists('freq_ttable')) - { - my $f_table = get_frequency_trans_table(); - for (my $i=0; $i < $cpuinfo->{'cpu_count'}; $i++) - { - if(check_exists('freq_ttable', $i)) - { - # - cpu frequencyes table - - my $graph_name = sprintf("cpu_frequency.trans_table_cpu%s", $i); - append_graph($pg, $graph_name, sprintf("CPU %s", $i), 'cpu_freq_trans_table', $i, $cpuinfo->{$i}{'info'}); - for my $from (sort keys %{$f_table->{'values'}{$i}}) - { - for my $to (sort keys %{$f_table->{'values'}{$i}{$from}}) - { - next if ($from eq $to); - append_field($pg, $graph_name, sprintf("f_%s_t_%s", $from, $to), 'freq_trans_table', sprintf(". %9s -> %s", scaleNumber($from, 'Hz'), scaleNumber($to, 'Hz')), ''); - } - } - # - cpu frequencyes table per secund - - $graph_name = sprintf("cpu_frequency.trans_table_ps_cpu%s", $i); - append_graph($pg, $graph_name, sprintf("CPU %s", $i), 'cpu_freq_trans_table_ps', $i, $cpuinfo->{$i}{'info'}); - for my $from (sort keys %{$f_table->{'values'}{$i}}) - { - for my $to (sort keys %{$f_table->{'values'}{$i}{$from}}) - { - next if ($from eq $to); - append_field($pg, $graph_name, sprintf("f_%s_t_%s", $from, $to), 'freq_trans_table', sprintf(". %9s -> %s", scaleNumber($from, 'Hz'), scaleNumber($to, 'Hz')), ''); - } - } - } - } - } - } - - - # --------------- cpu_all ----------------------------------------- - if ($cpuinfo->{'cpu_count'} > 1) - { - append_graph($pg, 'cpu_all', 'cpu', 'cpu_all', '', ''); - for (my $i = 0; $i < $cpuinfo->{'cpu_count'}; $i++) { append_field($pg, 'cpu_all', sprintf("cpu_%s", $i), 'cpu_util', sprintf("CPU%s", $i)); } - append_field($pg, 'cpu_all', 'total', 'cpu_util', 'Combined'); - } - return $pg; -} - - -# ------------------------------------ printing limits (for utilisation graphs) ---------------- -sub append_utilisation_limits -{ - my ($pg, $graph_name, $cname, $i) = @_[0..3]; - for my $type (qw(warning critical)) - { - my $field = sprintf("%s_%s", $cname, $type); - my $cpu_field = defined($i) ? sprintf("cpu%s_%s_%s", $i, $cname, $type) : undef; - my $limit = (defined($i) and defined($limits->{'utilisation'}{$cpu_field})) ? - $limits->{'utilisation'}{$cpu_field} : - ( - defined($limits->{'utilisation'}{$field}) ? - $limits->{'utilisation'}{$field} : - ( - defined($limits->{'utilisation'}{$type}) ? - $limits->{'utilisation'}{$type} : - undef - ) - ); - if(defined($limit)) { $pg->{$graph_name}{'fields'}{$cname}{$type} = $limit; } - } -} - -# ---------------- loading limits ------------- -sub load_limits -{ - $limits->{'utilisation'}{'warning'} = $ENV{warning} || undef; - $limits->{'utilisation'}{'critical'} = $ENV{critical} || undef; - for my $cname (@cnames) - { - for my $t (qw(warning critical)) - { - my $name = sprintf("%s_%s", $cname, $t); - $limits->{'utilisation'}{$name} = $ENV{$name} || undef; - for (my $i = 0; $i <= $cpuinfo->{'cpu_count'}; $i++) - { - $name = sprintf("cpu%s_%s_%s",$i, $cname, $t); - $limits->{'utilisation'}{$name} = $ENV{$name} || undef; - } - } - } -} - -# --------------------------------- graph configs ---------------------------- -sub print_config -{ - load_limits(); - my $config = prepare_graphs_fields(); - for my $g (sort keys %{$config}) - { - printf("multigraph %s\n", $g); - for my $go (sort keys %{$config->{$g}{'graph'}}) { printf("graph_%s %s\n", $go, $config->{$g}{'graph'}{$go}); } - for my $f (sort keys %{$config->{$g}{'fields'}}) { for my $fo (sort keys %{$config->{$g}{'fields'}{$f}}) { printf("%s.%s %s\n", $f, $fo, $config->{$g}{'fields'}{$f}{$fo}); } } - print "\n"; - } -} - -# ----------------------------------- saving state data using munin -------------------- -sub save_state_data -{ - my $data = $_[0]; - my $d = Data::Dumper->new([$data]); - $d->Indent(0); - save_state($d->Dump); -} - -# -------------------------------- loading previous state data using munin ------------------- -sub restore_state_data -{ - my $VAR1; - my $states = (restore_state())[0]; - eval $states if defined $states; - return $VAR1; -} - -sub load_stats -{ - my $stats = {}; - # need to store -------------------- - $stats->{'timestamp'} = time(); - $stats->{'cpu_util'} = get_cpu_utilisation_stats() ; - $stats->{'f_trans'} = get_frequency_transitions() if check_exists('freq_trans') ; - $stats->{'f_times'} = get_frequency_times() if check_exists('freq_times') ; - $stats->{'f_ttable'} = get_frequency_trans_table() if check_exists('freq_ttable'); - - save_state_data($stats); - - # no need to store -------------------- - $stats->{'f_minmax'} = get_cpu_curr_max_freqences() if check_exists('freq_hz') ; - - - #print Dumper $stats; - return $stats; -} - -# ---------------------------------- loading cpu stats from loaded lines ------------------------ -sub get_cpu_utilisation_stats -{ - my $stats = {}; - for (my $i = 0; $i <= $cpuinfo->{'cpu_count'}; $i++) - { - my $rx = $i == $cpuinfo->{'cpu_count'} ? 'cpu' : sprintf ("cpu%s", $i); - my $cn = $i == $cpuinfo->{'cpu_count'} ? 'total' : $i; - my @tmp = split(/\s+/, (grep(/$rx\s/, get_stat_file_content()))[0]); - my $j = 1; - for my $cname (@cnames) - { - $stats->{$cn}{$cname} = $tmp[$j]; - $j++; - } - } - return $stats; -} -# ------------------ loading frequency transitions for each cpu ---------------------------- -sub get_frequency_transitions -{ - my $stats = {}; - for (my $i = 0; $i < $cpuinfo->{'cpu_count'}; $i++) - { - next unless (check_exists('freq_trans', $i)); - $stats->{$i} = get_sys_file_content(sprintf("%s/cpu%s/cpufreq/stats/total_trans", $freq_path, $i)); - } - return $stats; -} - -# ------------------ loading frequency times for each cpu ---------------------------- -sub get_frequency_times -{ - my $stat = {}; - for (my $i = 0; $i < $cpuinfo->{'cpu_count'}; $i++) - { - next unless (check_exists('freq_times', $i)); - my $total = 0; - my $file = sprintf("%s/cpu%s/cpufreq/stats/time_in_state", $freq_path, $i); - open (FH, '<', $file) or die "$! $file \n"; - for my $line () - { - chomp $line; - my ($hz, $count) = split(/\s+/, $line); - $hz = $hz*$freq_mul; - $stat->{'values'}{$i}{$hz} = $count; - push(@{$stat->{'names'}{$i}}, $hz); - $total += $count; - } - close (FH); - $stat->{'total'}{$i} = $total; - } - return $stat; -} - -# ------------------ loading current and max frequency for each cpu ---------------------------- -sub get_cpu_curr_max_freqences -{ - my $freq = {}; - for (my $i = 0; $i < $cpuinfo->{'cpu_count'}; $i++) - { - next unless (check_exists('freq_hz', $i)); - my $cpu_path = sprintf("%s/cpu%s/cpufreq", $freq_path, $i); - $freq->{'cur'}{$i} = get_sys_file_content(sprintf("%s/scaling_cur_freq", $cpu_path))*$freq_mul; - $freq->{'max'}{$i} = get_sys_file_content(sprintf("%s/scaling_max_freq", $cpu_path))*$freq_mul; - $freq->{'min'}{$i} = get_sys_file_content(sprintf("%s/scaling_min_freq", $cpu_path))*$freq_mul; - } - return $freq; -} - -sub get_frequency_trans_table -{ - my $tbl = {}; - for (my $i = 0; $i < $cpuinfo->{'cpu_count'}; $i++) - { - next unless (check_exists('freq_ttable', $i)); - my @frequences; - my $fcount = 0; - my $total = 0; - my $file = sprintf("%s/cpu%s/cpufreq/stats/trans_table", $freq_path, $i); - open (FH, '<', $file) or die "$! $file \n"; - for my $line () - { - chomp $line; - my ($left, $right) = split(/:/, $line); - next if($left =~ m/From/); - if($left =~ m/\d+/) - { - my $frequence = trim($left)*$freq_mul; - my @counters = split(/\s+/, trim($right)); - for (my $j = 0; $j<$fcount; $j++) - { - $tbl->{'values'}{$i}{$frequence}{$frequences[$j]*$freq_mul} = $counters[$j]; - $total += $counters[$j]; - } - } - else - { - @frequences = split(/\s+/, trim($right)); - $fcount = scalar(@frequences); - } - } - $tbl->{'total'}{$i} = $total; - close (FH); - } - return $tbl; -} - -sub one_second_part -{ - my ($curr, $prev, $timediff) = @_[0..2]; - #print "$prev, $curr, $timediff\n"; - return 'NaN' if ($curr < $prev or $timediff < 0); - return $curr - $prev if $timediff == 0; - return ($curr - $prev)/$timediff; -} - -sub divide -{ - my ($divider, $divident) = @_[0..1]; - return 'NaN' if $divident == 0; - return $divider/$divident; -} - -# -------------------------------- calculating fields values ------------------------------ -sub calculate -{ - my ($pstats, $cstats) = @_[0..1]; - my $result = {}; - my $timediff = $cstats->{'timestamp'} - $pstats->{'timestamp'}; - # --- cpu utilisation ---- - for my $cpu (keys %{$cstats->{'cpu_util'}}) - { - # ------ calculating 1% - $result->{'cpu_util'}{'1%'}{$cpu} = 0; - for my $cname (@cnames) - { - $result->{'cpu_util'}{'diff'}{$cpu}{$cname} = one_second_part($cstats->{'cpu_util'}{$cpu}{$cname}, $pstats->{'cpu_util'}{$cpu}{$cname}, $timediff); - $result->{'cpu_util'}{'1%'}{$cpu} += $result->{'cpu_util'}{'diff'}{$cpu}{$cname} if $result->{'cpu_util'}{'diff'}{$cpu}{$cname} ne 'NaN'; - } - $result->{'cpu_util'}{'1%'}{$cpu} = $result->{'cpu_util'}{'1%'}{$cpu}/100; - # ------ calculating used percents - $result->{'cpu_util'}{'used'}{$cpu} = 0; - for my $cname (@cnames) - { - $result->{'cpu_util'}{'%'}{$cpu}{$cname} = divide($result->{'cpu_util'}{'diff'}{$cpu}{$cname}, $result->{'cpu_util'}{'1%'}{$cpu}); - next if $cname eq 'idle'; - $result->{'cpu_util'}{'used'}{$cpu} += $result->{'cpu_util'}{'%'}{$cpu}{$cname} if $result->{'cpu_util'}{'%'}{$cpu}{$cname} ne 'NaN'; - } - } - # ------ freq min max ---- - if (check_exists('freq_hz')) - { - for (my $i = 0; $i < $cpuinfo->{'cpu_count'}; $i++) - { - $result->{'f_minmax'}{'%'}{$i} = divide($cstats->{'f_minmax'}{'cur'}{$i} - $cstats->{'f_minmax'}{'min'}{$i}, - (($cstats->{'f_minmax'}{'max'}{$i} - $cstats->{'f_minmax'}{'min'}{$i}) / 100 )) if (check_exists('freq_hz', $i)); - } - } - # ---- freq trans ---- - if (check_exists('freq_trans')) - { - $result->{'f_trans'}{'total'} = 0; - for (my $i = 0; $i < $cpuinfo->{'cpu_count'}; $i++) - { - if(check_exists('freq_trans', $i)) - { - $result->{'f_trans'}{$i} = one_second_part($cstats->{'f_trans'}{$i}, $pstats->{'f_trans'}{$i}, $timediff); - $result->{'f_trans'}{'total'} += $result->{'f_trans'}{$i} if $result->{'f_trans'}{$i} ne 'NaN'; - } - } - } - # -- freq times --- - if (check_exists('freq_times')) - { - for (my $i = 0; $i < $cpuinfo->{'cpu_count'}; $i++) - { - if (check_exists('freq_times', $i)) - { - my $oneprc = $cstats->{'f_times'}{'total'}{$i}/100; - my $ps_total = 0; - for my $hz (@{$cstats->{'f_times'}{'names'}{$i}}) - { - $result->{'f_times'}{$i}{$hz} = divide($cstats->{'f_times'}{'values'}{$i}{$hz}, $oneprc); - $cstats->{'f_times'}{'%_ps'}{$i}{$hz} = one_second_part($cstats->{'f_times'}{'values'}{$i}{$hz}, $pstats->{'f_times'}{'values'}{$i}{$hz}, $timediff); - $ps_total += $cstats->{'f_times'}{'%_ps'}{$i}{$hz}; - } - $ps_total = $ps_total/100; - for my $hz (@{$cstats->{'f_times'}{'names'}{$i}}) - { - $result->{'f_times_ps'}{$i}{$hz} = divide($cstats->{'f_times'}{'%_ps'}{$i}{$hz}, $ps_total); - } - } - } - } - # ------- freq trans table --- - if (check_exists('freq_ttable')) - { - for (my $i = 0; $i < $cpuinfo->{'cpu_count'}; $i++) - { - if (check_exists('freq_ttable', $i)) - { - my $oneprc = $cstats->{'f_ttable'}{'total'}{$i}/100; - my $ps_total = 0; - for my $from (keys %{$cstats->{'f_ttable'}{'values'}{$i}}) - { - for my $to (keys %{$cstats->{'f_ttable'}{'values'}{$i}{$from}}) - { - next if ($from eq $to); - $result->{'f_ttable'}{$i}{$from}{$to} = divide($cstats->{'f_ttable'}{'values'}{$i}{$from}{$to}, $oneprc); - $cstats->{'f_ttable'}{'%_ps'}{$i}{$from}{$to} = one_second_part($cstats->{'f_ttable'}{'values'}{$i}{$from}{$to}, $pstats->{'f_ttable'}{'values'}{$i}{$from}{$to}, $timediff); - $ps_total += $cstats->{'f_ttable'}{'%_ps'}{$i}{$from}{$to}; - } - } - $ps_total = $ps_total/100; - for my $from (keys %{$cstats->{'f_ttable'}{'values'}{$i}}) - { - for my $to (keys %{$cstats->{'f_ttable'}{'values'}{$i}{$from}}) - { - next if ($from eq $to); - $result->{'f_ttable_ps'}{$i}{$from}{$to} = divide($cstats->{'f_ttable'}{'%_ps'}{$i}{$from}{$to}, $ps_total); - } - } - } - } - } - #print Dumper $result; - return $result; -} - -# ---------------------------------------- preparing values ------------------------------------ -sub prepare_graphs_values -{ - my ($data) = $_[0]; - my $pg = {}; - # ------------------ cpu_utilisation ----------------------------------- - # ---------- general ---------------------- - for my $cname (@cnames) { $pg->{'cpu_utilisation'}{$cname} = $data->{'cpu_util'}{'%'}{'total'}{$cname}; } - if(check_exists('freq_hz')) { for (my $i = 0; $i < $cpuinfo->{'cpu_count'}; $i++) { $pg->{'cpu_utilisation'}{sprintf("fp_%s", $i)} = $data->{'f_minmax'}{'%'}{$i}; } } - # ---------------- childs ------------------- - if ($cpuinfo->{'cpu_count'} > 1) - { - for (my $i=0; $i < $cpuinfo->{'cpu_count'}; $i++) - { - my $graph_name = sprintf("cpu_utilisation.cpu%s", $i); - for my $cname (@cnames) { $pg->{$graph_name}{$cname} = $data->{'cpu_util'}{'%'}{$i}{$cname}; } - if(check_exists('freq_hz', $i)) { $pg->{$graph_name}{'fp'} = $data->{'f_minmax'}{'%'}{$i}; } - } - } - - - # --------------- cpu_frequency -------------------------------------------- - if(check_exists('freq_trans')) - { - # ------------ general -------------------- - for (my $i = 0; $i < $cpuinfo->{'cpu_count'}; $i++) { $pg->{'cpu_frequency'}{sprintf("cpu_%s", $i)} = $data->{'f_trans'}{$i} if (check_exists('freq_trans', $i)); } - $pg->{'cpu_frequency'}{'total'} = $data->{'f_trans'}{'total'}; - # ---------------- childs ------------------- - if(check_exists('freq_times')) - { - for (my $i=0; $i < $cpuinfo->{'cpu_count'}; $i++) - { - if(check_exists('freq_times', $i)) - { - my $graph_name = sprintf("cpu_frequency.percent_cpu%s", $i); - for my $freq (keys %{$data->{'f_times'}{$i}}) { $pg->{$graph_name}{sprintf("hz_%s", $freq)} = $data->{'f_times'}{$i}{$freq}; } - $graph_name = sprintf("cpu_frequency.percent_ps_cpu%s", $i); - for my $freq (keys %{$data->{'f_times_ps'}{$i}}) { $pg->{$graph_name}{sprintf("hz_%s", $freq)} = $data->{'f_times_ps'}{$i}{$freq}; } - } - } - } - if(check_exists('freq_ttable')) - { - for (my $i=0; $i < $cpuinfo->{'cpu_count'}; $i++) - { - if(check_exists('freq_ttable', $i)) - { - my $graph_name = sprintf("cpu_frequency.trans_table_cpu%s", $i); - for my $from (keys %{$data->{'f_ttable'}{$i}}) - { - for my $to (keys %{$data->{'f_ttable'}{$i}{$from}}) - { - $pg->{$graph_name}{sprintf("f_%s_t_%s", $from, $to)} = $data->{'f_ttable'}{$i}{$from}{$to}; - } - } - $graph_name = sprintf("cpu_frequency.trans_table_ps_cpu%s", $i); - for my $from (keys %{$data->{'f_ttable_ps'}{$i}}) - { - for my $to (keys %{$data->{'f_ttable_ps'}{$i}{$from}}) - { - $pg->{$graph_name}{sprintf("f_%s_t_%s", $from, $to)} = $data->{'f_ttable_ps'}{$i}{$from}{$to}; - } - } - } - } - } - } - - # --------------- cpu_all -------------------------------------------- - if ($cpuinfo->{'cpu_count'} > 1) - { - for (my $i = 0; $i < $cpuinfo->{'cpu_count'}; $i++) { $pg->{'cpu_all'}{sprintf("cpu_%s", $i)} = $data->{'cpu_util'}{'used'}{$i}; } - $pg->{'cpu_all'}{'total'} = $data->{'cpu_util'}{'used'}{'total'}; - } - return $pg; -} - - - -# -------------------------------- printing values ----------------------------------- -sub print_values -{ - my $pstats = restore_state_data(); - my $cstats = load_stats(); - if (exists ($pstats->{'timestamp'})) - { - my $values = prepare_graphs_values(calculate($pstats, $cstats)); - #print Dumper $values; - for my $g (sort keys %{$values}) - { - printf("multigraph %s\n", $g); - for my $f (sort keys %{$values->{$g}}) { printf("%s.value %s\n", $f, $values->{$g}{$f}); } - print "\n"; - } - } -} - -__END__ - -- user: normal processes executing in user mode -- nice: niced processes executing in user mode -- system: processes executing in kernel mode -- idle: twiddling thumbs -- iowait: waiting for I/O to complete -- irq: servicing interrupts -- softirq: servicing softirqs -- steal: involuntary wait -- guest: running a guest diff --git a/plugins/system/cpu_by_process b/plugins/system/cpu_by_process deleted file mode 100755 index 41d33d53..00000000 --- a/plugins/system/cpu_by_process +++ /dev/null @@ -1,135 +0,0 @@ -#!/bin/sh -# -# Copyright (C) 2006 Holger Levsen -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; version 2 dated June, -# 1991. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -# -# Configuration variables -# vservers - specify the vservers to include in the graph (default: all) -# limits - if true, turn on limit graphing (default: false) -# -# NOTE: If no configuration variables are set, the defaults will be used - -# Example /etc/munin/plugin-conf.d/munin-node -# -# The first group monitors the vservers named "vserver1 vserver2 -# vserver3 vserver4" and looks to see if the resource limit has been -# breached, if so it sends a message to nagios via send_nsca, and -# sends an email to notify that this has happened. -# -# The second monitors the vservers "vserver5 vserver6 vserver7" and -# has no limit notifications turned on. -# -# The third monitors all vservers on the system, in one graph, and it has -# no limit notifications defined. -# -# You can use any combination of these to fit your needs. -# -# -# [vsrmem_group1] -# user root -# env.vservers vserver1 vserver2 vserver3 vserver4 -# env.limits 1 -# contacts nagios email -# contact.nagios.command /usr/bin/send_nsca -H your.nagios-host.here -c /etc/send_nsca.cfg -# contact.email.command mail -s "Munin-notification for ${var:group} :: ${var:host}" your@email.address.here -# -# [vsrmem_group2] -# user root -# env.vservers vserver5 vserver6 vserver7 -# env.limits 0 -# -# [vserver_rmemory] -# user root -# -# Graph Vserver RSS usage and limits -# -# Changelog -# version 0.1 - 2006 April xx - Holger Levsen -# - initial author -# version 0.2 - 2006 April 24 - Micah Anderson -# - Add dynamic arch page size determination -# - Some cleanup and clarification -# version 0.3 - 2006 May 3 - Micah Anderson -# - Add ability to group vservers via environment vars -# - Fix missing close quotes and standardize indents -# - Add limit notification -# - Update documentation to include info on groups and limits -# version 0.4 - 2006 Jun 22 - Micah Anderson -# - Fix error that results if NodeName is set to include a domain name - -#scriptname=`basename $0` -#vsname=`echo $scriptname | perl -ne '/^vserver_proc_VM_(.*)/ and print $1'` - -#if [ "$1" = "suggest" ]; then -# ls -1 /etc/vservers -# exit 0 -#elif [ -z "$vsname" ]; then -# echo "Must be used with a vserver name; try '$0 suggest'" >&2 -# exit 2 -#fi - -#xid=`cat /etc/vservers/$vsname/context` - -if [ "$1" = "config" ]; then - echo "graph_title CPU time by Process" - echo 'graph_args --base 1000 -l 0' - echo 'graph_vlabel seconds' - echo 'graph_category system' - echo "graph_info Shows CPU time used by each process name" - - # ps -eo time,comm h | perl -e ' - ps -eo pid,time,comm | perl -e ' - $junk = <>; - while (<>) - { - @a = split; - $proc = $a[2]; - $var = $proc; - $var =~ s|/.*||; - $var =~ s|\.$||; - $var =~ tr|a-zA-Z0-9|_|c; - $procs{$var} = $proc; - } - my $stack = 0; - sub draw() { return $stack++ ? "STACK" : "AREA" } - print map - { - "$_.label $procs{$_}\n" . - "$_.min 0\n" . - "$_.type DERIVE\n" . - "$_.draw " . draw() . "\n" - } - sort keys %procs; - ' - exit 0 -else - # ps -eo time,comm h | perl -e ' - ps -eo pid,time,comm | perl -e ' - $junk = <>; - while (<>) - { - @a = split; - $cpu = $a[1]; - $var = $a[2]; - $var =~ s|/.*||; - $var =~ s|\.$||; - $var =~ tr|a-zA-Z0-9|_|c; - @b = split /:/, $cpu; - $cpu = (($b[0] * 60) + $b[1]) * 60 + $b[2]; - $total{$var} += $cpu; - } - print map {"$_.value $total{$_}\n"} sort keys %total' -fi diff --git a/plugins/system/cpufreq b/plugins/system/cpufreq deleted file mode 100755 index 3f25b7c5..00000000 --- a/plugins/system/cpufreq +++ /dev/null @@ -1,55 +0,0 @@ -#!/bin/sh -# -# Plugin to monitor CPU speed on system that allow to reduce it for power saving -# -# probably only useful on laptops if you configure it to lower CPU frequency for -# less power consumptoin. This plugin works by reading from the /sys file system. -# -# by dominik dot stadler at gmx.at -# -# Magic markers (optional - only used by munin-config and some -# installation scripts): -# -#%# family=auto -#%# capabilities=autoconf - -if [ "$1" = "autoconf" ]; then - if [ -r /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq ]; then - echo yes - exit 0 - else - echo no - exit 1 - fi -fi - - -if [ "$1" = "config" ]; then - echo 'graph_title CPU speed' - echo 'graph_args --base 1000 -l 0' - echo 'graph_vlabel speed' -# echo 'graph_scale no' - echo 'graph_category system' - echo 'graph_info This graph shows the speed of the system fan.' - echo 'maxspeed.label maxspeed' - echo 'maxspeed.info The maximum speed of the CPU.' - echo 'minspeed.label minspeed' - echo 'minspeed.info The minimum speed of the CPU.' - echo 'cpuspeed.label speed' - echo 'cpuspeed.info The current speed of the CPU.' - - exit 0 -fi - -echo -n "maxspeed.value " -cat /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq -echo -n "minspeed.value " -cat /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_min_freq -echo -n "cpuspeed.value " - -# cpuinfo_cur_freq is not readable for user munin, therefore using scaling_cur_freq for now, should -# be equal information, see http://wiki.ubuntuusers.de/Prozessortaktung -# We could also configure this plugin to require root access, but I like it better this way. -#cat /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_cur_freq -cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq - diff --git a/plugins/system/cpufreq-avg b/plugins/system/cpufreq-avg deleted file mode 100755 index 0544722f..00000000 --- a/plugins/system/cpufreq-avg +++ /dev/null @@ -1,144 +0,0 @@ -#!/bin/sh -# -# Plugin to measure average CPU frequency for each CPU/core. -# -# Contributed by Mark Edwards -# -# Usage: Place in /etc/munin/plugins (or link it there using ln -s) -# -# Parameters understood: -# -# config (required) -# autoconf (optional - used by munin-config) -# -# $Log$ -# -# Revision 0.2 2010/01/04 16:37:00 medwards -# Minor bugfixes in config section -# -# Revision 0.1 2010/01/04 16:13:00 medwards -# First version -# -# -# -# Magic markers - optional - used by installation scripts and -# munin-config: -# -#%# family=auto -#%# capabilities=autoconf - -if [ "$1" = "autoconf" ]; then - if [ -r /sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state ]; then - echo yes - exit 0 - else - echo no - exit 1 - fi -fi - -cpu_count=`grep -c "^processor" /proc/cpuinfo` - -if [ "$1" = "config" ]; then - - echo 'graph_title Average CPU Frequency' - up_lim=`cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq` - low_lim=`cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq` - up_lim=`expr $up_lim \* 1000` # Convert to Hz - low_lim=`expr $low_lim \* 1000` # Convert to Hz - echo "graph_args -u $up_lim -l $low_lim -r --base 1000" - echo 'graph_vlabel Hz' - echo 'graph_category system' - cpu=0 - while [ $cpu -lt $cpu_count ] - do - echo "cpu_$cpu.label CPU $cpu" - echo "cpu_$cpu.type GAUGE" - echo "cpu_$cpu.info Hz" - cpu=`expr $cpu + 1` - done - exit 0 -fi - -# Run measurements -cpu=0 -while [ $cpu -lt $cpu_count ] -do - - time_in_state=`cat /sys/devices/system/cpu/cpu$cpu/cpufreq/stats/time_in_state` - - # Check/create statefile(s) - statefile="/var/lib/munin/plugin-state/cpufreq-avg_cpu$cpu.state" - if [ ! -r "$statefile" ] - then - echo "$time_in_state" > $statefile - if [ "$?" -ne "0" ] - then - exit ${1} - else - cpu=`expr $cpu + 1` - continue - fi - fi - state=`cat $statefile` - - # Calculated total time since last state - total_time=0 - total_time=$( - echo "$time_in_state" | { - i=0 - while read line - do - this_freq=`echo $line | awk '{ print $1; }'` - this_time=`echo $line | awk '{ print $2; }'` - this_time_state=`echo "$state" | grep $this_freq | awk '{ print $2; }'` - if [ $this_time -ge $this_time_state ] # Only measure if state is valid - then - time_diff=`expr $this_time - $this_time_state` # Calculate time since last state - total_time=`expr $total_time + $time_diff` - fi - i=`expr $i + 1` - done - echo $total_time - } - ) - - # Measure average CPU frequency if total time calculation was successful - - frequency=0 - frequency=$( - echo "$time_in_state" | { - i=0 - while read line - do - this_freq=`echo $line | awk '{ print $1; }'` - this_time=`echo $line | awk '{ print $2; }'` - this_time_state=`echo "$state" | grep $this_freq | awk '{ print $2; }'` - this_freq=`expr $this_freq \* 1000` # Convert to Hz - this_time=`expr $this_time - $this_time_state` # Calculate time since last state - if [ $total_time -gt 0 ] - then - calc=`echo "($this_time / $total_time) * $this_freq" | bc -l` - frequency=`echo "$frequency + $calc" | bc -l` - fi - i=`expr $i + 1` - done - echo $frequency - } - ) - - # Round result to an integer and return it - frequency=`echo "scale=0 ; ($frequency+0.5)/1" | bc -l` - if [ $frequency -gt 0 ] - then - echo "cpu_$cpu.value $frequency" - fi - - # Update statefile - echo "$time_in_state" > $statefile - - cpu=`expr $cpu + 1` - -done - -exit 0 \ No newline at end of file diff --git a/plugins/system/cpufreq-info b/plugins/system/cpufreq-info deleted file mode 100755 index 103827a7..00000000 --- a/plugins/system/cpufreq-info +++ /dev/null @@ -1,100 +0,0 @@ -#!/bin/bash -# -# Plugin to measure CPU frequency via cpufreq-info binary. -# This makes the plugin run on linux machines only. -# However the same goes for using sysfs directly. -# -# Contributed by Jo Schulze -# -# Config variables: -# -# -# Requires: -# cpufrequtils http://www.kernel.org/pub/linux/utils/kernel/cpufreq/cpufrequtils.html -# -# @remarks -# jo20061130 using cpufreq-info should simplify the whole thing -# jo20061202 tested on AMD K8 X2, intel Core 2 Duo -# -# $Log$ -# -# Magic markers - optional - used by installation scripts and -# munin-config: -# -#%# family=manual -#%# capabilities=autoconf - -LC_ALL="C" - -CINFOBIN="/usr/bin/cpufreq-info" - -nCPU=$(grep -c "^processor" /proc/cpuinfo) - -function getFreq () -{ - i=0 - while ((i < nCPU)); do - affc=`$CINFOBIN -a -c $i` - internal=`echo $affc | tr ' ' '_'` - cpus=( $affc ) - n=${#cpus[@]} - - freq=`$CINFOBIN -f -c $i` - echo "freq_$internal.value $freq" - - ((i += n)) - done -} - -function getAvail () -{ - i=0 - while ((i < nCPU)); do - affc=`$CINFOBIN -a -c $i` - internal=`echo $affc | tr ' ' '_'` - label=`echo $affc | tr ' ' ','` - cpus=( $affc ) - n=${#cpus[@]} - - echo "freq_$internal.label CPU $i (Core $label)" - echo "freq_$internal.type GAUGE" - echo "freq_$internal.info Hz" - - ((i += n)) - done -} - -function config () -{ -cat <) - { - $d =~ /(cpu[0-9]+)/i; - print "$1\n"; - } - exit 0; -} - -if (eval "require Storable") -{ - Storable->import(qw /lock_store lock_nstore lock_retrieve/); -} -else -{ - die "Sorry, you don't have Storable. (update your perl!)\n"; -} - -# Let's see what is requested. -my $target; -if ($0 =~ /_(cpu\d+)$/i) -{ - $target = $1; -} -else -{ - die "Error: we need to know what cpu you want, so link this plugin as cpufreq_cpu0 orso."; -} - -my $statedir = $ENV{"statedir"} || "/var/lib/munin/plugin-state/"; -my $statefile = $statedir.($ENV{"statefile"} || "/cpufreq_$target.state"); -$statefile = glob($statefile); # Make it saner, remove tildes. Not foolproof though ;) - -my $cpufreq; - -eval { $cpufreq = lock_retrieve($statefile); }; -unless(defined($cpufreq)) -{ - print STDERR "Couldn't read state file! (ignore this error on first run)\n"; - $cpufreq = {}; -} - -my $cpufreq_now = {}; -foreach my $d () -{ - unless(open(TIS, "<", "$d"."/time_in_state")) - { - die "Could not open ${d}/time_in_state: $!\n"; - } - $d =~ /(cpu[0-9]+)/i; - my $cpu = $1; - while() - { - if (/^(\d+)\s(\d+)/) - { - $cpufreq_now->{$cpu}{$1} = $2; - $cpufreq_now->{total}{$cpu} = 0 unless(defined($cpufreq_now->{$cpu})); - $cpufreq_now->{total}{$cpu} += $2; - } - } - close(TIS); -} - -# Let's figure out the percentages. -my %freq_p; -my $cpu = $target; -foreach my $freq (keys %{$cpufreq_now->{$cpu}}) -{ - my $new = $cpufreq_now->{$cpu}{$freq}; - my $old = (defined($cpufreq->{$cpu}{$freq})?$cpufreq->{$cpu}{$freq}:0); # If no old data, we average everything. - my $total = $cpufreq_now->{total}{$cpu}; - $total -= $cpufreq->{total}{$cpu} if (defined($cpufreq->{total}{$cpu})); - if (defined($total) && $total > 0) - { - my $p = ($new - $old) / $total; - $freq_p{$cpu}{$freq} = $p * 100.0; - $freq_p{avg}{$cpu} += $p * $freq; # Average speed, weighted average - } - else - { - $freq_p{$cpu}{$freq} = 0; - } -} - -if ( $ARGV[0] and $ARGV[0] eq "config" ) -{ - print "graph_title Cpu frequency usage of $target\n"; - print "graph_args --base 1000 -l 0\n"; - print "graph_vlabel CPU Frequency %\n"; - print "graph_category System\n"; - print "graph_info This graph shows information about the cpu frequency scaling of your CPU(s)\n"; - - my $count = 0; - my ($r,$g,$blue) = (0,255,0); - my $range = scalar(keys(%{$freq_p{$cpu}})); - my $step = (255+255) / ($range - 1); - # In order to let color work, let's go from green to yellow to red -- 00FF00 to FFFF00 to FF0000. 256 and 256 steps. - foreach my $freq (sort { $a <=> $b } keys %{$freq_p{$cpu}}) - { - printf "freq_%d.label %s\n", $freq, "$freq KHz"; - printf "freq_%d.info %s\n", $freq, "Time $cpu spent (percentage) running on $freq KHz"; - printf "freq_%d.type GAUGE\n", $freq; - printf "freq_%d.draw %s\n", $freq, ($count++?"STACK":"AREA"); - printf "freq_%d.colour %02X%02X%02X\n", $freq, $r,$g,$blue; - # Update color - my $s = $step; - if ($r < 255) - { - $r += $s; - if ($r > 255) - { - $s = $r - 255; - $r = 255; - $g -= $s; - } - } - else - { - $g -= $step; - $g = 0 if ($g < 0); - } - } - printf "cpuspeed_avg.label %s\n", "Average speed of cpu $cpu"; - printf "cpuspeed_avg.info %s\n", "Average speed of cpu $cpu scaled to a percentage"; - printf "cpuspeed_avg.GAUGE\n"; - printf "cpuspeed_avg.LINE2\n"; - printf "cpuspeed_avg.colour 0000FF\n"; - exit 0; -} - -# Print requested garbage. -foreach my $freq (sort { $a <=> $b } keys %{$freq_p{$cpu}}) -{ - printf "freq_%d.value %s\n", $freq, $freq_p{$cpu}{$freq}; -} -# Average speed should be as a percentage as well. So divide it by the max freq. -my $max_freq = (sort { $a <=> $b } keys %{$freq_p{$cpu}})[-1]; -if (defined($max_freq) && $max_freq > 0) -{ - printf "cpuspeed_avg.value %d\n", $freq_p{avg}{$cpu} / $max_freq * 100; -} - -# Save state! -eval { lock_nstore($cpufreq_now, $statefile) }; -if ($@) -{ - print STDERR "[$0] Error writing state file!\n"; -} - -exit 0; diff --git a/plugins/system/cpuspeed b/plugins/system/cpuspeed deleted file mode 100755 index 98766372..00000000 --- a/plugins/system/cpuspeed +++ /dev/null @@ -1,34 +0,0 @@ -#!/bin/bash -# -# Munin-plugin to monitor the CPU temperature using mbmon -# -# Used the plugin-frame from "cpuload" by Bjørn Ruberg, published under the GNU GPL -# -# Plugin monitors the cpu-frequency for both cores of my AMD 64-X2-3600+. Works fine running Debian -# GNU/Linux 4.0 (Etch). I do not know any perl and will not proceed in developing this plugin any further. -# If you wish to, feel free to do so yourself. If I am able to, I will help with any problems, contact me -# via Mail (x-stars gmx.de) or through the Debian-German-User-Mailing-List. -# -# Frank-Michael Schulze, 21-09-2007 -# Licensed under: GNU GPL - - - - -if [ "$1" = "config" ]; then - echo "graph_title CPU speed" - echo 'graph_category system' - echo "graph_info This graph shows the cpu-speed for each core, as reported by the kernel" - echo 'core0.label Core 0 speed in MHz' - echo 'core1.label Core 1 speed in MHz' - echo "core0.info Core 0 speed in MHz" - echo "core1.info Core 1 speed in MHz" - echo "core0.type GAUGE" - echo "core1.type GAUGE" - exit 0 -fi - -echo -n "core0.value " -cat /proc/cpuinfo | grep MHz | head -n 1 | cut -c 12- | awk '{ sum += $1 } END { print sum }' -echo -n "core1.value " -cat /proc/cpuinfo | grep MHz | tail -n 1 | cut -c 12- | awk '{ sum += $1 } END { print sum }' diff --git a/plugins/system/cpuspeed2 b/plugins/system/cpuspeed2 deleted file mode 100755 index 4a5801c9..00000000 --- a/plugins/system/cpuspeed2 +++ /dev/null @@ -1,45 +0,0 @@ -#!/bin/bash -# -# Munin-plugin to monitor the cpu speeds of all available cpus -# -# Armin Haaf, 4-11-2007 -# Licensed under: GNU GPL - -MAX_CORES=1024 - - -if [ "$1" = "config" ]; then - echo "graph_title CPU speed" - echo 'graph_category system' - echo "graph_info This graph shows the cpu-speed for each core, as reported by the kernel" - - i=0 - while [ $i -lt $MAX_CORES ] - do - MODEL=`cat /proc/cpuinfo | grep -A 6 "processor.*:.*$i" | grep "model name"` - if [ $? -ne 0 ] - then - break - fi - MODEL=`echo $MODEL | cut -c 12-` - echo "core$i.label Core $i speed in MHz" - echo "core$i.info Core $i speed in MHz $MODEL" - echo "core$i.type GAUGE" - i=$[$i+1] - done - exit 0 -fi - -i=0 -while true -do - cat /proc/cpuinfo | grep -A 6 "processor.*:.*$i" > /dev/null - if [ $? -ne 0 ] - then - break - fi - echo -n "core$i.value " - cat /proc/cpuinfo | grep -A 6 "processor.*:.*$i" | grep "cpu MHz" | cut -c 12- | cut -f 1 -d . - i=$[$i+1] -done - diff --git a/plugins/system/cpuspeed_sane b/plugins/system/cpuspeed_sane deleted file mode 100755 index 097b9f63..00000000 --- a/plugins/system/cpuspeed_sane +++ /dev/null @@ -1,60 +0,0 @@ -#!/bin/sh -# -# Plugin to graph cpu speed on FreeBSD -# -# Parameters: -# -# sysctl - Override path to sysctl program -# -#%# family=auto -#%# capabilities=autoconf - -sysctl=${sysctl:-/sbin/sysctl} - -if [ "$1" = "autoconf" ]; then - if [ -x ${sysctl} ]; then - ${sysctl} dev.cpu.0.freq 2>/dev/null | grep 'dev' >/dev/null 2>/dev/null - if [ "$?" = "0" ]; then - echo yes - exit 0 - fi - echo "no (dev.cpu.0.freq not found)" - exit 1 - else - echo "no (sysctl binary not found)" - exit 2 - fi -fi - -if [ "$1" = "config" ]; then - - echo 'graph_title CPU speed' - echo 'graph_args --base 1000 -l 0' - echo 'graph_vlabel speed in MHz' - echo 'graph_category system' - echo 'graph_scale no' - echo 'graph_info Current CPU speed in MHz. Available levels for the CPU:' `$sysctl -n dev.cpu.0.freq_levels|sed 's!/[0-9]*!!g;s! !, !g'` 'MHz' - - echo cpu0.label cpu0 - echo cpu0.info `$sysctl -n hw.model` Speed - - exit 0 -fi - -file=/usr/local/var/munin/plugin-state/cpuspeed - -echo -n "cpu0.value " -if find $file -mtime -300s 2>/dev/null|grep -Fq $file ; then - head -1 $file -else - $sysctl -n dev.cpu.0.freq -fi - -# Get/cache cpuspeed "later". -export sysctl file -sh -c '( - rand=$(dd if=/dev/urandom bs=1 count=1 2>/dev/null|od -A n -D) - rand=$(expr $rand \* 60 / 256 + 25) - sleep $rand - $sysctl -n dev.cpu.0.freq > $file -)&' >/dev/null 2>&1 diff --git a/plugins/system/uptime b/plugins/system/uptime deleted file mode 100755 index f12b4be7..00000000 --- a/plugins/system/uptime +++ /dev/null @@ -1,50 +0,0 @@ -#!/bin/sh -# -# Plugin to get system uptime and kernel age -# -# Magic markers - optional - used by installation scripts and -# munin-config: -# -#%# family=manual -#%# capabilities=autoconf - -if [ "$1" = "autoconf" ]; then - echo "yes" - exit 0 -fi - -if [ "$1" = "config" ]; then - - cat </dev/null 2>&1"); - my $ret = system(@who); - if ($ret == 0) { - print "yes\n"; - exit 0; - } else { - print "no\n"; - exit 1; - } -} - -if ((exists $ARGV[0]) && ($ARGV[0] eq "config")) { - print "graph_title Users Online\n"; - print "graph_args --base 1000 -l 0\n"; - print "graph_scale no\n"; - print "graph_vlabel Number of users\n"; - print "graph_category system\n"; - print "graph_info This graph shows the amount of (unique) users logged in\n"; - print "users.label total users\n"; - print "users.info something like who | wc -l\n"; - print "uusers.label unique users\n"; - print "uusers.info something like who | cut -f -1 -d ' ' | sort | uniq | wc -l\n"; - exit 0; -} - -my @who = split(/\s+/, `$who_cmd -q | head -1`); -print "users.value ".scalar(@who)."\n"; - -my %who; -$who{$_} = 1 foreach (@who); -print "uusers.value ".scalar(keys %who)."\n"; - From 6b25a2f4113a10f82a7ea7a8093b48801c68a123 Mon Sep 17 00:00:00 2001 From: Kenyon Ralph Date: Mon, 27 Feb 2012 23:56:56 -0800 Subject: [PATCH 05/42] add file_age plugin by Edward Plainview https://it.sverigedemokraterna.se/program/file_age/ --- plugins/disk/file_age | 133 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100755 plugins/disk/file_age diff --git a/plugins/disk/file_age b/plugins/disk/file_age new file mode 100755 index 00000000..29636d14 --- /dev/null +++ b/plugins/disk/file_age @@ -0,0 +1,133 @@ +#!/bin/bash + +. $MUNIN_LIBDIR/plugins/plugin.sh + +case $1 in + config) + GRAPH_ORDER="" + COUNTER=1 + while [ $COUNTER -gt 0 ]; do + FILE_PATH="file${COUNTER}_path" + + # Is the path for this file specified? + eval FILE=\$$FILE_PATH + if [ "$FILE" == "" ]; then + break; + fi + + # It is! Add it to the graphs. + GRAPH_ORDER="$GRAPH_ORDER file_$COUNTER" + + # Does this file have a specified label? + LABEL_COUNTER="file${COUNTER}_label" + eval LABEL=\$$LABEL_COUNTER + if [ "$LABEL" == "" ]; then + LABEL=`basename $FILE` + fi + + # Associated warning level? + WARNING="file${COUNTER}_warning" + eval WARNING=\$$WARNING + if [ "$WARNING" != "" ]; then + echo "file_$COUNTER.warning $WARNING" + fi + + # Associated critical level? + CRITICAL="file${COUNTER}_critical" + eval CRITICAL=\$$CRITICAL + if [ "$CRITICAL" != "" ]; then + echo "file_$COUNTER.critical $CRITICAL" + fi + + echo "file_$COUNTER.label $LABEL" + echo "file_$COUNTER.type GAUGE" + echo "file_$COUNTER.min 0" + let COUNTER=COUNTER+1 + done; + + echo "graph_order $GRAPH_ORDER" + echo "graph_title File age" + echo 'graph_args --base 1000 -l 0' + echo 'graph_vlabel seconds' + echo 'graph_category disk' + + exit 0 + ;; +esac + + +COUNTER=1 +while [ $COUNTER -gt 0 ]; do + FILE_COUNTER="file${COUNTER}_path" + eval FILE=\$$FILE_COUNTER + if [ "$FILE" == "" ]; then + break; + fi + + # If the file isn't readable, say it's zero. + if [ ! -r "$FILE" ]; then + VALUE=0 + else + VALUE=$(($(date +%s) - $(stat -c '%Y' "$FILE"))) + fi + + echo "file_$COUNTER.value $VALUE" + let COUNTER=COUNTER+1 +done; +exit + +# -*- sh -*- + +: << =cut + +=head1 NAME + +file_age - Monitors the age of files. + +=head1 CONFIGURATION + +Since there is no way for the plugin to guess which files you want monitored, you're going to have to set each file up separately. Put the following in a file in your plugin-conf.d directory. + +[file_age] + user root # May not be necessary, depending on which files you want monitored. + + env.file1_path /var/log/syslog # Mandatory, complete path to file. + env.file1_label System syslog # Optional label if you don't want the file name to be displayed. + env.file1_warning 86400 # Optional warning level. Measured in seconds. 86400 is one day of seconds. + env.file1_critical 864000 # Optional critical level. Measured in seconds. + +Continue with file2, file3, etc... + +Here, have some seconds: + + 3600 One hour + 7300 Two hours + 10800 Three hours + 21600 Six hours + 43200 Twelve hours + 86400 One day + 172800 Two days + 259200 Three days + 604800 One week + +=head1 AUTHOR + +Edward Plainview + +=head1 DONATIONS + +If you wish to donate money for this plugin, please read https://it.sverigedemokraterna.se/donera/ + +=head1 LICENSE + +GPLv3 + +=head1 MAGIC MARKERS + + #%# family=auto + +=head1 VERSION + +1.0 released 2012-02-26 + +=cut From 336ca2a97731c2b85880c5a3c3da7f8189cb62ed Mon Sep 17 00:00:00 2001 From: David Bjornsson Date: Tue, 28 Feb 2012 15:26:42 +0000 Subject: [PATCH 06/42] Fixing visibility issues in the utilization function --- plugins/zfs/zfs_stats_ | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/plugins/zfs/zfs_stats_ b/plugins/zfs/zfs_stats_ index a9d9801e..40a4072b 100755 --- a/plugins/zfs/zfs_stats_ +++ b/plugins/zfs/zfs_stats_ @@ -213,25 +213,25 @@ utilization() { echo 'max_size.label Maximum Size' echo 'max_size.draw AREA' - echo 'target_size.label Target Size' - echo 'target_size.draw AREA' echo 'size.label Size' echo 'size.draw AREA' - echo 'recently_size.label Recently Used Cache Size' - echo 'recently_size.draw AREA' - echo 'frequently_size.label Frequently Used Cache Size' - echo 'frequently_size.draw AREA' echo 'min_size.label Minimum Size' - echo 'min_size.draw AREA' + echo 'min_size.draw AREA' + echo 'target_size.label Target Size' + echo 'target_size.draw LINE1' + echo 'recently_size.label Recently Used Cache Size' + echo 'recently_size.draw LINE1' + echo 'frequently_size.label Frequently Used Cache Size' + echo 'frequently_size.draw LINE1' exit 0 else echo 'max_size.value ' $MAX_SIZE - echo 'target_size.value ' $TARGET_SIZE echo 'size.value ' $SIZE + echo 'min_size.value ' $MIN_SIZE + echo 'target_size.value ' $TARGET_SIZE echo 'recently_size.value ' $MRU_SIZE echo 'frequently_size.value ' $MFU_SIZE - echo 'min_size.value ' $MIN_SIZE fi } From b9557dd930d8f6ca7cfb52efc0038a167bb8341e Mon Sep 17 00:00:00 2001 From: Kenyon Ralph Date: Tue, 28 Feb 2012 09:36:19 -0800 Subject: [PATCH 07/42] dovecot: logfile backwards compatibility for Debian Squeeze Authored by Alessandro Fazzi . I just squashed his commits into one commit. --- plugins/mail/dovecot | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/plugins/mail/dovecot b/plugins/mail/dovecot index beb62633..03119c26 100755 --- a/plugins/mail/dovecot +++ b/plugins/mail/dovecot @@ -68,7 +68,7 @@ fi # Total Logins ###################### echo -en "login_total.value " -NEW_TOTAL=$(egrep 'dovecot.*Login' $LOGFILE | grep "`date '+%b %e'`" | sort | wc -l) +NEW_TOTAL=$(egrep '[dovecot]?.*Login' $LOGFILE | grep "`date '+%b %e'`" | sort | wc -l) OLD_TOTAL=$(grep TOTAL $STAT_FILE | cut -f2 -d '=') TOTAL=$($EXPR_BIN $NEW_TOTAL - $OLD_TOTAL) if [ $TOTAL -gt 0 ]; then @@ -80,8 +80,8 @@ echo -n ###################### # Connected Users ###################### -DISCONNECTS=$(egrep 'dovecot.*Disconnected' $LOGFILE | sort | wc -l) -CONNECTS=$(egrep 'dovecot.*Login' $LOGFILE | sort | wc -l) +DISCONNECTS=$(egrep '[dovecot]?.*Disconnected' $LOGFILE | sort | wc -l) +CONNECTS=$(egrep '[dovecot]?.*Login' $LOGFILE | sort | wc -l) DISCON=$($EXPR_BIN $CONNECTS - $DISCONNECTS) if [ $DISCON -lt 0 ]; then DISCON=0 @@ -93,7 +93,7 @@ echo -n # TLS Logins ###################### echo -en "login_tls.value " -NEW_TLS=$(egrep 'dovecot.*Login.*TLS' $LOGFILE | grep "`date '+%b %e'`" | sort | wc -l) +NEW_TLS=$(egrep '[dovecot]?.*Login.*TLS' $LOGFILE | grep "`date '+%b %e'`" | sort | wc -l) OLD_TLS=$(grep TLS $STAT_FILE | cut -f2 -d '=') TLS=$($EXPR_BIN $NEW_TLS - $OLD_TLS) if [ $TLS -gt 0 ]; then @@ -106,7 +106,7 @@ echo -n # SSL Logins ###################### echo -en "login_ssl.value " -NEW_SSL=$(egrep 'dovecot.*Login.*SSL' $LOGFILE | grep "`date '+%b %e'`" | sort | wc -l) +NEW_SSL=$(egrep '[dovecot]?.*Login.*SSL' $LOGFILE | grep "`date '+%b %e'`" | sort | wc -l) OLD_SSL=$(grep SSL $STAT_FILE | cut -f2 -d '=') SSL=$($EXPR_BIN $NEW_SSL - $OLD_SSL) if [ $SSL -gt 0 ]; then @@ -119,7 +119,7 @@ echo -n # IMAP Logins ###################### echo -en "login_imap.value " -NEW_IMAP=$(egrep 'dovecot.*imap.*Login' $LOGFILE | grep "`date '+%b %e'`" | sort | wc -l) +NEW_IMAP=$(egrep '[dovecot]?.*imap.*Login' $LOGFILE | grep "`date '+%b %e'`" | sort | wc -l) OLD_IMAP=$(grep IMAP $STAT_FILE | cut -f2 -d '=') IMAP=$($EXPR_BIN $NEW_IMAP - $OLD_IMAP) if [ $IMAP -gt 0 ]; then @@ -132,7 +132,7 @@ echo -n # POP3 Logins ###################### echo -en "login_pop3.value " -NEW_POP3=$(egrep 'dovecot.*pop3.*Login' $LOGFILE | grep "`date '+%b %e'`" | sort | wc -l) +NEW_POP3=$(egrep '[dovecot]?.*pop3.*Login' $LOGFILE | grep "`date '+%b %e'`" | sort | wc -l) OLD_POP3=$(grep POP3 $STAT_FILE | cut -f2 -d '=') POP3=$($EXPR_BIN $NEW_POP3 - $OLD_POP3) if [ $POP3 -gt 0 ]; then From 992d1c8d5ccbf4b7ace672b00af0598f6ac7250c Mon Sep 17 00:00:00 2001 From: Kenyon Ralph Date: Wed, 29 Feb 2012 15:28:16 -0800 Subject: [PATCH 08/42] remove plugin included in main munin distribution --- plugins/network/openvpn_clients | 91 --------------------------------- 1 file changed, 91 deletions(-) delete mode 100755 plugins/network/openvpn_clients diff --git a/plugins/network/openvpn_clients b/plugins/network/openvpn_clients deleted file mode 100755 index 522f5598..00000000 --- a/plugins/network/openvpn_clients +++ /dev/null @@ -1,91 +0,0 @@ -#!/usr/bin/perl -# -# Copyright (C) 2005-2007 Rodolphe Quiedeville -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; version 2 dated June, -# 1991. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -# -# If you improve this script please send your version to my email address -# with the copyright notice upgrade with your name. -# -# Munin's plugin to monitor number of clients connected to openvpn server -# -# Usage: copy or link into /etc/munin/plugins -# -# Parameters: -# -# config (required) -# autoconf (optional - used by munin-config) -# -# $Log$ -# Revision 1.2 2007/01/17 15:57:19 rodo -# Correct family -# -# Revision 1.1 2005/10/11 14:12:19 Rodolphe Quiedeville -# -# Magic markers (optinal - used by munin-config and some installation -# scripts): -# -#%# family=auto -#%# capabilities=autoconf - -use strict; - -my $statuslogfile = "/etc/openvpn/openvpn-status.log"; -my $clients = 0; - -if($ARGV[0] and $ARGV[0] eq "autoconf" ) { - if(-f $statuslogfile) { - if(-r $statuslogfile) { - print "yes\n"; - exit 0; - } else { - print "no (logfile not readable)\n"; - } - } else { - print "no (logfile not found)\n"; - } - exit 1; -} - -if ($ARGV[0] and $ARGV[0] eq "config" ){ - print "graph_title OpenVpn\n"; - print "graph_args --base 1000 -l 0\n"; - print "graph_scale yes\n"; - print "graph_vlabel clients\n"; - print "graph_category network\n"; - print "graph_info This graph shows the numbers of clients connected to openvpn server.\n"; - print "clients.label clients\n"; - print "clients.info The number of clients connected to openvpn server\n"; - exit 0; -} - -if (-f "$statuslogfile") { - open(IN, "$statuslogfile") or exit 4; - my $flagu = 0; - while() { - if(/^ROUTING TABLE$/) { - $flagu = 0; - } - if ($flagu) { - $clients = $clients + 1; - } - if(/^Common Name,Real Address,Bytes Received,Bytes Sent,Connected Since$/) { - $flagu = 1; - } - } - close(IN); -} - -print "clients.value " . $clients."\n"; From d055f33780344d06d0673127716ba714aaabe01a Mon Sep 17 00:00:00 2001 From: Kenyon Ralph Date: Wed, 29 Feb 2012 15:44:07 -0800 Subject: [PATCH 09/42] move singleton plugins to the network directory --- plugins/{networker => network}/nsr_device_writing | 0 plugins/{openvpn => network}/openvpn_multi | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename plugins/{networker => network}/nsr_device_writing (100%) rename plugins/{openvpn => network}/openvpn_multi (100%) diff --git a/plugins/networker/nsr_device_writing b/plugins/network/nsr_device_writing similarity index 100% rename from plugins/networker/nsr_device_writing rename to plugins/network/nsr_device_writing diff --git a/plugins/openvpn/openvpn_multi b/plugins/network/openvpn_multi similarity index 100% rename from plugins/openvpn/openvpn_multi rename to plugins/network/openvpn_multi From e70093d387e7e05cb6151dd8af6c424ea80337c8 Mon Sep 17 00:00:00 2001 From: Kenyon Ralph Date: Thu, 1 Mar 2012 06:43:38 -0800 Subject: [PATCH 10/42] remove processes plugin already in main munin distribution --- plugins/processes/processes | 282 ------------------------------------ 1 file changed, 282 deletions(-) delete mode 100755 plugins/processes/processes diff --git a/plugins/processes/processes b/plugins/processes/processes deleted file mode 100755 index c6986646..00000000 --- a/plugins/processes/processes +++ /dev/null @@ -1,282 +0,0 @@ -#!/bin/sh -# -# Copyright (C) 2006 Lars Strand -# -# Munin-plugin to monitor processes on Linux -# FreeBSD, OpenBSD, NetBSD, Solaris and OSX. -# -# Require munin-server version 1.2.5 or 1.3.3 (or higher). -# -# This plugin is backwards compatible with the old processes-plugins -# found on SunOS, Linux and *BSD (i.e. the history is preserved). -# -# All fields have colours associated with them which reflect the type -# of process (sleeping/idle = blue, running = green, -# stopped/zombie/dead = red, etc.) -# -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; version 2 dated June, -# 1991. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -# -# -# Parameters understood: -# -# config (required) -# autoconf (optional - used by munin-config) -# -# $Log$ -# -# -# -# Magick markers (optional - used by munin-config and som installation -# scripts): -# -#%# family=auto -#%# capabilities=autoconf - -# Search for program in $PATH unless predefined. -[ $awk ] || awk="awk" -[ $ps ] || ps="ps" - -# Find operating system -[ $OPERSYS ] || OPERSYS=`uname` || exit 1 - -if [ "$1" = "autoconf" ]; then - echo yes - exit 0 -fi - -# Define colours -RUNNABLE='22ff22' # Green -SLEEPING='0022ff' # Blue -STOPPED='cc0000' # Darker red -ZOMBIE='990000' # Darkest red -UNINTERRUPTIBLE='ffa500' # Orange -IDLE='4169e1' # Royal blue -PAGING='00aaaa' # Darker turquoise -INTERRUPT='ff00ff' # Fuchsia -LOCK='ff3333' # Lighter red -RUNNING='00ff7f' # Spring green -DEAD='ff0000' # Red -SUSPENDED='ff1493' # Deep pink -TOTAL='c0c0c0' # Silver - -# Taken from ps(1) -# R - Linux, SunOS, FreeBSD, OpenBSD, NetBSD, OSX (runable) -# S - Linux, SunOS, FreeBSD*, OpenBSD*, NetBSD*, OSX* (sleeping) -# T - Linux, SunOS, FreeBSD, OpenBSD, NetBSD, OSX (stopped) -# Z - Linux, SunOS, FreeBSD, OpenBSD, NetBSD, OSX (zombie) -# D - Linux, FreeBSD, OpenBSD, NetBSD (uninterruptible) -# I - FreeBSD, OpenBSD, NetBSD, OSX (idle) -# W - Linux*, FreeBSD* (paging/interrupt) -# L - FreeBSD (lock) -# O - SunOS (running) -# X - Linux (dead) -# U - OSX, NetBSD* (uninterruptible/suspended) -# *) Differ meaning - -if [ "$1" = "config" ]; then - echo "graph_title Processes" - echo "graph_info This graph shows the number of processes" - echo "graph_category processes" - echo "graph_args --base 1000 -l 0" - - # OS specific flags - if [ "$OPERSYS" = "Linux" ]; then - echo "graph_order sleeping stopped zombie dead paging uninterruptible runnable processes" - echo "dead.label dead" - echo "dead.draw STACK" - echo "dead.colour $DEAD" - echo "dead.info The number of dead processes." - echo "paging.label paging" - echo "paging.draw STACK" - echo "paging.colour $PAGING" - echo "paging.info The number of paging processes (<2.6 kernels only)." - - elif [ "$OPERSYS" = "SunOS" ]; then - echo "graph_order sleeping stopped zombie runnable running total" - echo "running.label running" - echo "running.draw STACK" - echo "running.colour $RUNNING" - echo "running.info The number of processes that are running on a processor." - # Be backwards compatible. - echo "total.label total" - echo "total.draw LINE1" - echo "total.colour $TOTAL" - echo "total.info The total number of processes." - - elif [ "$OPERSYS" = "FreeBSD" ]; then - echo "graph_order sleeping idle stopped zombie lock uninterruptible interrupt runnable processes" - echo "lock.label lock" - echo "lock.draw STACK" - echo "lock.colour $LOCK" - echo "lock.info The number of processes that are waiting to acquire a lock." - echo "interrupt.label interrupt" - echo "interrupt.draw STACK" - echo "interrupt.colour $INTERRUPT" - echo "interrupt.info The number of idle interrupt threads." - - elif [ "$OPERSYS" = "OpenBSD" ]; then - echo "graph_order sleeping idle stopped zombie uninterruptible runnable processes" - - elif [ "$OPERSYS" = "NetBSD" ]; then - echo "graph_order sleeping idle stopped zombie uninterruptible suspended runnable processes" - echo "suspended.label suspended" - echo "suspended.draw STACK" - echo "suspended.colour $SUSPENDED" - echo "suspended.info The number of processes that are suspended." - - elif [ "$OPERSYS" = "Darwin" ]; then - echo "graph_order sleeping idle stopped zombie uninterruptible running processes" - echo "uninterruptible.label uninterruptible" - echo "uninterruptible.draw STACK" - echo "uninterruptible.colour $UNINTERRUPTIBLE" - echo "uninterruptible.info The number of uninterruptible processes (usually IO)." - fi - - # Common flags for some OS - if [ "$OPERSYS" = "FreeBSD" ] || [ "$OPERSYS" = "OpenBSD" ] || - [ "$OPERSYS" = "NetBSD" ] || [ "$OPERSYS" = "Darwin" ]; then - echo "idle.label idle" - echo "idle.draw STACK" - echo "idle.colour $IDLE" - echo "idle.info The number of processes that are idle (sleeping for longer than about 20 seconds)." - echo "sleeping.label sleeping" - echo "sleeping.draw AREA" - echo "sleeping.colour $SLEEPING" - echo "sleeping.info The number of processes that are sleeping for less than about 20 seconds." - else - echo "sleeping.label sleeping" - echo "sleeping.draw AREA" - echo "sleeping.colour $SLEEPING" - echo "sleeping.info The number of sleeping processes." - fi - - if [ "$OPERSYS" = "Linux" ] || [ "$OPERSYS" = "FreeBSD" ] || - [ "$OPERSYS" = "OpenBSD" ] || [ "$OPERSYS" = "NetBSD" ]; then - echo "uninterruptible.label uninterruptible" - echo "uninterruptible.draw STACK" - echo "uninterruptible.colour $UNINTERRUPTIBLE" - echo "uninterruptible.info The number of uninterruptible processes (usually IO)." - fi - - # Common flags - echo "zombie.label zombie" - echo "zombie.draw STACK" - echo "zombie.colour $ZOMBIE" - echo "zombie.info The number of defunct ("zombie") processes (process terminated and parent not waiting)." - - echo "stopped.label stopped" - echo "stopped.draw STACK" - echo "stopped.colour $STOPPED" - echo "stopped.info The number of stopped or traced processes." - - echo "runnable.label runnable" - echo "runnable.draw STACK" - echo "runnable.colour $RUNNABLE" - echo "runnable.info The number of runnable processes (on the run queue)." - - if [ "$OPERSYS" != "SunOS" ]; then - # Not using 'graph_total' due to backwards compability. SunOS uses 'total'. - #echo 'graph_total total' - echo "processes.label total" - echo "processes.draw LINE1" - echo "processes.colour $TOTAL" - echo "processes.info The total number of processes." - fi - - exit 0 -fi - -if [ "$OPERSYS" = "Linux" ]; then - $ps --no-header -eo s | $awk ' -{ processes++; stat[$1]++ } -END { -print "processes.value " 0+processes; -print "uninterruptible.value " 0+stat["D"]; -print "runnable.value " 0+stat["R"]; -print "sleeping.value " 0+stat["S"]; -print "stopped.value " 0+stat["T"]; -print "paging.value " 0+stat["W"]; -print "dead.value " 0+stat["X"]; -print "zombie.value " 0+stat["Z"]; -}' - -elif [ "$OPERSYS" = "SunOS" ]; then - $ps -e -o s | $awk ' -{ total++; stat[$1]++ } -END { -print "total.value " 0+total; -print "running.value " 0+stat["O"]; -print "sleeping.value " 0+stat["S"]; -print "runnable.value " 0+stat["R"]; -print "stopped.value " 0+stat["T"]; -print "zombie.value " 0+stat["Z"]; -}' -elif [ "$OPERSYS" = "FreeBSD" ]; then - $ps -axo state= | sed -e 's/^\(.\).*/\1/' | $awk ' -{ processes++; stat[$1]++ } -END { -print "processes.value " 0+processes; -print "uninterruptible.value " 0+stat["D"]; -print "idle.value " 0+stat["I"]; -print "lock.value " 0+stat["G"]; -print "runnable.value " 0+stat["R"]; -print "sleeping.value " 0+stat["S"]; -print "stopped.value " 0+stat["T"]; -print "interrupt.value " 0+stat["W"]; -print "zombie.value " 0+stat["Z"]; -}' -elif [ "$OPERSYS" = "OpenBSD" ]; then - # First line is header. Remove it. - $ps -axo state= | sed '1d' | sed -e 's/^\(.\).*/\1/' | $awk ' -{ processes++; stat[$1]++ } -END { -print "processes.value " 0+processes; -print "uninterruptible.value " 0+stat["D"]; -print "idle.value " 0+stat["I"]; -print "runnable.value " 0+stat["R"]; -print "sleeping.value " 0+stat["S"]; -print "stopped.value " 0+stat["T"]; -print "zombie.value " 0+stat["Z"]; -}' -elif [ "$OPERSYS" = "NetBSD" ]; then - # First line is header. Remove it. - $ps -axo state= | sed '1d' | sed -e 's/^\(.\).*/\1/' | $awk ' -{ processes++; stat[$1]++ } -END { -print "processes.value " 0+processes; -print "uninterruptible.value " 0+stat["D"]; -print "idle.value " 0+stat["I"]; -print "suspended.value " 0+stat["U"]; -print "runnable.value " 0+stat["R"]; -print "sleeping.value " 0+stat["S"]; -print "stopped.value " 0+stat["T"]; -print "zombie.value " 0+stat["Z"]; -}' - -elif [ "$OPERSYS" = "Darwin" ]; then - # First line is header. Remove it. - $ps -axo state= | sed '1d' | sed -e 's/^\(.\).*/\1/' | $awk ' -{ processes++; stat[$1]++ } -END { -print "processes.value " 0+processes; -print "uninterruptible.value " 0+stat["U"]; -print "idle.value " 0+stat["I"]; -print "runnable.value " 0+stat["R"]; -print "sleeping.value " 0+stat["S"]; -print "stopped.value " 0+stat["T"]; -print "zombie.value " 0+stat["Z"]; -}' -fi From b0f2565a40e158be34670c3e0cdb009209928bc5 Mon Sep 17 00:00:00 2001 From: Lars Falk-Petersen Date: Thu, 1 Mar 2012 17:03:07 +0100 Subject: [PATCH 11/42] Small bugfixed on cleware. --- plugins/sensors/cleware | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/plugins/sensors/cleware b/plugins/sensors/cleware index ac183cc1..0e26e3b6 100755 --- a/plugins/sensors/cleware +++ b/plugins/sensors/cleware @@ -1,15 +1,26 @@ #!/bin/bash -# Written by Lars Falk-Petersen, cleware@falk-petersen.no -# Version 0.3. To be used with http://www.vanheusden.com/clewarecontrol/ +# Written by Lars Falk-Petersen +# Version 0.4. See https://github.com/ways +# To be used with http://www.vanheusden.com/clewarecontrol/ # Clewarecontrol device serial must be set in config file: # #Put the following lines in /etc/munin/plugin-conf.d/cleware -# [cleware*] -# #device serial. find it by running: clewarecontrol -l -# env.serial 7778 -# #path of clewarecontrol -# env.bin /usr/bin/clewarecontrol +# [cleware*] +# #device serial. find it by running: clewarecontrol -l +# env.serial 7778 +# #path of clewarecontrol +# env.bin /usr/bin/clewarecontrol +# +# Munin must be able to read and write to the device. +# Run the following as root: +# # chgrp munin /dev/usb/hiddev0 +# # chmod g+rw /dev/usb/hiddev0 +# +# If that works, you have to make it permanent. On Ubuntu: +# Create this file: /etc/udev/rules.d/99-hidraw-permissions.rules +# #Allow munin to read hidraws from Cleware +# SUBSYSTEM=="usb", KERNEL=="hiddev*", GROUP="munin", MODE="0660" case $1 in config) @@ -32,11 +43,10 @@ if [ ! $serial ]; then exit 1 fi -$bin -c 1 -ag > /dev/null 2>&1 +$bin -d $serial -c 1 -ag > /dev/null 2>&1 echo -n "temp.value " $bin -d $serial -b -c 1 -rt echo -n "hum.value " $bin -d $serial -b -c 1 -rh - From 933f0ea70b97bd1383b61e95554afa34787555f5 Mon Sep 17 00:00:00 2001 From: Kenyon Ralph Date: Mon, 5 Mar 2012 00:00:56 -0800 Subject: [PATCH 12/42] remove plugin in main munin distribution (jmx), and extract jmx2munin --- plugins/java/jmx2munin/.gitignore | 8 ++ plugins/java/jmx2munin/README.md | 79 ++++++++++++ .../jmx2munin.cfg/cassandra/nodes_in_cluster | 3 + plugins/java/jmx2munin/contrib/jmx2munin.sh | 55 ++++++++ plugins/java/jmx2munin/pom.xml | 121 ++++++++++++++++++ .../src/main/java/org/vafer/jmx/Enums.java | 77 +++++++++++ .../src/main/java/org/vafer/jmx/Filter.java | 9 ++ .../main/java/org/vafer/jmx/ListOutput.java | 26 ++++ .../src/main/java/org/vafer/jmx/NoFilter.java | 10 ++ .../src/main/java/org/vafer/jmx/Output.java | 9 ++ .../src/main/java/org/vafer/jmx/Query.java | 52 ++++++++ .../src/main/java/org/vafer/jmx/Value.java | 52 ++++++++ .../main/java/org/vafer/jmx/munin/Munin.java | 67 ++++++++++ .../jmx/munin/MuninAttributesFilter.java | 24 ++++ .../java/org/vafer/jmx/munin/MuninOutput.java | 93 ++++++++++++++ plugins/{jvm => java}/jstat__gccount | 0 plugins/{jvm => java}/jstat__gctime | 0 plugins/{jvm => java}/jstat__heap | 0 plugins/{jvm => java}/jvm_sun_memory | 0 plugins/{jvm => java}/jvm_sun_minorgcs | 0 plugins/{jvm => java}/jvm_sun_tenuredgcs | 0 plugins/other/jmx | Bin 15002 -> 0 bytes plugins/other/jmx2munin | Bin 12440 -> 0 bytes 23 files changed, 685 insertions(+) create mode 100644 plugins/java/jmx2munin/.gitignore create mode 100644 plugins/java/jmx2munin/README.md create mode 100644 plugins/java/jmx2munin/contrib/jmx2munin.cfg/cassandra/nodes_in_cluster create mode 100644 plugins/java/jmx2munin/contrib/jmx2munin.sh create mode 100644 plugins/java/jmx2munin/pom.xml create mode 100644 plugins/java/jmx2munin/src/main/java/org/vafer/jmx/Enums.java create mode 100644 plugins/java/jmx2munin/src/main/java/org/vafer/jmx/Filter.java create mode 100644 plugins/java/jmx2munin/src/main/java/org/vafer/jmx/ListOutput.java create mode 100644 plugins/java/jmx2munin/src/main/java/org/vafer/jmx/NoFilter.java create mode 100644 plugins/java/jmx2munin/src/main/java/org/vafer/jmx/Output.java create mode 100644 plugins/java/jmx2munin/src/main/java/org/vafer/jmx/Query.java create mode 100644 plugins/java/jmx2munin/src/main/java/org/vafer/jmx/Value.java create mode 100644 plugins/java/jmx2munin/src/main/java/org/vafer/jmx/munin/Munin.java create mode 100644 plugins/java/jmx2munin/src/main/java/org/vafer/jmx/munin/MuninAttributesFilter.java create mode 100644 plugins/java/jmx2munin/src/main/java/org/vafer/jmx/munin/MuninOutput.java rename plugins/{jvm => java}/jstat__gccount (100%) rename plugins/{jvm => java}/jstat__gctime (100%) rename plugins/{jvm => java}/jstat__heap (100%) rename plugins/{jvm => java}/jvm_sun_memory (100%) rename plugins/{jvm => java}/jvm_sun_minorgcs (100%) rename plugins/{jvm => java}/jvm_sun_tenuredgcs (100%) delete mode 100755 plugins/other/jmx delete mode 100755 plugins/other/jmx2munin diff --git a/plugins/java/jmx2munin/.gitignore b/plugins/java/jmx2munin/.gitignore new file mode 100644 index 00000000..0a99f329 --- /dev/null +++ b/plugins/java/jmx2munin/.gitignore @@ -0,0 +1,8 @@ +.DS_Store +.classpath +.project +.fatjar +target +eclipse +old +bin diff --git a/plugins/java/jmx2munin/README.md b/plugins/java/jmx2munin/README.md new file mode 100644 index 00000000..d1899db0 --- /dev/null +++ b/plugins/java/jmx2munin/README.md @@ -0,0 +1,79 @@ +# jmx2munin + +The [jmx2munin](http://github.com/tcurdt/jmx2munin) project exposes JMX MBean attributes to [Munin](http://munin-monitoring.org/). +Some of it's features: + + * strictly complies to the plugin format + * exposes composite types like Lists, Maps, Set as useful as possible + * String values can be mapped to numbers + +# How to use + +This is what the Munin script will call. So you should test this first. Of course with your parameters. This example expose all Cassandra information to Munin. + + java -jar jmx2munin.jar \ + -url service:jmx:rmi:///jndi/rmi://localhost:8080/jmxrmi \ + -query "org.apache.cassandra.*:*" + +The "url" parameters specifies the JMX URL, the query selects the MBeans (and optionally also the attributes) to expose. + + java -jar jmx2munin.jar \ + -url service:jmx:rmi:///jndi/rmi://localhost:8080/jmxrmi \ + -query "org.apache.cassandra.*:*" \ + -attribute org_apache_cassandra_db_storageservice_livenodes_size + +The script that does the actual interaction with munin you can find in the contrib section. It's the one you should link in the your Munin plugin directory. + + :/etc/munin/plugins$ ls -la cassandra_* + lrwxrwxrwx 1 root root 37 2011-04-07 19:58 cassandra_nodes_in_cluster -> /usr/share/munin/plugins/jmx2munin.sh + +In the plugin conf you point to the correct configuration + + [cassandra_*] + env.query org.apache.cassandra.*:* + + [cassandra_nodes_in_cluster] + env.config cassandra/nodes_in_cluster + +A possible configuration could look like this + + graph_title Number of Nodes in Cluster + graph_vlabel org_apache_cassandra_db_storageservice_livenodes_size + org_apache_cassandra_db_storageservice_livenodes_size.label number of nodes + +The script will extract the attributes from the config and caches the JMX results to reduce the load when showing many values. + +# More advanced + +Sometimes it can be useful to track String values by mapping them into an enum as they really describe states. To find this possible candidates you can call: + + java -jar jmx2munin.jar \ + -url service:jmx:rmi:///jndi/rmi://localhost:8080/jmxrmi \ + -query "org.apache.cassandra.*:*" \ + list + +It should output a list of possible candidates. This can now be turned into a enum configuration file: + + [org.apache.cassandra.db.StorageService:OperationMode] + 0 = ^Normal + 1 = ^Client + 2 = ^Joining + 3 = ^Bootstrapping + 4 = ^Leaving + 5 = ^Decommissioned + 6 = ^Starting drain + 7 = ^Node is drained + +Which you then can provide: + + java -jar jmx2munin.jar \ + -url service:jmx:rmi:///jndi/rmi://localhost:8080/jmxrmi \ + -query "org.apache.cassandra.*:*" \ + -enums /path/to/enums.cfg + +Now matching values get replaced by their numerical representation. On the left needs to be a unique number on the right side is a regular expression. If a string cannot be matched according to the spec "U" for "undefined" will be returned. + +# License + +Licensed under the Apache License, Version 2.0 (the "License") +You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 diff --git a/plugins/java/jmx2munin/contrib/jmx2munin.cfg/cassandra/nodes_in_cluster b/plugins/java/jmx2munin/contrib/jmx2munin.cfg/cassandra/nodes_in_cluster new file mode 100644 index 00000000..7fe323e3 --- /dev/null +++ b/plugins/java/jmx2munin/contrib/jmx2munin.cfg/cassandra/nodes_in_cluster @@ -0,0 +1,3 @@ +graph_title Number of Nodes in Cluster +graph_vlabel org_apache_cassandra_db_storageservice_livenodes_size +org_apache_cassandra_db_storageservice_livenodes_size.label number of nodes diff --git a/plugins/java/jmx2munin/contrib/jmx2munin.sh b/plugins/java/jmx2munin/contrib/jmx2munin.sh new file mode 100644 index 00000000..2ccb1841 --- /dev/null +++ b/plugins/java/jmx2munin/contrib/jmx2munin.sh @@ -0,0 +1,55 @@ +#!/bin/bash +# [cassandra_nodes_in_cluster] +# env.config cassandra/nodes_in_cluster +# env.query org.apache.cassandra.*:* + +if [ -z "$MUNIN_LIBDIR" ]; then + MUNIN_LIBDIR="`dirname $(dirname "$0")`" +fi + +if [ -f "$MUNIN_LIBDIR/plugins/plugin.sh" ]; then + . $MUNIN_LIBDIR/plugins/plugin.sh +fi + +if [ "$1" = "autoconf" ]; then + echo yes + exit 0 +fi + +if [ -z "$url" ]; then + # this is very common so make it a default + url="service:jmx:rmi:///jndi/rmi://127.0.0.1:8080/jmxrmi" +fi + +if [ -z "$config" -o -z "$query" -o -z "$url" ]; then + echo "Configuration needs attributes config, query and optinally url" + exit 1 +fi + +JMX2MUNIN_DIR="$MUNIN_LIBDIR/plugins" +CONFIG="$JMX2MUNIN_DIR/jmx2munin.cfg/$config" + +if [ "$1" = "config" ]; then + cat "$CONFIG" + exit 0 +fi + +JAR="$JMX2MUNIN_DIR/jmx2munin.jar" +CACHED="/tmp/jmx2munin" + +if test ! -f $CACHED || test `find "$CACHED" -mmin +2`; then + + java -jar "$JAR" \ + -url "$url" \ + -query "$query" \ + $ATTRIBUTES \ + > $CACHED + + echo "cached.value `date +%s`" >> $CACHED +fi + +ATTRIBUTES=`awk '/\.label/ { gsub(/\.label/,""); print $1 }' $CONFIG` + +for ATTRIBUTE in $ATTRIBUTES; do + grep $ATTRIBUTE $CACHED +done \ No newline at end of file diff --git a/plugins/java/jmx2munin/pom.xml b/plugins/java/jmx2munin/pom.xml new file mode 100644 index 00000000..2bbbd026 --- /dev/null +++ b/plugins/java/jmx2munin/pom.xml @@ -0,0 +1,121 @@ + + + 4.0.0 + + org.vafer + jmx2munin + jmx2munin + 1.0 + + Munin plugin to access JMX information + + http://github.com/tcurdt/jmx2munin + + + + tcurdt + Torsten Curdt + tcurdt at vafer.org + +1 + + + + + + Apache License 2 + http://www.apache.org/licenses/LICENSE-2.0.txt + + + + + scm:git:git://github.com:tcurdt/jmx2munin.git + scm:git:git://github.com:tcurdt/jmx2munin.git + http://github.com/tcurdt/jmx2munin/tree/master + + + + + com.beust + jcommander + 1.17 + + + + junit + junit + 4.5 + test + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.5 + 1.5 + UTF-8 + + + + org.apache.maven.plugins + maven-surefire-plugin + + never + + **/*TestCase.java + + + **/Abstract* + + true + false + + + + org.apache.maven.plugins + maven-source-plugin + 2.1 + + true + + + + create-source-jar + + jar-no-fork + + + + + + org.apache.maven.plugins + maven-shade-plugin + 1.4 + + + package + + shade + + + false + + + com.beust:jcommander + + + + + org.vafer.jmx.munin.Munin + + + + + + + + + diff --git a/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/Enums.java b/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/Enums.java new file mode 100644 index 00000000..ab5b4831 --- /dev/null +++ b/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/Enums.java @@ -0,0 +1,77 @@ +package org.vafer.jmx; + +import java.io.BufferedReader; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.TreeMap; +import java.util.regex.Pattern; + +import javax.management.ObjectName; + +public final class Enums { + + private TreeMap> sections = new TreeMap>(); + + public boolean load(String filePath) throws IOException { + BufferedReader input = null; + LinkedHashMap section = new LinkedHashMap(); + try { + input = new BufferedReader(new InputStreamReader(new FileInputStream(filePath))); + String line; + int linenr = 0; + while((line = input.readLine()) != null) { + linenr += 1; + line = line.trim(); + if (line.startsWith("#")) { + continue; + } + if (line.startsWith("[") && line.endsWith("]")) { + // new section + String id = line.substring(1, line.length() - 1); + section = new LinkedHashMap(); + sections.put(id, section); + } else { + String[] pair = line.split("="); + if (pair.length == 2) { + Integer number = Integer.parseInt(pair[0].trim()); + Pattern pattern = Pattern.compile(pair[1].trim()); + if (section.put(number, pattern) != null) { + System.err.println("Line " + linenr + ": previous definitions of " + number); + } + } + } + } + } finally { + if (input != null) { + input.close(); + } + } + return false; + } + + public static String id(ObjectName beanName, String attributeName) { + StringBuilder sb = new StringBuilder(); + sb.append(beanName.getDomain()); + sb.append('.'); + sb.append(beanName.getKeyProperty("type")); + sb.append(':'); + sb.append(attributeName); + return sb.toString(); + } + + public Number resolve(String id, String value) { + LinkedHashMap section = sections.get(id); + if (section == null) { + return null; + } + for(Map.Entry entry : section.entrySet()) { + if (entry.getValue().matcher(value).matches()) { + return entry.getKey(); + } + } + return null; + } +} diff --git a/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/Filter.java b/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/Filter.java new file mode 100644 index 00000000..e7d67a8a --- /dev/null +++ b/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/Filter.java @@ -0,0 +1,9 @@ +package org.vafer.jmx; + +import javax.management.ObjectName; + +public interface Filter { + + public boolean include(ObjectName bean, String attribute); + +} diff --git a/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/ListOutput.java b/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/ListOutput.java new file mode 100644 index 00000000..4e050faf --- /dev/null +++ b/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/ListOutput.java @@ -0,0 +1,26 @@ +package org.vafer.jmx; + +import java.util.HashSet; +import java.util.Set; + +import javax.management.ObjectName; + +public final class ListOutput implements Output { + + private final Set seen = new HashSet(); + + public void output(ObjectName beanName, String attributeName, Object value) { + Value.flatten(beanName, attributeName, value, new Value.Listener() { + public void value(ObjectName beanName, String attributeName, String value) { + final String id = Enums.id(beanName, attributeName); + if (!seen.contains(id)) { + System.out.println("[" + id + "]"); + seen.add(id); + } + } + public void value(ObjectName beanName, String attributeName, Number value) { + } + }); + } + +} diff --git a/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/NoFilter.java b/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/NoFilter.java new file mode 100644 index 00000000..6188d5c7 --- /dev/null +++ b/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/NoFilter.java @@ -0,0 +1,10 @@ +package org.vafer.jmx; + +import javax.management.ObjectName; + +public final class NoFilter implements Filter { + + public boolean include(ObjectName bean, String attribute) { + return true; + } +} diff --git a/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/Output.java b/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/Output.java new file mode 100644 index 00000000..eb9e6ca2 --- /dev/null +++ b/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/Output.java @@ -0,0 +1,9 @@ +package org.vafer.jmx; + +import javax.management.ObjectName; + +public interface Output { + + public void output(ObjectName beanName, String attributeName, Object value); + +} diff --git a/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/Query.java b/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/Query.java new file mode 100644 index 00000000..e27bc4f5 --- /dev/null +++ b/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/Query.java @@ -0,0 +1,52 @@ +package org.vafer.jmx; + +import java.io.IOException; +import java.util.Collection; + +import javax.management.AttributeNotFoundException; +import javax.management.InstanceNotFoundException; +import javax.management.IntrospectionException; +import javax.management.MBeanAttributeInfo; +import javax.management.MBeanException; +import javax.management.MBeanInfo; +import javax.management.MBeanServerConnection; +import javax.management.MalformedObjectNameException; +import javax.management.ObjectInstance; +import javax.management.ObjectName; +import javax.management.ReflectionException; +import javax.management.remote.JMXConnector; +import javax.management.remote.JMXConnectorFactory; +import javax.management.remote.JMXServiceURL; + +public final class Query { + + public void run(String url, String expression, Filter filter, Output output) throws IOException, MalformedObjectNameException, InstanceNotFoundException, ReflectionException, IntrospectionException, AttributeNotFoundException, MBeanException { + JMXConnector connector = JMXConnectorFactory.connect(new JMXServiceURL(url)); + MBeanServerConnection connection = connector.getMBeanServerConnection(); + final Collection mbeans = connection.queryMBeans(new ObjectName(expression), null); + + for(ObjectInstance mbean : mbeans) { + final ObjectName mbeanName = mbean.getObjectName(); + final MBeanInfo mbeanInfo = connection.getMBeanInfo(mbeanName); + final MBeanAttributeInfo[] attributes = mbeanInfo.getAttributes(); + for (final MBeanAttributeInfo attribute : attributes) { + if (attribute.isReadable()) { + if (filter.include(mbeanName, attribute.getName())) { + final String attributeName = attribute.getName(); + try { + output.output( + mbean.getObjectName(), + attributeName, + connection.getAttribute(mbeanName, attributeName) + ); + } catch(Exception e) { + // System.err.println("Failed to read " + mbeanName + "." + attributeName); + } + } + } + } + + } + connector.close(); + } +} diff --git a/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/Value.java b/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/Value.java new file mode 100644 index 00000000..87af5f8a --- /dev/null +++ b/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/Value.java @@ -0,0 +1,52 @@ +package org.vafer.jmx; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.management.ObjectName; + +public final class Value { + + public interface Listener { + public void value(ObjectName beanName, String attributeName, String value); + public void value(ObjectName beanName, String attributeName, Number value); + } + + public static void flatten(ObjectName beanName, String attributeName, Object value, Listener listener) { + if (value instanceof Number) { + + listener.value(beanName, attributeName, (Number) value); + + } else if (value instanceof String) { + + listener.value(beanName, attributeName, (String) value); + + } else if (value instanceof Set) { + + final Set set = (Set) value; + flatten(beanName, attributeName + ".size", set.size(), listener); + for(Object entry : set) { + flatten(beanName, attributeName + "[" + entry + "]", 1, listener); + } + + } else if (value instanceof List) { + + final List list = (List)value; + listener.value(beanName, attributeName + ".size", list.size()); + for(int i = 0; i map = (Map) value; + listener.value(beanName, attributeName + ".size", map.size()); + for(Map.Entry entry : map.entrySet()) { + flatten(beanName, attributeName + "[" + entry.getKey() + "]", entry.getValue(), listener); + } + + } else { + // System.err.println("Failed to convert " + beanName + "." + attributeName); + } + } +} diff --git a/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/munin/Munin.java b/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/munin/Munin.java new file mode 100644 index 00000000..9f1ffdc7 --- /dev/null +++ b/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/munin/Munin.java @@ -0,0 +1,67 @@ +package org.vafer.jmx.munin; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +import org.vafer.jmx.Enums; +import org.vafer.jmx.Filter; +import org.vafer.jmx.ListOutput; +import org.vafer.jmx.NoFilter; +import org.vafer.jmx.Query; + +import com.beust.jcommander.JCommander; +import com.beust.jcommander.Parameter; + +public final class Munin { + + @Parameter(description = "") + private List args = new ArrayList(); + + @Parameter(names = "-url", description = "jmx url", required = true) + private String url; + + @Parameter(names = "-query", description = "query expression", required = true) + private String query; + + @Parameter(names = "-enums", description = "file string to enum config") + private String enumsPath; + + @Parameter(names = "-attribute", description = "attributes to return") + private List attributes = new ArrayList(); + + private void run() throws Exception { + final Filter filter; + if (attributes == null || attributes.isEmpty()) { + filter = new NoFilter(); + } else { + filter = new MuninAttributesFilter(attributes); + } + + final Enums enums = new Enums(); + if (enumsPath != null) { + enums.load(enumsPath); + } + + final String cmd = args.toString().toLowerCase(Locale.US); + if ("[list]".equals(cmd)) { + new Query().run(url, query, filter, new ListOutput()); + } else { + new Query().run(url, query, filter, new MuninOutput(enums)); + } + } + + public static void main(String[] args) throws Exception { + Munin m = new Munin(); + + JCommander cli = new JCommander(m); + try { + cli.parse(args); + } catch(Exception e) { + cli.usage(); + System.exit(1); + } + + m.run(); + } +} diff --git a/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/munin/MuninAttributesFilter.java b/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/munin/MuninAttributesFilter.java new file mode 100644 index 00000000..e1a49e83 --- /dev/null +++ b/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/munin/MuninAttributesFilter.java @@ -0,0 +1,24 @@ +package org.vafer.jmx.munin; + +import java.util.HashSet; +import java.util.List; + +import javax.management.ObjectName; + +import org.vafer.jmx.Filter; + +public final class MuninAttributesFilter implements Filter { + + private final HashSet attributes = new HashSet(); + + public MuninAttributesFilter(List pAttributes) { + for (String attribute : pAttributes) { + attributes.add(attribute.trim().replaceAll("_size$", "")); + } + } + + public boolean include(ObjectName bean, String attribute) { + return attributes.contains(MuninOutput.attributeName(bean, attribute)); + } + +} diff --git a/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/munin/MuninOutput.java b/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/munin/MuninOutput.java new file mode 100644 index 00000000..9fb50b12 --- /dev/null +++ b/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/munin/MuninOutput.java @@ -0,0 +1,93 @@ +package org.vafer.jmx.munin; + +import java.text.NumberFormat; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Hashtable; +import java.util.Locale; + +import javax.management.ObjectName; + +import org.vafer.jmx.Enums; +import org.vafer.jmx.Output; +import org.vafer.jmx.Value; + +public final class MuninOutput implements Output { + + private final Enums enums; + + public MuninOutput(Enums enums) { + this.enums = enums; + } + + public static String attributeName(ObjectName bean, String attribute) { + StringBuilder sb = new StringBuilder(); + sb.append(fieldname(beanString(bean))); + sb.append('_'); + sb.append(fieldname(attribute)); + return sb.toString().toLowerCase(Locale.US); + } + + private static String fieldname(String s) { + return s.replaceAll("[^A-Za-z0-9]", "_"); + } + + private static String beanString(ObjectName beanName) { + StringBuilder sb = new StringBuilder(); + sb.append(beanName.getDomain()); + + Hashtable properties = beanName.getKeyPropertyList(); + + String keyspace = "keyspace"; + if (properties.containsKey(keyspace)) { + sb.append('.'); + sb.append(properties.get(keyspace)); + properties.remove(keyspace); + } + + String type = "type"; + if (properties.containsKey(type)) { + sb.append('.'); + sb.append(properties.get(type)); + properties.remove(type); + } + + ArrayList keys = new ArrayList(properties.keySet()); + Collections.sort(keys); + + for(String key : keys) { + sb.append('.'); + sb.append(properties.get(key)); + } + + return sb.toString(); + // return beanName.getCanonicalName(); + } + + public void output(ObjectName beanName, String attributeName, Object value) { + Value.flatten(beanName, attributeName, value, new Value.Listener() { + public void value(ObjectName beanName, String attributeName, String value) { + final Number v = enums.resolve(Enums.id(beanName, attributeName), value); + if (v != null) { + value(beanName, attributeName, v); + } else { + value(beanName, attributeName, Double.NaN); + } + } + public void value(ObjectName beanName, String attributeName, Number value) { + final String v; + + if (Double.isNaN(value.doubleValue())) { + v = "U"; + } else { + final NumberFormat f = NumberFormat.getInstance(); + f.setMaximumFractionDigits(2); + f.setGroupingUsed(false); + v = f.format(value); + } + + System.out.println(attributeName(beanName, attributeName) + ".value " + v); + } + }); + } +} \ No newline at end of file diff --git a/plugins/jvm/jstat__gccount b/plugins/java/jstat__gccount similarity index 100% rename from plugins/jvm/jstat__gccount rename to plugins/java/jstat__gccount diff --git a/plugins/jvm/jstat__gctime b/plugins/java/jstat__gctime similarity index 100% rename from plugins/jvm/jstat__gctime rename to plugins/java/jstat__gctime diff --git a/plugins/jvm/jstat__heap b/plugins/java/jstat__heap similarity index 100% rename from plugins/jvm/jstat__heap rename to plugins/java/jstat__heap diff --git a/plugins/jvm/jvm_sun_memory b/plugins/java/jvm_sun_memory similarity index 100% rename from plugins/jvm/jvm_sun_memory rename to plugins/java/jvm_sun_memory diff --git a/plugins/jvm/jvm_sun_minorgcs b/plugins/java/jvm_sun_minorgcs similarity index 100% rename from plugins/jvm/jvm_sun_minorgcs rename to plugins/java/jvm_sun_minorgcs diff --git a/plugins/jvm/jvm_sun_tenuredgcs b/plugins/java/jvm_sun_tenuredgcs similarity index 100% rename from plugins/jvm/jvm_sun_tenuredgcs rename to plugins/java/jvm_sun_tenuredgcs diff --git a/plugins/other/jmx b/plugins/other/jmx deleted file mode 100755 index 0f4820b224ba5f34b182330e99497ad827ca0956..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15002 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 From ddc46f88fd5f67204e1c65bdb665991e43d767e1 Mon Sep 17 00:00:00 2001 From: Kenyon Ralph Date: Mon, 5 Mar 2012 00:18:19 -0800 Subject: [PATCH 13/42] readd jmx (extracted) because it seems to be different from the jmx in the main munin distribution --- plugins/java/jmx/examples/java/java_cpu.conf | 22 ++++++ .../examples/java/java_process_memory.conf | 46 ++++++++++++ .../java/jmx/examples/java/java_threads.conf | 14 ++++ .../examples/tomcat/catalina_requests.conf | 16 +++++ .../jmx/examples/tomcat/catalina_threads.conf | 12 ++++ .../jmx/examples/tomcat/catalina_times.conf | 32 +++++++++ .../jmx/examples/tomcat/catalina_traffic.conf | 18 +++++ plugins/java/jmx/plugin/jmx_ | 54 ++++++++++++++ plugins/java/jmx/plugin/jmxquery.jar | Bin 0 -> 8977 bytes plugins/java/jmx/readme.txt | 66 ++++++++++++++++++ 10 files changed, 280 insertions(+) create mode 100644 plugins/java/jmx/examples/java/java_cpu.conf create mode 100644 plugins/java/jmx/examples/java/java_process_memory.conf create mode 100644 plugins/java/jmx/examples/java/java_threads.conf create mode 100644 plugins/java/jmx/examples/tomcat/catalina_requests.conf create mode 100644 plugins/java/jmx/examples/tomcat/catalina_threads.conf create mode 100644 plugins/java/jmx/examples/tomcat/catalina_times.conf create mode 100644 plugins/java/jmx/examples/tomcat/catalina_traffic.conf create mode 100755 plugins/java/jmx/plugin/jmx_ create mode 100644 plugins/java/jmx/plugin/jmxquery.jar create mode 100644 plugins/java/jmx/readme.txt diff --git a/plugins/java/jmx/examples/java/java_cpu.conf b/plugins/java/jmx/examples/java/java_cpu.conf new file mode 100644 index 00000000..1e56cd34 --- /dev/null +++ b/plugins/java/jmx/examples/java/java_cpu.conf @@ -0,0 +1,22 @@ +graph_args --upper-limit 100 -l 0 +graph_scale no +graph_title CPU Usage +graph_vlabel 1000* CPU time % +graph_category Java +graph_order java_cpu_time java_cpu_user_time + +java_cpu_time.label cpu +java_cpu_time.jmxObjectName java.lang:type=Threading +java_cpu_time.jmxAttributeName CurrentThreadCpuTime +java_cpu_time.type DERIVE +java_cpu_time.min 0 +java_cpu_time.graph yes +java_cpu_time.cdef java_cpu_time,3000000,/ + +java_cpu_user_time.label user +java_cpu_user_time.jmxObjectName java.lang:type=Threading +java_cpu_user_time.jmxAttributeName CurrentThreadUserTime +java_cpu_user_time.type DERIVE +java_cpu_user_time.min 0 +java_cpu_user_time.graph yes +java_cpu_user_time.cdef java_cpu_user_time,3000000,/ \ No newline at end of file diff --git a/plugins/java/jmx/examples/java/java_process_memory.conf b/plugins/java/jmx/examples/java/java_process_memory.conf new file mode 100644 index 00000000..ddbdc4d0 --- /dev/null +++ b/plugins/java/jmx/examples/java/java_process_memory.conf @@ -0,0 +1,46 @@ +graph_title Process Memory +graph_vlabel Bytes +graph_category Java +graph_order java_memory_nonheap_committed java_memory_nonheap_max java_memory_nonheap_used java_memory_heap_committed java_memory_heap_max java_memory_heap_used os_memory_physical os_memory_vm + +java_memory_nonheap_committed.label non-heap committed +java_memory_nonheap_committed.jmxObjectName java.lang:type=Memory +java_memory_nonheap_committed.jmxAttributeName NonHeapMemoryUsage +java_memory_nonheap_committed.jmxAttributeKey committed + +java_memory_nonheap_max.label non-heap max +java_memory_nonheap_max.jmxObjectName java.lang:type=Memory +java_memory_nonheap_max.jmxAttributeName NonHeapMemoryUsage +java_memory_nonheap_max.jmxAttributeKey max + +java_memory_nonheap_used.label non-heap used +java_memory_nonheap_used.jmxObjectName java.lang:type=Memory +java_memory_nonheap_used.jmxAttributeName NonHeapMemoryUsage +java_memory_nonheap_used.jmxAttributeKey used + +java_memory_heap_committed.label heap committed +java_memory_heap_committed.jmxObjectName java.lang:type=Memory +java_memory_heap_committed.jmxAttributeName HeapMemoryUsage +java_memory_heap_committed.jmxAttributeKey committed + +java_memory_heap_max.label heap max +java_memory_heap_max.jmxObjectName java.lang:type=Memory +java_memory_heap_max.jmxAttributeName HeapMemoryUsage +java_memory_heap_max.jmxAttributeKey max + +java_memory_heap_used.label heap used +java_memory_heap_used.jmxObjectName java.lang:type=Memory +java_memory_heap_used.jmxAttributeName HeapMemoryUsage +java_memory_heap_used.jmxAttributeKey used + +os_memory_physical.label os free mem +os_memory_physical.jmxObjectName java.lang:type=OperatingSystem +os_memory_physical.jmxAttributeName FreePhysicalMemorySize +os_memory_physical.graph no + +os_memory_vm.label os vmem committed +os_memory_vm.jmxObjectName java.lang:type=OperatingSystem +os_memory_vm.jmxAttributeName CommittedVirtualMemorySize +os_memory_vm.graph no + + diff --git a/plugins/java/jmx/examples/java/java_threads.conf b/plugins/java/jmx/examples/java/java_threads.conf new file mode 100644 index 00000000..f8b64cd4 --- /dev/null +++ b/plugins/java/jmx/examples/java/java_threads.conf @@ -0,0 +1,14 @@ +graph_title Thread Count +graph_vlabel Thread Count +graph_category Java +graph_order java_thread_count java_thread_count_peak + +java_thread_count.label count +java_thread_count.jmxObjectName java.lang:type=Threading +java_thread_count.jmxAttributeName ThreadCount + +java_thread_count_peak.label peak +java_thread_count_peak.jmxObjectName java.lang:type=Threading +java_thread_count_peak.jmxAttributeName PeakThreadCount + + diff --git a/plugins/java/jmx/examples/tomcat/catalina_requests.conf b/plugins/java/jmx/examples/tomcat/catalina_requests.conf new file mode 100644 index 00000000..acb48175 --- /dev/null +++ b/plugins/java/jmx/examples/tomcat/catalina_requests.conf @@ -0,0 +1,16 @@ +graph_title Requests Per Second +graph_vlabel requests per second +graph_category Tomcat +graph_order catalina_request_count catalina_error_count + +catalina_request_count.label requests +catalina_request_count.jmxObjectName Catalina:name=http-8080,type=GlobalRequestProcessor +catalina_request_count.jmxAttributeName requestCount +catalina_request_count.type DERIVE +catalina_request_count.min 0 + +catalina_error_count.label errors +catalina_error_count.jmxObjectName Catalina:name=http-8080,type=GlobalRequestProcessor +catalina_error_count.jmxAttributeName errorCount +catalina_error_count.type DERIVE +catalina_error_count.min 0 diff --git a/plugins/java/jmx/examples/tomcat/catalina_threads.conf b/plugins/java/jmx/examples/tomcat/catalina_threads.conf new file mode 100644 index 00000000..32ab44c2 --- /dev/null +++ b/plugins/java/jmx/examples/tomcat/catalina_threads.conf @@ -0,0 +1,12 @@ +graph_title Thread Count +graph_vlabel Thread Count +graph_category Tomcat +graph_order catalina_threads_count catalina_threads_busy + +catalina_threads_busy.label busy +catalina_threads_busy.jmxObjectName Catalina:name=http-8080,type=ThreadPool +catalina_threads_busy.jmxAttributeName currentThreadsBusy + +catalina_threads_count.label current +catalina_threads_count.jmxObjectName Catalina:name=http-8080,type=ThreadPool +catalina_threads_count.jmxAttributeName currentThreadCount diff --git a/plugins/java/jmx/examples/tomcat/catalina_times.conf b/plugins/java/jmx/examples/tomcat/catalina_times.conf new file mode 100644 index 00000000..e3790259 --- /dev/null +++ b/plugins/java/jmx/examples/tomcat/catalina_times.conf @@ -0,0 +1,32 @@ +graph_title Response Time +graph_vlabel Time, ms +graph_category Rules Engine +graph_args --upper-limit 100 -l 0 +graph_scale no +graph_category Tomcat +graph_order catalina_request_count catalina_max_time catalina_proc_time catalina_proc_tpr + +catalina_request_count.label requests +catalina_request_count.jmxObjectName Catalina:name=http-8080,type=GlobalRequestProcessor +catalina_request_count.jmxAttributeName requestCount +catalina_request_count.graph no +catalina_request_count.type DERIVE +catalina_request_count.min 0 + +catalina_proc_time.label time +catalina_proc_time.jmxObjectName Catalina:name=http-8080,type=GlobalRequestProcessor +catalina_proc_time.jmxAttributeName processingTime +catalina_proc_time.type DERIVE +catalina_proc_time.min 0 +catalina_proc_time.graph no + +catalina_proc_tpr.label avg +catalina_proc_tpr.jmxObjectName Catalina:name=http-8080,type=GlobalRequestProcessor +catalina_proc_tpr.jmxAttributeName processingTime +catalina_proc_tpr.cdef catalina_request_count,0,EQ,0,catalina_proc_time,catalina_request_count,/,IF + +catalina_max_time.label peak +catalina_max_time.jmxObjectName Catalina:name=http-8080,type=GlobalRequestProcessor +catalina_max_time.jmxAttributeName maxTime + + diff --git a/plugins/java/jmx/examples/tomcat/catalina_traffic.conf b/plugins/java/jmx/examples/tomcat/catalina_traffic.conf new file mode 100644 index 00000000..f6d9498d --- /dev/null +++ b/plugins/java/jmx/examples/tomcat/catalina_traffic.conf @@ -0,0 +1,18 @@ +graph_title Traffic +graph_vlabel Bytes rec(-)/sent(+) per second +graph_category Tomcat +graph_order catalina_bytes_received catalina_bytes_sent + +catalina_bytes_sent.label bps +catalina_bytes_sent.jmxObjectName Catalina:name=http-8080,type=GlobalRequestProcessor +catalina_bytes_sent.jmxAttributeName bytesSent +catalina_bytes_sent.type DERIVE +catalina_bytes_sent.min 0 +catalina_bytes_sent.negative catalina_bytes_received + +catalina_bytes_received.label received +catalina_bytes_received.jmxObjectName Catalina:name=http-8080,type=GlobalRequestProcessor +catalina_bytes_received.jmxAttributeName bytesReceived +catalina_bytes_received.type DERIVE +catalina_bytes_received.min 0 +catalina_bytes_received.graph no \ No newline at end of file diff --git a/plugins/java/jmx/plugin/jmx_ b/plugins/java/jmx/plugin/jmx_ new file mode 100755 index 00000000..9fe33f2f --- /dev/null +++ b/plugins/java/jmx/plugin/jmx_ @@ -0,0 +1,54 @@ +#!/bin/sh +# +# Wildcard-plugin to monitor Java JMX (http://java.sun.com/jmx)attributes. +# To monitor a # specific set of JMX attributes, +# link to this file. E.g. +# +# ln -s /usr/share/plugins/jmx_ /etc/munin/plugins/jmx_java_threads +# + +# ...will monitor Java thread count, assuming java_threads.conf file is located in +# /etc/munin/plugins folder. +# +# For Java process to be monitored, it must expose JMX remote interface. +# With Java 1.5 it can be done by adding parameters as: +# +# -Dcom.sun.management.jmxremote.port= -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false +# +# For Tomcat to be monitored, add the following line in catalina.bat script: +# set JAVA_OPTS=%JAVA_OPTS% -Dcom.sun.management.jmxremote.port= -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false +# +# By default, the plugin monitors localhost on = 1616 using URL +# service:jmx:rmi:///jndi/rmi://localhost:1616/jmxrmi +# It can be changed by specifying parameters in /etc/munin/plugin-conf.d/munin-node +# +# [jmx_*] +# env.jmxurl service:jmx:rmi:///jndi/rmi://localhost:1616/jmxrmi +# +# Read more on JMX configuring at http://java.sun.com/j2se/1.5.0/docs/guide/management/agent.html +# $Log$ +# +LINK=`readlink $0` +CONFIGNAME=`basename $0 | sed 's/^jmx_//g'`.conf +RDIR=`dirname $LINK` + +if [ "$jmxurl" = "" ]; then +SERVICE=service:jmx:rmi:///jndi/rmi://localhost:1616/jmxrmi +else +SERVICE="$jmxurl" +fi + +if [ "$jmxuser" != "" ]; then +CREDS="--user=$jmxuser --pass=$jmxpass" +fi + +# checks +test -z $CONFIGNAME || test -z "$RDIR" && exit 1 + +JMXQUERY="java -cp $RDIR/jmxquery.jar org.munin.JMXQuery --url=$SERVICE $CREDS --conf=$RDIR/$CONFIGNAME" + + +case $1 in + (config) $JMXQUERY config;; + (*) $JMXQUERY ;; +esac diff --git a/plugins/java/jmx/plugin/jmxquery.jar b/plugins/java/jmx/plugin/jmxquery.jar new file mode 100644 index 0000000000000000000000000000000000000000..5c37d4bdec25e453d0891a5d12d899f30cecd157 GIT binary patch literal 8977 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/bin/jconsole.exe and connect to +the host/port you setup in your Java process. + +Some examples are: +* standard Java JMX implementation exposes memory, threads, OS, garbage collector parameters +* Tomcat exposes multiple parameters - requests, processing time, threads, etc.. +* spring framework allows to expose Java beans parameters to JMX +* your application may expose any attributes for JMX by declaration or explicitly. +* can monitor localhost or remote processes + +-------- Installation --------- + +Pre-requsisites are: +- installed munin-node +- Java version 5 JRE + +1) Files from "plugin" folder must be copied to /usr/share/munin/plugins (or another - where your munin plugins located) +2) Make sure that jmx_ executable : chmod a+x /usr/share/munin/plugins/jmx_ +3) Copy configuration files that you want to use, from "examples" folder, into /usr/share/munin/plugins folder +4) create links from the /etc/munin/plugins folder to the /usr/share/munin/plugins/jmx_ +The name of the link must follow wildcard pattern: +jmx_, +where configname is the name of the configuration (config filename without extension), for example: +ln -s /usr/share/munin/plugins/jmx_ /etc/munin/plugins/jmx_process_memory +5) optionally specify the environment variable for JMX URL. The default URL corresponds to localhost:1616. +If you have different port listening by JMX or different hostname to monitor, specify jmxurl parameter +in /etc/munin/plugin-conf.d/munin-node: + +[jmx_*] +env.jmxurl service:jmx:rmi:///jndi/rmi://localhost:1616/jmxrmi + +-------- Check Installation --------- + +To check that all installed properly, try invoke plugins from command line, using links like: + +root@re:/etc/munin/plugins# ./jmx_java_process_memory config +graph_category Java +... +root@re:/etc/munin/plugins# ./jmx_java_process_memory +java_memory_nonheap_committed.value 35291136 +... + +If you have configured environment for jmxurl, do not forget to export it before! + +-------- Configuration Files --------- + +Folder "examples" contains configuration files for Java and Tomcat monitoring examples. +The format of configuration file is a superset of Munin plugin "config" command output +(http://munin.projects.linpro.no/wiki/protocol-config) +It has the following additions: + +.jmxObjectName JMX object name, e.g. java.lang:type=Memory +.jmxAttributeName JMX attribute name, e.g. NonHeapMemoryUsage +.jmxAttributeKey If attribute is a composed data (structure), the name of the field in structure, e.g. max + +% separates comments in file + + + From bc344d005e731314506273cb4646f89dd72f8cec Mon Sep 17 00:00:00 2001 From: Matt Stith Date: Mon, 5 Mar 2012 23:11:27 -0500 Subject: [PATCH 14/42] Switch to Ruby and MineQuery to remove an extra plugin dependency Most servers have minequery installed already, no need to also have OnlinePlayers when this is what MineQuery was built for. --- plugins/minecraft/minecraft-users | 58 +++++++++++++++++-------------- 1 file changed, 31 insertions(+), 27 deletions(-) diff --git a/plugins/minecraft/minecraft-users b/plugins/minecraft/minecraft-users index a1038962..7e4861f3 100755 --- a/plugins/minecraft/minecraft-users +++ b/plugins/minecraft/minecraft-users @@ -1,32 +1,36 @@ -#!/bin/sh +#!/usr/local/bin/ruby # Config: -# [minecraft_players -# playerfile /etc/minecraft/players.txt -# subtract true +# [minecraft_users] +# env.host awesomeserver.com +# env.port 25566 # -# playerfile - location of player list file, for example from the OnlineUsers -# plugin -# subtract - OnlineUsers has a header above the user list, set this to true -# to subtract 1 from the output to compensate -case $1 in - config) - cat <<'EOM' -graph_title Connected players -graph_vlabel players -players.label players -graph_info Number of players connected to Minecraft -graph_category Minecraft -EOM - exit 0;; -esac -echo -n "players.value " +require 'socket' -count=`wc -l ${playerfile} | cut -d' ' -f1` -if [ $subtract="true" ]; -then - echo -n "$(($count - 1))" -else - echo $count -fi +if ARGV[0] == 'config' + puts "graph_title Connected players" + puts "graph_vlabel players" + puts "players.label players" + puts "graph_info Number of players connected to Minecraft" + puts "graph_category Minecraft" + exit +end +host = ENV['host'] +host = 'localhost' unless host + +port = ENV['port'] +port = '25566' unless port + +socket = TCPSocket.new(host, port) +socket.puts "QUERY" + +response = socket.read +response = response.split("\n") + +server_port = response[0].split(" ", 2)[1].to_i +player_count = response[1].split(" ", 2)[1].to_i +max_players = response[2].split(" ", 2)[1].to_i +player_list = response[3].split(" ", 2)[1].chomp[1..-2] + +puts "players.value #{player_count}" \ No newline at end of file From 7b9893484c3127269f668bd467b0c7718a63d62d Mon Sep 17 00:00:00 2001 From: Andreas Thienemann Date: Wed, 7 Mar 2012 11:53:51 +0100 Subject: [PATCH 15/42] Add snmp__areca_ plugin I still had lying around. Needs a bit of cleanup but in the meantime it works to poll temperature, fan rpm and voltage from Areca RAID controllers. --- plugins/sensors/snmp__areca_ | 292 +++++++++++++++++++++++++++++++++++ 1 file changed, 292 insertions(+) create mode 100755 plugins/sensors/snmp__areca_ diff --git a/plugins/sensors/snmp__areca_ b/plugins/sensors/snmp__areca_ new file mode 100755 index 00000000..ffbd8568 --- /dev/null +++ b/plugins/sensors/snmp__areca_ @@ -0,0 +1,292 @@ +#!/usr/bin/python + +# Copyright (C) 2009-2012 Andreas Thienemann +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU Library General Public License as published by +# the Free Software Foundation; version 2 only +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Library General Public License for more details. +# +# You should have received a copy of the GNU Library General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# + +""" +=head1 NAME + +snmp__areca_ - Munin plugin to get temperature, voltage or fan speed values +from Areca network enabled RAID controllers via SNMP. + +=head1 APPLICABLE SYSTEMS + +All machines with a SNMP capable ARECA raid controller. + +=head1 CONFIGURATION + +Make sure your Areca controller is accessible via SNMP from the munin host: +snmpwalk -v 1 -c snmp_community ip.add.re.ss + +The plugin is a wildcard plugin and can thus be used to retrieve different +values depending on the name of the file. + +Linking it as snmp_10.8.1.230_areca_fan would retrieve the fan speed values from +the Areca controller at 10.8.1.230. + +Valid values are fan, temp and volt. + +Add the following to your /etc/munin/plugin-conf.d/snmp__areca file: + + [snmp_ip.add.re.ss_*] + community private + version 1 + +Then test the plugin by calling the following commands: + +munin-run snmp_10.8.1.230_areca_temp config +munin-run snmp_10.8.1.230_areca_temp + +Both commands should output sensible data without failing. + +=head1 INTERPRETATION + +The plugin shows the temperature in Celsius or the fanspeed in rotations per minute. + +=head1 MAGIC MARKERS + + #%# family=contrib + #%# capabilities= + +=head1 VERSION + +0.0.1 + +=head1 BUGS + +None known. If you know of any, please raise a ticket at https://trac.bawue.org/munin/wiki/areca__snmp_ + +=head1 AUTHOR + +Andreas Thienemann + +=head1 LICENSE + +GPLv2 + +=cut +""" + +import pprint +import time +import sys +import re +import os +from pysnmp import v1, v2c, role, asn1 + +request_conf = { + "volt" : { + "label" : "Voltages", + "vlabel" : "Volt", + "graph" : "--base 1000 --logarithmic", + "oid" : ".1.3.6.1.4.1.18928.1.2.2.1.8" + }, + "fan" : { + "label" : "Fans", + "vlabel" : "RPM", + "graph" : "--base 1000 -l 0", + "oid" : ".1.3.6.1.4.1.18928.1.2.2.1.9" + }, + "temp" : { + "label" : "Temperatures", + "vlabel" : "Celsius", + "graph" : "--base 1000 -l 0", + "oid" : ".1.3.6.1.4.1.18928.1.2.2.1.10" + } +} + +# Sanity check and parsing of the commandline +host = None +port = 161 +request = None +try: + match = re.search("^(?:|.*\/)snmp_([^_]+)_areca_(.+)$", sys.argv[0]) + host = match.group(1) + request = match.group(2) + match = re.search("^([^:]+):(\d+)$", host) + if match is not None: + host = match.group(1) + port = match.group(2) +except: + pass + +if host is None or request is None: + print "# Error: Incorrect filename. Cannot parse host or request." + sys.exit(1) + +# Parse env variables +if os.getenv("community") is not None: + community = os.getenv("community") +else: + community = "public" +if os.getenv("version") is not None: + version = os.getenv("version") +else: + version = "1" + + +def get_data(): + # Fetch the data + results = snmpwalk(request_conf[request]["oid"]) + + # parse data + vals = [] + for i in range(0, len(results)): + idx, res = results[i][0].split(request_conf[request]["oid"])[1].split(".")[1:], results[i][1] + if idx[1] == '1': + vals.append([]) + vals[int(idx[2])-1].append(res) + if idx[1] == '2': + vals[int(idx[2])-1].append(res) + if idx[1] == '3': + if request == "volt": + res = float(res)/1000 + vals[int(idx[2])-1].append(res) + + return vals + +def snmpwalk(root): + + # Create SNMP manager object + client = role.manager((host, port)) + + # Create a SNMP request&response objects from protocol version + # specific module. + try: + req = eval('v' + version).GETREQUEST() + nextReq = eval('v' + version).GETNEXTREQUEST() + rsp = eval('v' + version).GETRESPONSE() + + except (NameError, AttributeError): + print '# Unsupported SNMP protocol version: %s\n%s' % (version, usage) + sys.exit(-1) + + # Store tables headers + head_oids = [root] + + encoded_oids = map(asn1.OBJECTID().encode, head_oids) + + result = []; + + while 1: + + # Encode OIDs, encode SNMP request message and try to send + # it to SNMP agent and receive a response + (answer, src) = client.send_and_receive(req.encode(community=community, encoded_oids=encoded_oids)) + + # Decode SNMP response + rsp.decode(answer) + + # Make sure response matches request (request IDs, communities, etc) + if req != rsp: + raise 'Unmatched response: %s vs %s' % (str(req), str(rsp)) + + # Decode BER encoded Object IDs. + oids = map(lambda x: x[0], map(asn1.OBJECTID().decode, rsp['encoded_oids'])) + + # Decode BER encoded values associated with Object IDs. + vals = map(lambda x: x[0](), map(asn1.decode, rsp['encoded_vals'])) + + # Check for remote SNMP agent failure + if rsp['error_status']: + # SNMP agent reports 'no such name' when walk is over + if rsp['error_status'] == 2: + # Switch over to GETNEXT req on error + # XXX what if one of multiple vars fails? + if not (req is nextReq): + req = nextReq + continue + # One of the tables exceeded + for l in oids, vals, head_oids: + del l[rsp['error_index']-1] + else: + raise 'SNMP error #' + str(rsp['error_status']) + ' for OID #' + str(rsp['error_index']) + + # Exclude completed OIDs + while 1: + for idx in range(len(head_oids)): + if not asn1.OBJECTID(head_oids[idx]).isaprefix(oids[idx]): + # One of the tables exceeded + for l in oids, vals, head_oids: + del l[idx] + break + else: + break + + if not head_oids: + return result + + # Print out results + for (oid, val) in map(None, oids, vals): + result.append((oid, str(val))) + # print oid + ' ---> ' + str(val) + + # BER encode next SNMP Object IDs to query + encoded_oids = map(asn1.OBJECTID().encode, oids) + + # Update request object + req['request_id'] = req['request_id'] + 1 + + # Switch over GETNEXT PDU for if not done + if not (req is nextReq): + req = nextReq + + raise "error" + + +def print_config(): + print "graph_title " + request_conf[request]["label"] + print "graph_vlabel " + request_conf[request]["vlabel"] + print "graph_args " + request_conf[request]["graph"] + print "graph_category sensors" + print "host_name", host + for dataset in get_data(): + if request == "volt": + if dataset[1] == "Battery Status": + continue + else: + print request + dataset[0] + ".label", dataset[1] + ref_val = float(dataset[1].split()[-1][:-1]) + print request + dataset[0] + ".warning", str(ref_val * 0.95) + ":" + str(ref_val * 1.05) + print request + dataset[0] + ".critical", str(ref_val * 0.80) + ":" + str(ref_val * 1.20) + if request == "temp": + print request + dataset[0] + ".label", dataset[1] + if dataset[1].startswith("CPU"): + print request + dataset[0] + ".warning", 55 + print request + dataset[0] + ".critical", 60 + if dataset[1].startswith("System"): + print request + dataset[0] + ".warning", 40 + print request + dataset[0] + ".critical", 45 + if request == "fan": + print request + dataset[0] + ".label", dataset[1] + print request + dataset[0] + ".warning", 2400 + print request + dataset[0] + ".critical", 2000 + + sys.exit(0) + + +if "config" in sys.argv[1:]: + print_config() +elif "snmpconf" in sys.argv[1:]: + print "require 1.3.6.1.4.1.18928.1.2.2.1.8.1.1" + sys.exit(0) +else: + for dataset in get_data(): + # Filter Battery Status (255 == Not installed) + if request == "volt" and dataset[1] == "Battery Status": + continue + print request + dataset[0] + ".value", dataset[2] + sys.exit(0) From 97cf6d3235f8f596eec5269d1d330c3b4c06f79b Mon Sep 17 00:00:00 2001 From: Andreas Thienemann Date: Wed, 7 Mar 2012 11:55:49 +0100 Subject: [PATCH 16/42] Minor changes to the documentation. --- plugins/sensors/freeipmi_ | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/plugins/sensors/freeipmi_ b/plugins/sensors/freeipmi_ index ab6860f8..101fea4f 100755 --- a/plugins/sensors/freeipmi_ +++ b/plugins/sensors/freeipmi_ @@ -1,6 +1,6 @@ #!/usr/bin/python # -# Copyright (C) 2011 Andreas Thienemann +# Copyright (C) 2011,2012 Andreas Thienemann # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -40,11 +40,13 @@ likely no bmc to query. In certain cases however bmc-info will just seem to hang for quite some time. In this case, autodetection does not work because the smbios table has -incorrect information. One system know to experience this problem is the +incorrect information. One system known to experience this problem is the HP Proliant Microserver. Adding env.freeipmi_args "--no-probing --driver-type=KCS --driver-address=0xca2 --register-spacing=1" -to the munin plugin configuration will make the plugin work. +to the munin plugin configuration will make the plugin work. This is the +specific line for the HP Proliant Microserver mentioned above. Your mileage +may vary. Basic configuration for every system is that the plugin needs to be called as root. From 06c1e27d34478e07d7b936c09ca1bc4141dd0bc6 Mon Sep 17 00:00:00 2001 From: Andreas Thienemann Date: Wed, 7 Mar 2012 11:59:04 +0100 Subject: [PATCH 17/42] Simple bash fixes to make comparision clearer. --- plugins/cyrus/cyrus-imapd | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/plugins/cyrus/cyrus-imapd b/plugins/cyrus/cyrus-imapd index 5345caa0..d765af6e 100755 --- a/plugins/cyrus/cyrus-imapd +++ b/plugins/cyrus/cyrus-imapd @@ -1,8 +1,6 @@ #!/bin/sh # -# $Id: cyrus-imapd 18 2011-07-15 09:14:04Z ixs $ -# -# Copyright (C) 2009-2011 Andreas Thienemann +# Copyright (C) 2009 - 2012 Andreas Thienemann # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU Library General Public License as published by @@ -51,7 +49,7 @@ It displays the following three datapoints: =head1 VERSION - $Revision: 18 $ + 0.0.20120307 =head1 BUGS @@ -71,7 +69,7 @@ GPLv2 CONFIGDIR=$(awk -F : '/^configdirectory:/ { gsub(/ /, "", $2); print $2 }' /etc/imapd.conf 2> /dev/null) PROCDIR="${CONFIGDIR}/proc" -if [ "$1" = "autoconf" ]; then +if [ "$1" == "autoconf" ]; then if [ "x${CONFIGDIR}x" != "xx" ] && [ -d ${PROCDIR} ]; then echo yes else @@ -81,14 +79,14 @@ if [ "$1" = "autoconf" ]; then fi # Check if we actually got some sensible data -if [ "x${CONFIGDIR}x" = "xx" ]; then +if [ "x${CONFIGDIR}x" == "xx" ]; then exit 1 fi # If run with the "config"-parameter, give out information on how the # graphs should look. -if [ "$1" = "config" ]; then +if [ "$1" == "config" ]; then echo 'graph_title Cyrus IMAPd Load' echo 'graph_args --base 1000 -l 0' echo 'graph_vlabel connections' From 227e510310512a65920758dbd7c11cf0afb50489 Mon Sep 17 00:00:00 2001 From: Andreas Thienemann Date: Wed, 7 Mar 2012 11:59:43 +0100 Subject: [PATCH 18/42] updated (c) line --- plugins/sensors/snmp__areca_ | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/sensors/snmp__areca_ b/plugins/sensors/snmp__areca_ index ffbd8568..dbb41454 100755 --- a/plugins/sensors/snmp__areca_ +++ b/plugins/sensors/snmp__areca_ @@ -1,6 +1,6 @@ #!/usr/bin/python -# Copyright (C) 2009-2012 Andreas Thienemann +# Copyright (C) 2009 - 2012 Andreas Thienemann # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU Library General Public License as published by From 3c6692940b99b344f1b2636e5bd24111d8bbca2e Mon Sep 17 00:00:00 2001 From: Andreas Thienemann Date: Wed, 7 Mar 2012 12:02:30 +0100 Subject: [PATCH 19/42] Added an check_munin script I had lying around. If your nagios isntallation uses munin as a passive datasource a check_dummy script is normally installed for the active checks. This script is a replacement and will be able to query your munin-node to get current values. This is great to manually run a check. --- tools/nagios/check_munin | 187 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 187 insertions(+) create mode 100644 tools/nagios/check_munin diff --git a/tools/nagios/check_munin b/tools/nagios/check_munin new file mode 100644 index 00000000..23d7dc7d --- /dev/null +++ b/tools/nagios/check_munin @@ -0,0 +1,187 @@ +#!/usr/bin/python +# +# Copyright (C) 2009 Andreas Thienemann +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU Library General Public License as published by +# the Free Software Foundation; version 2 only +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Library General Public License for more details. +# +# You should have received a copy of the GNU Library General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# + +# +# Nagios script to query a munin host for plugin values +# +# Can be used as an active check instead of check_dummy +# + +import optparse +import socket +import pprint +import sys +import re + +parser = optparse.OptionParser("usage: %prog -H -M [-P ] -D [] []") +parser.add_option("-H", "--host", dest="host", type="string", + help="specify host to poll") +parser.add_option("-M", "--module", dest="module", type="string", + help="munin module to poll") +parser.add_option("-P", "--port", dest="port", default=4949, + type="int", help="port number to poll") +parser.add_option("-D", "--debug", action="store_true", dest="debug", default=False, + help="Debug output") + +(options, args) = parser.parse_args() + +HOST = options.host +PORT = options.port +MODULE = options.module +DEBUG = options.debug + +if HOST == None or MODULE == None: + parser.error("options -H and -M are required.") + +def compare(val, thresh): + # Compare value to warning and critical threshoulds + # Handle different threshold formats: max, :max, min:, min:max + + val = float(val) + + # max + match = re.match("^[:]?([-+]?[0-9]+)$", str(thresh)) + if match: + max = float(match.group(1)) + if val > max: + return 3 + + + # min + match = re.match("^([-+]?[0-9]+):$", str(thresh)) + if match: + min = float(match.group(1)) + if val < min: + return 2 + + # min:max + match = re.match("^([-+]?[0-9]+):([-+]?[0-9]+)$", str(thresh)) + if match: + min, max = float(match.group(1)), float(match.group(2)) + if val < min or val > max: + return 1 + + # okay + return 0 + +def output(l, cat, desc, ret): + if len(l[cat]) > 0: + print MODULE, desc + ";" + for line in l["critical"]: + print "CRITICAL: " + line + ";" + for line in l["warning"]: + print "WARNING: " + line + ";" + for line in l["ok"]: + print "OK: " + line + ";" + sys.exit(ret) + +try: + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.connect((HOST, PORT)) + conn = s.makefile('wb', 0) +except: + print "Couldn't connect to requested host" + sys.exit(3) + + +if conn.readline().startswith("# munin node at"): + conn.writelines("config" + MODULE + "\n") + order = [] + data = {} + while True: + line = conn.readline() + if DEBUG: + pprint.pprint(line) + # Last message, bail + if line == ".\n": + break + + label = "" + + key, val = line.split(" ", 1) + if key.find(".") is not -1: + label = key.split(".")[0] + if label not in data: + data[label] = { "warning" : "", "critical" : "", "value" : "" } + order.append(label) + # No thresholds passed on the command line + if len(args) == 2: + data[label]["warning"] = args[0] + data[label]["critical"] = args[1] + + # No thresholds passed on the command line, take the munin supplied ones + if len(args) < 2: + if key.endswith("warning"): + data[label]["warning"] = val[:-1] + if key.endswith("critical"): + data[label]["critical"] = val[:-1] + + if data[label]["warning"] == "" or data[label]["critical"] == "": + print "UNKNOWN - Couldn't retrieve thresholds, pass some on the command line" + sys.exit(3) + + + conn.writelines("fetch " + MODULE + "\n") + while True: + line = conn.readline() + # Last line, bail + if line == ".\n": + if DEBUG: + pprint.pprint(data) + break + + key, val = line.split(" ", 1) + label = key.split(".")[0] + if key.endswith("value"): + data[label]["value"] = val[:-1] + + conn.writelines("quit\n") + +else: + print "UNKNOWN - No munin node detected" + sys.exit(3) + +conn.close() +s.close() + +l = { "ok" : [], "warning" : [], "critical" : [] } +for entry in order: + # compare actual data: 3 max exceeded, 2 minimum underrun, 1 outside limit, 0 okay + for tresh in ["critical", "warning"]: + val = data[entry]["value"] + tval = data[entry][tresh] + tmp = "" + if compare(val, tval) == 3: + tmp = entry + ": " + val + " has exceeded the maximum threshold of " + tval + break + elif compare(val, tval) == 2: + tmp = entry + ": " + val + " has underrun the minimum threshold of " + tval + break + elif compare(val, tval) == 1: + tmp = entry + ": " + val + " is outside of range " + tval + break + + if tmp != "": + l[tresh].append(tmp) + else: + l["ok"].append(entry + ": " + val + " is okay") + + +output(l, "critical", "CRITICAL", 2) +output(l, "warning", "WARNING", 1) +output(l, "ok", "OK", 0) From 031d4e669f779e1c66f8573d1f370b797b16c62e Mon Sep 17 00:00:00 2001 From: Andreas Thienemann Date: Wed, 7 Mar 2012 12:09:04 +0100 Subject: [PATCH 20/42] Plugin to check fanspeed readings from a Thecus NAS server. --- plugins/sensors/snmp__thecus_fans | 104 ++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100755 plugins/sensors/snmp__thecus_fans diff --git a/plugins/sensors/snmp__thecus_fans b/plugins/sensors/snmp__thecus_fans new file mode 100755 index 00000000..ec43e277 --- /dev/null +++ b/plugins/sensors/snmp__thecus_fans @@ -0,0 +1,104 @@ +#!/usr/bin/perl -w +# -*- cperl -*- + +=head1 NAME + +snmp__thecus_fans - Munin plugin to retrive fanspeed readings from a Thecus +NAS device running SNMP. + +=head1 APPLICABLE SYSTEMS + +All Thecus NAS devices which have the third party NETSNMPD module installed. +This is available at http://www.fajo.de/main/thecus/modules/netsnmpd + +=head1 CONFIGURATION + +As a rule SNMP plugins need site specific configuration. The default +configuration (shown here) will only work on insecure sites/devices. + + [snmp_*] + env.version 2 + env.community public + +In general SNMP is not very secure at all unless you use SNMP version +3 which supports authentication and privacy (encryption). But in any +case the community string for your devices should not be "public". + +Please see 'perldoc Munin::Plugin::SNMP' for further configuration +information. + +=head1 INTERPRETATION + +The plugin reports the current fan speed readings as reported by the thecusIO +module. + +=head1 MIB INFORMATION + +Private MIB + +=head1 MAGIC MARKERS + + #%# family=snmpauto + #%# capabilities=snmpconf + +=head1 VERSION + + 0.0.20120307 + +=head1 BUGS + +None known. + +=head1 AUTHOR + +Copyright (C) 2011 - 2012 Andreas Thienemann + +Based on the snmp__uptime plugin as a template. + +=head1 LICENSE + +GPLv2 or (at your option) any later version. + +=cut + +use strict; +use Munin::Plugin::SNMP; +use vars qw($DEBUG); + +$DEBUG = $ENV{'MUNIN_DEBUG'}; + +my $response; + +if (defined $ARGV[0] and $ARGV[0] eq "snmpconf") { + print "index 1.3.6.1.4.1.14822.101.21.\n"; + print "require 1.3.6.1.4.1.14822.101.21.5200.1.1.0 [0-9]\n"; + print "require 1.3.6.1.4.1.14822.101.21.5200.1.2.0 [0-9]\n"; + exit 0; +} + +if (defined $ARGV[0] and $ARGV[0] eq "config") { + my ($host) = Munin::Plugin::SNMP->config_session(); + print "host_name $host\n" unless $host eq 'localhost'; + print "graph_title Thecus Fans +graph_args --base 1000 -l 0 +graph_vlabel RPM +graph_info This graph shows the RPMs of the fans as reported by the ThecusIO module. +graph_category sensors +fan1.label Fan 1 +fan1.info Thecus CPU Fan +fan2.label Fan 2 +fan2.info Thecus System Fan +"; + exit 0; +} + +my $session = Munin::Plugin::SNMP->session(-translate => + [ -timeticks => 0x0 ]); + +my $fan1 = $session->get_single ("1.3.6.1.4.1.14822.101.21.5200.1.1.0") || 'U'; +my $fan2 = $session->get_single ("1.3.6.1.4.1.14822.101.21.5200.1.2.0") || 'U'; + +#print "Retrived uptime is '$uptime'\n" if $DEBUG; + +print "fan1.value ", $fan1, "\n"; +print "fan2.value ", $fan2, "\n"; From c96bafa128e27fecffaebc99503650b9f765e823 Mon Sep 17 00:00:00 2001 From: Andreas Thienemann Date: Wed, 7 Mar 2012 12:21:41 +0100 Subject: [PATCH 21/42] New plugin to monitor a single raw value from a disk. Great to chart LOAD_CYCLE_COUNT or CURRENT_PENDING_SECTORS as they indicate disk lifetime... --- plugins/disk/smart_raw__ | 145 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100755 plugins/disk/smart_raw__ diff --git a/plugins/disk/smart_raw__ b/plugins/disk/smart_raw__ new file mode 100755 index 00000000..e466a7dd --- /dev/null +++ b/plugins/disk/smart_raw__ @@ -0,0 +1,145 @@ + #!/usr/bin/python +# +# Copyright (C) 2011 Andreas Thienemann +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +""" +=head1 NAME + +smart_raw__ - Munin plugin to retreive raw SMART values from a disk. + +=head1 APPLICABLE SYSTEMS + +All machines with a SMART capable disk and smartmontools installed. + +This plugin is very useful if you want to need to monitor a single raw S.M.A.R.T. +value reported by a disk. Load Cycle Counts or Pending Sectors come to mind as +these are a good indicator of problems with a disk. + +=head1 CONFIGURATION + +The plugin should be installed as smart_raw_sda_193 which means that the smart value +numbered 193 will be read from the disk sda. + + +Basic configuration for every system is that the plugin needs to be called as root +in order to execute smartmontools. + +Add the following to your /etc/munin/plugin-conf.d/smart_raw: + + [smart_raw_sda_193] + user root + +=head1 INTERPRETATION + +Smart RAW values are provided as is. You need to undertand what you are monitoring... + +=head1 MAGIC MARKERS + + #%# family=contrib + #%# capabilities= + +=head1 VERSION + +0.0.1 + +=head1 BUGS + +None known. + +=head1 AUTHOR + +Andreas Thienemann + +=head1 LICENSE + +GPLv3+ + +=cut +""" + +import subprocess +import sys +import os +import re +import pprint + +# We are a wildcard plugin, figure out what we are being called for +try: + disk = sys.argv[0].split('_')[2] + value = sys.argv[0].split('_')[3] +except IndexError: + sys.exit(1) + +def normalize_name(name): + name = re.sub("[^a-z0-9A-Z]","_", name) + return name + +# Code sniplet from Philipp Keller +# http://code.pui.ch/2007/02/19/set-timeout-for-a-shell-command-in-python/ +def timeout_command(command, timeout): + """call shell-command and either return its output or kill it + if it doesn't normally exit within timeout seconds and return None""" + import subprocess, datetime, os, time, signal + start = datetime.datetime.now() + process = subprocess.Popen(command.split(), stdout=subprocess.PIPE, stderr=subprocess.PIPE) + while process.poll() is None: + time.sleep(0.1) + now = datetime.datetime.now() + if (now - start).seconds> timeout: + os.kill(process.pid, signal.SIGKILL) + os.waitpid(-1, os.WNOHANG) + return None + return process.stdout.read() + +def read_smart(): + """Read SMART attributes""" + out = timeout_command("/usr/sbin/smartctl -A -d ata /dev/%s" % (disk), 2) + smart_attribs = dict() + for line in out.split('\n')[7:-2]: # Cut away the header and the footer + line = line.split() + smart_attribs[line[0]] = { + 'name' : line[1], + 'label' : normalize_name(line[1]), + 'raw' : line [-1], + } + return smart_attribs + +def print_config(): + """Return configuration arguments for munin""" + attribs = read_smart() + print "graph_title S.M.A.R.T raw value %s for drive %s" % (attribs[value]['name'], disk) + print "graph_vlabel Raw value" + print "graph_info RAW smartctl value" + print "graph_category disk" + print "graph_args --base 1000 -l 0" + + print "%s.label %s" % (value, attribs[value]['label']) + sys.exit(0) + +def fetch(): + attribs = read_smart() + print "%s.value %s" % (value, attribs[value]['raw']) + sys.exit(0) + +if "config" in sys.argv[1:]: + print_config() +elif "autoconf" in sys.argv[1:]: + pass +elif "suggest" in sys.argv[1:]: + pass +else: + fetch() From dd4afac8691047dc6c2b011ad642be66bc1cfd0d Mon Sep 17 00:00:00 2001 From: Steve Schnepp Date: Wed, 7 Mar 2012 12:53:52 +0100 Subject: [PATCH 22/42] Move the screenshots next to plugins --- images/README.md | 2 ++ .../{ => ejabberd_scanlog}/ejabberd_scanlog | 0 .../ejabberd/ejabberd_scanlog}/ejabberd_scanlog.png | Bin plugins/network/{ => netstat_bsd_}/netstat_bsd_ | 0 .../network/netstat_bsd_}/netstat_bsd_.png | Bin plugins/system/{ => snmp__fn}/snmp__fn | 0 .../system/snmp__fn}/snmp__fn-cpu.png | Bin .../system/snmp__fn}/snmp__fn-memory.png | Bin .../system/snmp__fn}/snmp__fn-sessions.png | Bin .../system/snmp__fn}/snmp__fn-vpnsessions.png | Bin 10 files changed, 2 insertions(+) create mode 100644 images/README.md rename plugins/ejabberd/{ => ejabberd_scanlog}/ejabberd_scanlog (100%) rename {images/network => plugins/ejabberd/ejabberd_scanlog}/ejabberd_scanlog.png (100%) rename plugins/network/{ => netstat_bsd_}/netstat_bsd_ (100%) rename {images/network => plugins/network/netstat_bsd_}/netstat_bsd_.png (100%) rename plugins/system/{ => snmp__fn}/snmp__fn (100%) rename {images => plugins/system/snmp__fn}/snmp__fn-cpu.png (100%) rename {images => plugins/system/snmp__fn}/snmp__fn-memory.png (100%) rename {images => plugins/system/snmp__fn}/snmp__fn-sessions.png (100%) rename {images => plugins/system/snmp__fn}/snmp__fn-vpnsessions.png (100%) diff --git a/images/README.md b/images/README.md new file mode 100644 index 00000000..fedf12ba --- /dev/null +++ b/images/README.md @@ -0,0 +1,2 @@ +Please **don't** put screenshots of your plugins here. +Put them right next to your plugins. diff --git a/plugins/ejabberd/ejabberd_scanlog b/plugins/ejabberd/ejabberd_scanlog/ejabberd_scanlog similarity index 100% rename from plugins/ejabberd/ejabberd_scanlog rename to plugins/ejabberd/ejabberd_scanlog/ejabberd_scanlog diff --git a/images/network/ejabberd_scanlog.png b/plugins/ejabberd/ejabberd_scanlog/ejabberd_scanlog.png similarity index 100% rename from images/network/ejabberd_scanlog.png rename to plugins/ejabberd/ejabberd_scanlog/ejabberd_scanlog.png diff --git a/plugins/network/netstat_bsd_ b/plugins/network/netstat_bsd_/netstat_bsd_ similarity index 100% rename from plugins/network/netstat_bsd_ rename to plugins/network/netstat_bsd_/netstat_bsd_ diff --git a/images/network/netstat_bsd_.png b/plugins/network/netstat_bsd_/netstat_bsd_.png similarity index 100% rename from images/network/netstat_bsd_.png rename to plugins/network/netstat_bsd_/netstat_bsd_.png diff --git a/plugins/system/snmp__fn b/plugins/system/snmp__fn/snmp__fn similarity index 100% rename from plugins/system/snmp__fn rename to plugins/system/snmp__fn/snmp__fn diff --git a/images/snmp__fn-cpu.png b/plugins/system/snmp__fn/snmp__fn-cpu.png similarity index 100% rename from images/snmp__fn-cpu.png rename to plugins/system/snmp__fn/snmp__fn-cpu.png diff --git a/images/snmp__fn-memory.png b/plugins/system/snmp__fn/snmp__fn-memory.png similarity index 100% rename from images/snmp__fn-memory.png rename to plugins/system/snmp__fn/snmp__fn-memory.png diff --git a/images/snmp__fn-sessions.png b/plugins/system/snmp__fn/snmp__fn-sessions.png similarity index 100% rename from images/snmp__fn-sessions.png rename to plugins/system/snmp__fn/snmp__fn-sessions.png diff --git a/images/snmp__fn-vpnsessions.png b/plugins/system/snmp__fn/snmp__fn-vpnsessions.png similarity index 100% rename from images/snmp__fn-vpnsessions.png rename to plugins/system/snmp__fn/snmp__fn-vpnsessions.png From 58f03a964a6d69540445d4015ba2c08a270fc886 Mon Sep 17 00:00:00 2001 From: Steve Schnepp Date: Wed, 7 Mar 2012 16:53:31 +0100 Subject: [PATCH 23/42] Revert "- indentation" This reverts commit fa07809f26e12a8d2bd159a737d6f12b4c434cb9. --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index e9b3d4cc..db71671e 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# This is the repository for all user contributed stuff +This is the repository for all user contributed stuff -## contrib/plugins/ - 3rd-party plugins +# contrib/plugins/ - 3rd-party plugins **This is usually where you want to begin your journey.** @@ -9,7 +9,7 @@ That web site is for the time being disabled, new updates are done here. If a dedicated website comes back alive, its plugin backend will be this git repo. -## contrib/templates/ - 3rd-party templates +# contrib/templates/ - 3rd-party templates Feel free to update templates here, or even to create new ones. @@ -21,7 +21,7 @@ It should serves as a base for small editions that can be resynced in SVN trunk, * don't copy the whole template * directly edit files in this directory -## contrib/tools/ - 3rd-party tools +# 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. From f0d8bcdb66a2655c9d7a310de076dcdc1cb7b4d4 Mon Sep 17 00:00:00 2001 From: Steve Schnepp Date: Wed, 7 Mar 2012 17:59:19 +0100 Subject: [PATCH 24/42] fix hang when asking for unknown commands --- tools/pmmn/bin/pmmn | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/tools/pmmn/bin/pmmn b/tools/pmmn/bin/pmmn index ce149228..bf9d9bcf 100755 --- a/tools/pmmn/bin/pmmn +++ b/tools/pmmn/bin/pmmn @@ -54,6 +54,7 @@ while(my $line = <>) { next; } elsif ($cmd eq "nodes") { print "$host\n"; + print "."; next; } elsif ($cmd eq "quit") { exit(0); @@ -67,19 +68,19 @@ while(my $line = <>) { } closedir(PLUGIN_DIR); next; - } elsif (-e $plugin_filename) { - my $arg_plugin; - if ($cmd eq "config") { - $arg_plugin = "config"; - } elsif ($cmd eq "fetch") { - $arg_plugin = ""; - } else { - # Ignore + } elsif ($cmd eq "config" || $cmd eq "alert" || $cmd eq "fetch") { + if (-d $plugin_filename || ! -x $plugin_filename) { + print "# Unknown plugin [$arg] for $cmd"; next; } + my $arg_plugin = ($cmd eq "fetch") ? "" : $cmd; system($plugin_filename, $arg_plugin); print "."; + next; } + + # Arriving here is not a good sign + print "# Unknown command. Try list, nodes, config, fetch, version, alert or quit"; } continue { #print " " x 4096; print "\n"; From 5b1303b08d7dcf0bdb439b1a7ed11dea1bc35d99 Mon Sep 17 00:00:00 2001 From: Alexey Illarionov Date: Thu, 8 Mar 2012 00:10:06 +0400 Subject: [PATCH 25/42] ifem_: copy-paste error in description fixed --- plugins/network/ifem_ | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/network/ifem_ b/plugins/network/ifem_ index 63d4d522..89d42bd2 100755 --- a/plugins/network/ifem_ +++ b/plugins/network/ifem_ @@ -2,9 +2,9 @@ # # Wildcard-plugin to monitor FreeBSD em(4) and igb(4) network interfaces # using sysctl dev.em.0.mac_stats 64-bit counters -# To monitor an # interface, link if_ to this file. E.g. +# To monitor an # interface, link ifem_ to this file. E.g. # -# ln -s /usr/share/munin/node/plugins-auto/if_ /etc/munin/node.d/if_em0 +# ln -s /usr/share/munin/node/plugins-auto/ifem_ /etc/munin/node.d/ifem_em0 # # ...will monitor em0. # From 2f2252e98de3d33e25aaffc717968420f36ac9d4 Mon Sep 17 00:00:00 2001 From: Alexey Illarionov Date: Thu, 8 Mar 2012 00:19:06 +0400 Subject: [PATCH 26/42] Fixed typo in plugins.conf configuration description. Reported by Ilya Evseev at http://forum.nag.ru/forum/index.php?showtopic=70527&view=findpost&p=671563 --- plugins/network/ipfwcnt_ | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/plugins/network/ipfwcnt_ b/plugins/network/ipfwcnt_ index 58a41242..fff98860 100755 --- a/plugins/network/ipfwcnt_ +++ b/plugins/network/ipfwcnt_ @@ -2,11 +2,11 @@ # # Copyright (C) 2009 Alexey Illarionov # -# Wildcard plugin to monitor ipfw rules counters +# Wildcard plugin to monitor ipfw rules counters # Usage: # # Method 1: -# +# # Link ipfwcnt_ to this file. E.g. # # ln -s ipfwcnt_ ipfwcnt_100 @@ -28,19 +28,19 @@ # 3. Add rules configuration to plugins.conf: # [ipfwcnt_rl0-in] # user root -# env rules group0 group1 group2 nogroup -# env rule_group0 100 -# env rule_group0_label group0 -# env rule_group0_info Incoming traffic of group 0 -# env rule_group1 200 -# env rule_group1_label group1 -# env rule_group1_info Incoming traffic of group 1 -# env rule_group2 300 -# env rule_group2_label group2 -# env rule_group2_info Incoming traffic of group 2 -# env rule_nogroup 400 -# env rule_nogroup_label nogroup -# env rule_nogroup_info Incoming traffic of no group +# env.rules group0 group1 group2 nogroup +# env.rule_group0 100 +# env.rule_group0_label group0 +# env.rule_group0_info Incoming traffic of group 0 +# env.rule_group1 200 +# env.rule_group1_label group1 +# env.rule_group1_info Incoming traffic of group 1 +# env.rule_group2 300 +# env.rule_group2_label group2 +# env.rule_group2_info Incoming traffic of group 2 +# env.rule_nogroup 400 +# env.rule_nogroup_label nogroup +# env.rule_nogroup_info Incoming traffic of no group # # ... will monitor ipfw rules 100,200,300,400 # From d252526b937dcbd59120e1797ce7103dc42df5f1 Mon Sep 17 00:00:00 2001 From: Steve Schnepp Date: Thu, 8 Mar 2012 10:20:22 +0100 Subject: [PATCH 27/42] Adding copyright notice --- tools/pmmn/bin/pmmn | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/pmmn/bin/pmmn b/tools/pmmn/bin/pmmn index bf9d9bcf..c39ea2dd 100755 --- a/tools/pmmn/bin/pmmn +++ b/tools/pmmn/bin/pmmn @@ -1,6 +1,7 @@ #! /usr/bin/perl # Poor man's Munin Node # Usable with only Perl 5 +#(c) 2012 LGPL - Steve Schnepp use warnings; use strict; From 8232e65515eb6c5a2fc426dae673e3697ba21a64 Mon Sep 17 00:00:00 2001 From: Alexey Illarionov Date: Thu, 8 Mar 2012 00:10:06 +0400 Subject: [PATCH 28/42] ifem_: copy-paste error in description fixed --- plugins/network/ifem_ | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/network/ifem_ b/plugins/network/ifem_ index 63d4d522..89d42bd2 100755 --- a/plugins/network/ifem_ +++ b/plugins/network/ifem_ @@ -2,9 +2,9 @@ # # Wildcard-plugin to monitor FreeBSD em(4) and igb(4) network interfaces # using sysctl dev.em.0.mac_stats 64-bit counters -# To monitor an # interface, link if_ to this file. E.g. +# To monitor an # interface, link ifem_ to this file. E.g. # -# ln -s /usr/share/munin/node/plugins-auto/if_ /etc/munin/node.d/if_em0 +# ln -s /usr/share/munin/node/plugins-auto/ifem_ /etc/munin/node.d/ifem_em0 # # ...will monitor em0. # From 7fc99a37166de38e04d6c626065083e3135dd5d9 Mon Sep 17 00:00:00 2001 From: Alexey Illarionov Date: Thu, 8 Mar 2012 00:19:06 +0400 Subject: [PATCH 29/42] Fixed typo in plugins.conf configuration description. Reported by Ilya Evseev at http://forum.nag.ru/forum/index.php?showtopic=70527&view=findpost&p=671563 --- plugins/network/ipfwcnt_ | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/plugins/network/ipfwcnt_ b/plugins/network/ipfwcnt_ index 58a41242..fff98860 100755 --- a/plugins/network/ipfwcnt_ +++ b/plugins/network/ipfwcnt_ @@ -2,11 +2,11 @@ # # Copyright (C) 2009 Alexey Illarionov # -# Wildcard plugin to monitor ipfw rules counters +# Wildcard plugin to monitor ipfw rules counters # Usage: # # Method 1: -# +# # Link ipfwcnt_ to this file. E.g. # # ln -s ipfwcnt_ ipfwcnt_100 @@ -28,19 +28,19 @@ # 3. Add rules configuration to plugins.conf: # [ipfwcnt_rl0-in] # user root -# env rules group0 group1 group2 nogroup -# env rule_group0 100 -# env rule_group0_label group0 -# env rule_group0_info Incoming traffic of group 0 -# env rule_group1 200 -# env rule_group1_label group1 -# env rule_group1_info Incoming traffic of group 1 -# env rule_group2 300 -# env rule_group2_label group2 -# env rule_group2_info Incoming traffic of group 2 -# env rule_nogroup 400 -# env rule_nogroup_label nogroup -# env rule_nogroup_info Incoming traffic of no group +# env.rules group0 group1 group2 nogroup +# env.rule_group0 100 +# env.rule_group0_label group0 +# env.rule_group0_info Incoming traffic of group 0 +# env.rule_group1 200 +# env.rule_group1_label group1 +# env.rule_group1_info Incoming traffic of group 1 +# env.rule_group2 300 +# env.rule_group2_label group2 +# env.rule_group2_info Incoming traffic of group 2 +# env.rule_nogroup 400 +# env.rule_nogroup_label nogroup +# env.rule_nogroup_info Incoming traffic of no group # # ... will monitor ipfw rules 100,200,300,400 # From 8eee012f2cbb9e67d3412cb3269ecdbb6fb944d7 Mon Sep 17 00:00:00 2001 From: Steve Schnepp Date: Thu, 8 Mar 2012 10:27:11 +0100 Subject: [PATCH 30/42] Adding basic spoolfetch plugin support --- tools/pmmn/bin/pmmn | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/tools/pmmn/bin/pmmn b/tools/pmmn/bin/pmmn index c39ea2dd..a6f0c9f6 100755 --- a/tools/pmmn/bin/pmmn +++ b/tools/pmmn/bin/pmmn @@ -19,6 +19,8 @@ my $port; # Default is stdin/stdout my $verbose; my $host; my $plugin_dir = "plugins"; +my $spoolfetch_dir; + { my $man = 0; my $help = 0; @@ -29,6 +31,8 @@ my $plugin_dir = "plugins"; 'plugin-dir|d=s' => \$plugin_dir, 'host|h=s' => \$host, + 'spoolfetch-dir|s=s' => \$spoolfetch_dir, + 'help|?' => \$help, man => \$man, ) or pod2usage(2); @@ -78,12 +82,19 @@ while(my $line = <>) { system($plugin_filename, $arg_plugin); print "."; next; + } elsif ($cmd eq "cap") { + print "cap "; + print "spool " if $spoolfetch_dir; + next; + } elsif ($cmd eq "spoolfetch" && $spoolfetch_dir) { + system("$spoolfetch_dir/spoolfetch_$host", $arg); + print "."; + next; } # Arriving here is not a good sign print "# Unknown command. Try list, nodes, config, fetch, version, alert or quit"; } continue { - #print " " x 4096; print "\n"; }; @@ -105,6 +116,8 @@ Options: --plugin-dir Plugin directory (default is current dir) --host Host name (default is /bin/hostname) + --spoolfetch-dir Spoolfetch plugin dirs (default is disabled) + --help brief help message --man full documentation From 18b69829c0e08e2bcf7b5d018594d1ddcfad890b Mon Sep 17 00:00:00 2001 From: Steve Schnepp Date: Thu, 8 Mar 2012 11:46:43 +0100 Subject: [PATCH 31/42] Pmmn is now mature enough to be called 1.0.0 --- tools/pmmn/bin/pmmn | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/pmmn/bin/pmmn b/tools/pmmn/bin/pmmn index a6f0c9f6..e2749346 100755 --- a/tools/pmmn/bin/pmmn +++ b/tools/pmmn/bin/pmmn @@ -13,7 +13,7 @@ use Pod::Usage; $| = 1; -my $VERSION = "0.0.1"; +my $VERSION = "1.0.0"; my $port; # Default is stdin/stdout my $verbose; From 43642c1b7f33a89c13af4045aabba9d2420cf3a8 Mon Sep 17 00:00:00 2001 From: Phil Wray Date: Thu, 8 Mar 2012 18:19:00 +0000 Subject: [PATCH 32/42] fixed typo on line 46 --- plugins/forum/vbulletin_users | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/forum/vbulletin_users b/plugins/forum/vbulletin_users index 6a501482..9fb43980 100755 --- a/plugins/forum/vbulletin_users +++ b/plugins/forum/vbulletin_users @@ -43,7 +43,7 @@ my %regexp = ("vbulletin"=> "(\d+)[^\d]+(\d+)\sguests\", "phpbb" => ""); my $type = undef; -my $timoeout = 30; +my $timeout = 30; if ( defined $ARGV[0] and $ARGV[0] eq "autoconf" ) { From c9ab85cccf73f518f5d66b86cab4ae2f21630330 Mon Sep 17 00:00:00 2001 From: Phil Wray Date: Thu, 8 Mar 2012 18:28:57 +0000 Subject: [PATCH 33/42] Added a version for vbulletin4, just made a change to the html tags --- plugins/forum/vbulletin4_users | 94 ++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 plugins/forum/vbulletin4_users diff --git a/plugins/forum/vbulletin4_users b/plugins/forum/vbulletin4_users new file mode 100644 index 00000000..5f6ee7e5 --- /dev/null +++ b/plugins/forum/vbulletin4_users @@ -0,0 +1,94 @@ +#!/usr/bin/perl +# +# Plugin to monitor the number of guests/users on a vbulletin4 forum +# Since we are using a regexp, this will work for EN forum only... adapt the regexp if your forum is not using that language. +# visual-regexp is a great help for tuning the regexp (package exists on Debian and Ubuntu repositories) +# +# +# Parameters supported: +# +# config +# autoconf +# +# Configurable variables +# +# url to online.php, this page is part of vbulletin distribution, pp=1 so the page is fast to load and doesn't give us too many unwanted info +# +# $Log$ +# +# +# Not sure about those lines... +# +# Magic markers: +#%# family=auto +#%# capabilities=autoconf + +my $ret = undef; + +if (! eval "require LWP::UserAgent;") +{ + $ret = "LWP::UserAgent not found"; +} + +# CHANGE ME +my $URL = exists $ENV{'url'} ? $ENV{'url'} : "http://www.url.to/forums/online.php?pp=1"; + + +# We will use this variable, not on this release ;-) +my $forum_type = exists $ENV{'type'} ? $ENV{'type'} : "vbulletin"; + +# same here +my %regexp = ("vbulletin"=> "(\d+)[^\d]+(\d+)\sguests\", + "punbb" => "", + "phpbb" => ""); + +my $type = undef; +my $timeout = 30; + +if ( defined $ARGV[0] and $ARGV[0] eq "autoconf" ) +{ + if ($ret) + { + print "no ($ret)\n"; + exit 1; + } + +} + +if ( defined $ARGV[0] and $ARGV[0] eq "config" ) +{ + print "graph_title Forum Users\n"; + print "graph_args -l 0\n"; + print "graph_vlabel current users\n"; + print "graph_category Forum\n"; + print "graph_total Total\n"; + + print "members.label Members\n"; + print "members.draw AREA\n"; + print "guests.draw STACK\n"; + print "guests.label Guests\n"; + + + + exit 0; +} + + +my $ua = LWP::UserAgent->new(timeout => $timeout); +my $url = sprintf $URL; +my $response = $ua->request(HTTP::Request->new('GET',$url)); + +# part of the output we want to catch :

42 members and 420 guests

--> 42 - 420 +if ($response->content =~ /

(\d+)\smembers[^\d]+(\d+)\sguests<\/h1>/im) + { + + print "members.value $1\n"; + print "guests.value $2\n"; + + } else { + print "members.value U\n"; + print "guests.value U\n"; + } + + +# vim:syntax=perl From d8ef1a40a179d22f93709265452597d86f73e752 Mon Sep 17 00:00:00 2001 From: Kenyon Ralph Date: Sat, 10 Mar 2012 23:58:28 -0800 Subject: [PATCH 34/42] add plugin to monitor ARRIS TM502G cable modem --- plugins/network/arris-tm502g_ | 77 +++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100755 plugins/network/arris-tm502g_ diff --git a/plugins/network/arris-tm502g_ b/plugins/network/arris-tm502g_ new file mode 100755 index 00000000..8e1bf35d --- /dev/null +++ b/plugins/network/arris-tm502g_ @@ -0,0 +1,77 @@ +#!/usr/bin/env python3 +# -*- python -*- + +# This plugin graphs the following values of the ARRIS TM502G cable +# modem: +# +# * upstream and downstream powers +# * downstream signal-to-noise ratio +# +# The values are retrieved from the cable modem's status web pages at +# 192.168.100.1. So, this plugin must be installed on a munin node +# which can access those pages. +# +# Symlink this plugin into the node's plugins directory (like +# /etc/munin/plugins) as arris-tm502g_power for the powers, and +# arris-tm502g_snr for the SNR. +# +# Author: Kenyon Ralph +# +# The latest version of this plugin can be found in the munin contrib +# repository at https://github.com/munin-monitoring/contrib. Issues +# with this plugin may be reported there. Patches accepted through the +# normal github process of forking the repository and submitting a +# pull request with your commits. + +import html.parser +import os +import urllib.request +import sys + +plugin_name=list(os.path.split(sys.argv[0]))[1] +plugin_var=plugin_name.split('_', 1)[-1] + +if len(sys.argv) == 2 and sys.argv[1] == 'config': + if plugin_var == 'power': + print('graph_title ARRIS Cable Modem Power') + print('graph_vlabel Signal Strength (dBmV)') + print('graph_info This graph shows the downstream and upstream power reported by an ARRIS TM502G cable modem.') + print('downstream.label Downstream Power (dBmV)') + print('upstream.label Upstream Power (dBmV)') + if plugin_var == 'snr': + print('graph_title ARRIS Cable Modem SNR') + print('graph_vlabel Signal-to-Noise Ratio (dB)') + print('graph_info This graph shows the downstream signal-to-noise ratio reported by an ARRIS TM502G cable modem.') + print('snr.label Signal-to-Noise Ratio (dB)') + print('graph_category network') + sys.exit(0) + +class ArrisHTMLParser(html.parser.HTMLParser): + stats = list() + down_power = 'U' + up_power = 'U' + snr = 'U' + def handle_data(self, data): + data = data.strip() + if data != '' and 'dB' in data: + self.stats.append(data) + def done(self): + """Call this when done feeding the HTML page to the parser.""" + self.down_power = self.stats[0].split()[0] + self.snr = self.stats[1].split()[0] + self.up_power = self.stats[2].split()[0] + +page = urllib.request.urlopen("http://192.168.100.1/") +parser = ArrisHTMLParser() +for line in page: + parser.feed(line.decode()) +parser.done() + +if plugin_var == 'power': + print('downstream.value ' + parser.down_power) + print('upstream.value ' + parser.up_power) + sys.exit(0) + +if plugin_var == 'snr': + print('snr.value ' + parser.snr) + sys.exit(0) From 0e1d54f2fd1850871a90463be78a709fa165f984 Mon Sep 17 00:00:00 2001 From: Kenyon Ralph Date: Sun, 11 Mar 2012 04:08:48 -0700 Subject: [PATCH 35/42] fix tor-bandwidth-usage I mistakenly broke the authentication routine during my overhaul of the plugin. --- plugins/network/tor-bandwidth-usage | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/plugins/network/tor-bandwidth-usage b/plugins/network/tor-bandwidth-usage index 316d57ca..b1871ade 100755 --- a/plugins/network/tor-bandwidth-usage +++ b/plugins/network/tor-bandwidth-usage @@ -60,9 +60,13 @@ sub Authenticate if (open(COOKIE, "<$ENV{cookiefile}")) { my $cookie; binmode COOKIE; - read(COOKIE, $cookie, 32); + $authline .= " "; + while (read(COOKIE, $cookie, 32)) { + foreach my $byte (unpack "C*", $cookie) { + $authline .= sprintf "%02x", $byte; + } + } close COOKIE; - $authline .= ' "' . $cookie . '"'; } } elsif (defined($ENV{password})) { $authline .= ' "' . $ENV{password} . '"'; From 8a515d2476478c18ffb88c96b3f670b867ab1e0b Mon Sep 17 00:00:00 2001 From: Clemens Schwaighofer Date: Wed, 14 Mar 2012 10:23:34 +0900 Subject: [PATCH 36/42] Plugin to monitor PgBouncer a new plugin to monitor PostgreSQL pgbouncer. Uses perl and DBD::Pg --- plugins/postgresql/pgbouncer_ | 294 ++++++++++++++++++++++++++++++++++ 1 file changed, 294 insertions(+) create mode 100755 plugins/postgresql/pgbouncer_ diff --git a/plugins/postgresql/pgbouncer_ b/plugins/postgresql/pgbouncer_ new file mode 100755 index 00000000..51d2c91b --- /dev/null +++ b/plugins/postgresql/pgbouncer_ @@ -0,0 +1,294 @@ +#!/usr/bin/perl -w + +# re-write of python version of pgbouncer stats +# data from stats, pools (cleint, server) + +use Munin::Plugin; +use DBD::Pg; + +# check that multigraph is avaailable +need_multigraph(); +# get the script name +my $plugin_name = $Munin::Plugin::me; +# set the DB connection vars +my $db_user = $ENV{'pgbouncer_user'} || 'postgres'; +my $db_port = $ENV{'pgbouncer_port'} || '6432'; +my $db_host = $ENV{'pgbouncer_host'} || 'localhost'; +my $db_pass = $ENV{'pgbouncer_pass'} || ''; +my $db_name = 'pgbouncer'; + +# get the DB (pool) name we want to fetch +$plugin_name =~ /pgbouncer_(.*)$/; +$pool_name = $1; +# bail if no name +if (!$pool_name) +{ + print "Cannot get pool name\n"; + exit 1; +} + +# command line arguments for autconf and config +if (defined($ARGV[0])) +{ + # autoconf, nothing to do + if ($ARGV[0] eq 'autoconf') + { + $dbh = DBI->connect("DBI:Pg:dbname=$db_name;host=$db_host;port=$db_port", $db_user, $db_pass); + if (!$dbh) + { + print "no\n"; + exit 1; + } + else + { + print "yes\n"; + exit 0; + } + $dbh->disconnect(); + } + + if ($ARGV[0] eq 'config') + { + # create the basic RRD + # stats: average connections + print "multigraph ".$plugin_name."_stats_avg_req\n"; + print "graph_title PgBouncer $pool_name average connections\n"; + print "graph_args --base 1000\n"; # numbers not bytes + print "graph_vlabel Average connections\n"; + print "graph_scale no\n"; # so we do not print "micro, milli, kilo, etc" + print "graph_category pgbouncer\n"; + print $pool_name."_avg_req.type GAUGE\n"; + print $pool_name."_avg_req.label Avg Req\n"; + print $pool_name."_avg_req.min 0\n"; + print $pool_name."_avg_req.draw LINE2\n"; + # stats: average time for query + print "multigraph ".$plugin_name."_stats_avg_query\n"; + print "graph_title PgBouncer $pool_name average query time\n"; + print "graph_args --base 1000\n"; # numbers not bytes + print "graph_vlabel Average time per query (microseconds)\n"; + print "graph_category pgbouncer\n"; + print $pool_name."_avg_query.type GAUGE\n"; + print $pool_name."_avg_query.label Avg Time\n"; + print $pool_name."_avg_query.min 0\n"; + print $pool_name."_avg_query.draw LINE2\n"; + # stats: in/out bytes + print "multigraph ".$plugin_name."_stats_bytesinout\n"; + print "graph_title PgBouncer $pool_name average bytes received/sent\n"; + print "graph_args --base 1024\n"; # numbers in bytes + print "graph_vlabel Average bytes received (-)/sent (+)\n"; + print "graph_category pgbouncer\n"; + # bytes received + print $pool_name."_avg_recv.type GAUGE\n"; + print $pool_name."_avg_recv.label Avg received\n"; + print $pool_name."_avg_recv.min 0\n"; + print $pool_name."_avg_recv.draw LINE1\n"; + print $pool_name."_avg_recv.graph no\n"; + # bytes sent + print $pool_name."_avg_sent.type GAUGE\n"; + print $pool_name."_avg_sent.label Avg rcvd/sent\n"; + print $pool_name."_avg_sent.min 0\n"; + print $pool_name."_avg_sent.draw LINE1\n"; + print $pool_name."_avg_sent.negative ".$pool_name."_avg_recv\n"; + # pools: server (sv_) + print "multigraph ".$plugin_name."_pools_server\n"; + print "graph_title PgBouncer $pool_name servers\n"; + print "graph_category pgbouncer\n"; + print "graph_args --base 1000\n"; # numbers not bytes + print "graph_vlabel Server connections\n"; + print "graph_scale no\n"; + # active connections + print $pool_name."_server_active.label active\n"; + print $pool_name."_server_active.min 0\n"; + print $pool_name."_server_active.type GAUGE\n"; + print $pool_name."_server_active.draw AREA\n"; + # idle connections + print $pool_name."_server_idle.label idle\n"; + print $pool_name."_server_idle.min 0\n"; + print $pool_name."_server_idle.type GAUGE\n"; + print $pool_name."_server_idle.draw STACK\n"; + # used connections + print $pool_name."_server_used.label used\n"; + print $pool_name."_server_used.min 0\n"; + print $pool_name."_server_used.type GAUGE\n"; + print $pool_name."_server_used.draw STACK\n"; + # tested connections + print $pool_name."_server_tested.label tested\n"; + print $pool_name."_server_tested.min 0\n"; + print $pool_name."_server_tested.type GAUGE\n"; + print $pool_name."_server_tested.draw STACK\n"; + # logged in connections + print $pool_name."_server_login.label login\n"; + print $pool_name."_server_login.min 0\n"; + print $pool_name."_server_login.type GAUGE\n"; + print $pool_name."_server_login.draw STACK\n"; + # pools: client (cl_) + print "multigraph ".$plugin_name."_pools_client\n"; + print "graph_title PgBouncer $pool_name clients\n"; + print "graph_category pgbouncer\n"; + print "graph_args --base 1000\n"; # numbers not bytes + print "graph_vlabel Client connections\n"; + print "graph_scale no\n"; + # active client connections + print $pool_name."_client_active.label active\n"; + print $pool_name."_client_active.min 0\n"; + print $pool_name."_client_active.type GAUGE\n"; + print $pool_name."_client_active.draw AREA\n"; + # waiting client connections + print $pool_name."_client_waiting.label waiting\n"; + print $pool_name."_client_waiting.min 0\n"; + print $pool_name."_client_waiting.type GAUGE\n"; + print $pool_name."_client_waiting.draw STACK\n"; + # pools: maxwait (longest waiting connection, should be 0) + print "multigraph ".$plugin_name."_pools_maxwait\n"; + print "graph_title PgBouncer $pool_name maximum waiting time\n"; + print "graph_args --base 1000\n"; # numbers not bytes + print "graph_vlabel Maximum wait time (seconds)\n"; + print "graph_category pgbouncer\n"; + print $pool_name."_maxwait.type GAUGE\n"; + print $pool_name."_maxwait.label Wait Time\n"; + print $pool_name."_maxwait.min 0\n"; + print $pool_name."_maxwait.draw LINE2\n"; + print $pool_name."_maxwait.warning 1\n"; # warn if not 0 + print $pool_name."_maxwait.critical 10\n"; # go critical if 10 seconds waiting + # END graph + exit 0; + } +} + +# connect to data +$dbh = DBI->connect("DBI:Pg:dbname=$db_name;host=$db_host;port=$db_port", $db_user, $db_pass); +# go trough each set and get the data +foreach $get ('pools', 'stats') +{ + # prep and execute the show query + $pre = $dbh->prepare("SHOW $get"); + $pre->execute(); + while (@data = $pre->fetchrow) + { + # first defines the pool + if ($data[0] eq $pool_name) + { + # print values for the stats: average reqeust, average query time, bytes in/out + if ($get eq 'stats') + { + print "multigraph ".$plugin_name."_".$get."_avg_req\n"; + print $pool_name."_avg_req.value ".$data[5]."\n"; + print "multigraph ".$plugin_name."_".$get."_avg_query\n"; + print $pool_name."_avg_query.value ".$data[8]."\n"; + print "multigraph ".$plugin_name."_".$get."_bytesinout\n"; + print $pool_name."_avg_recv.value ".$data[6]."\n"; + print $pool_name."_avg_sent.value ".$data[7]."\n"; + } + # print data for the pools: server, client + if ($get eq 'pools') + { + print "multigraph ".$plugin_name."_".$get."_server\n"; + print $pool_name."_server_active.value ".$data[4]."\n"; + print $pool_name."_server_idle.value ".$data[5]."\n"; + print $pool_name."_server_used.value ".$data[6]."\n"; + print $pool_name."_server_tested.value ".$data[7]."\n"; + print $pool_name."_server_login.value ".$data[8]."\n"; + print "multigraph ".$plugin_name."_".$get."_client\n"; + print $pool_name."_client_active.value ".$data[2]."\n"; + print $pool_name."_client_waiting.value ".$data[3]."\n"; + print "multigraph ".$plugin_name."_".$get."_maxwait\n"; + print $pool_name."_maxwait.value ".$data[9]."\n"; + } + } + } +} +# close connection +$dbh->disconnect(); + +exit 0; + +__END__ + +=head1 NAME + +pgbouncer_ is a plugin to get the pool and stat values for a single pgbouncer pool name + +=head1 APPLICATION + +perl and DBD::Pg is required, and pgbounce must been installed with a correct setup access for a stat account + +=head1 CONFIGURATION + +the plugin that will be run needs to have the pool name after the plguin base name. + +=head2 plugin configuration + +eg: pgbouncer_foo will run for the pool named foo. + +see SHOW POOLS database list for the pool name + +=head2 munin plugin config file + +in the plugin config file under the [pgbouncer] name the access information ca be set. + +eg: + [pgbouncer*] + env.pgbouncer_pass barfoo + +more extended would be: + [pgbouncer*] + env.pgbouncer_pass barfoo + env.pgbouncer_user bar + env.pgbouncer_port 6542 + env.pgbouncer_host localhost + +The database name is always pgbouncer + +=head1 OUTPUT + +The plugin will output 5 graphs in the group pgbouncer + +=head2 Average bytes received/sent + +This graph will show the average bytes sent and received by the pgbouncer for this pool + +=head2 Avaerage connections + +This graph will show the average amount of connections to the pgbouncer for this pool + +=head2 Average query time + +This graph shows the average query time as processed by the pgbouncer for this pool in microseconds. The data will be shorted by standard SI. eg, m = milli, k = kilo. + +So 4.61K is 4610 milliseconds + +=head2 Client connections + +This graph shows the active and waiting client connections to pgbouncer for this pool + +=head2 Server connections + +This graph shows the server connections to pgbouncer for this pool. The following data sets are shown: active, idle, used, tested, login + +=head2 Max wait + +how long the oldest cllient the queue has waited, should be always 0 + +=head1 ACKNOWLEDGEMENTS + +Original idea derived from a simple python script by Dimitri Fontaine + +=head1 SEE ALSO + +See further info on stats and pools on the pgbouncer homepage: + http://pgbouncer.projects.postgresql.org/doc/usage.html#_show_commands + +=head1 VERSION + +1.0 + +=head1 AUTHOR + +Clemens Schwaighofer + +=head1 LICENSE + +GPLv2 + + +=cut From a2c35ad7c2e3b61b0d5e986e543cb79ed79e1ea0 Mon Sep 17 00:00:00 2001 From: Clemens Schwaighofer Date: Wed, 14 Mar 2012 12:43:50 +0900 Subject: [PATCH 37/42] Postfix monitoring plugins to work with multiple postfix installs If there are more than one postfix running, with separate config file, and still use the same main log file, this scripts can create stats split per running postfix instance. Those scripts have not been tested on the new debian multiple postfix infrastructure, only on real separate running postfix services. Except postfix_mailvolume_multi, each plugin needs to be configured per running postfix. For mailqueue* it is the config folder name, for mailstats it is the syslog_name in the config file. mailvolume is also based on the syslog_name, but can do auto config if nothing has been set. --- plugins/postfix/postfix_mailqueue_ | 138 +++++++++++++ plugins/postfix/postfix_mailqueuelog_ | 239 +++++++++++++++++++++++ plugins/postfix/postfix_mailstats_ | 208 ++++++++++++++++++++ plugins/postfix/postfix_mailvolume_multi | 220 +++++++++++++++++++++ 4 files changed, 805 insertions(+) create mode 100755 plugins/postfix/postfix_mailqueue_ create mode 100755 plugins/postfix/postfix_mailqueuelog_ create mode 100755 plugins/postfix/postfix_mailstats_ create mode 100755 plugins/postfix/postfix_mailvolume_multi diff --git a/plugins/postfix/postfix_mailqueue_ b/plugins/postfix/postfix_mailqueue_ new file mode 100755 index 00000000..5ee84556 --- /dev/null +++ b/plugins/postfix/postfix_mailqueue_ @@ -0,0 +1,138 @@ +#!/bin/sh +# -*- sh -*- + +: << =cut + +=head1 NAME + +postfix_mailqueue_ - Plugin to monitor postfix mail spools per running postfix + +=head1 ABOUT + +A guide to postfix mail queue manageent can be found at +L + +A summary: + +=over 4 + +=item maildrop + +Messages that have been submitted via the Postfix sendmail(1) command, +but not yet brought into the main Postfix queue by the pickup(8) +service. + +=item hold + +Messages placed in the "hold" queue stay there until the administrator +intervenes + +=item incoming + +Inbound mail from the network, or mail picked up by the local +pickup(8) daemon from the maildrop directory. + +=item active + +Messages that the queue manager has opened for delivery. Only a limited number +of messages is allowed to enter the active queue (leaky bucket strategy, for a +fixed delivery rate). + +=item deferred + +Mail that could not be delivered upon the first attempt. The queue manager +implements exponential backoff by doubling the time between delivery attempts. + +=item corrupt + +Unreadable or damaged queue files are moved here for inspection. + +=back + +=head1 CONFIGURATION + +Uses the last part of the symlink name to get the postfix queue directory for the config file. +It then extract the queue path from the configuration file and uses it as a spooldir. +A environment spooldir can be set as a fallback. + + [postfix_mailqueue] + env.spooldir /var/spool/postfix + +=head1 AUTHOR + +Unknown. + +Extended to multiple queue use by Clemens Schwaighofer (gullevek@gullevek.org) in 2010. + +=head1 LICENSE + +Unknown. + +=head1 MAGIC MARKERS + +=begin comment + +These magic markers are used by munin-node-configure when installing +munin-node. + +=end comment + + #%# family=auto + #%# capabilities=autoconf + +=cut + +# atempt to get spooldir via postconf, but environment overrides. + +# Remember that postconf is not available unless postfix is. +CONFIG=${0##*postfix_mailqueue_} +CONFIG="/etc/"$CONFIG"/" +POSTCONFSPOOL="$(postconf -c $CONFIG -h queue_directory 2>/dev/null || echo /var/spool/postfix)" +SPOOLDIR=${spooldir:-$POSTCONFSPOOL} + +. $MUNIN_LIBDIR/plugins/plugin.sh + +case $1 in + autoconf|detect) + if [ -d $SPOOLDIR ] ; then + echo yes + exit 0 + else + echo "no (spooldir not found)" + exit 0 + fi + ;; + config) + echo "graph_title Postfix Mailqueue $CONFIG"; + cat <<'EOF' +graph_vlabel Mails in queue +graph_category postfix +graph_total Total +active.label active +deferred.label deferred +maildrop.label maildrop +incoming.label incoming +corrupt.label corrupt +hold.label held +EOF + for field in active deferred maildrop incoming corrupt hold; do + print_warning $field + print_critical $field + done + exit 0 + ;; +esac + +cd $SPOOLDIR >/dev/null 2>/dev/null || { + echo "# Cannot cd to $SPOOLDIR" + exit 1 +} + +cat < =~ /^sum:(\d+)/) +# { +# $sum = $1; +# } +# while () +# { +# if (/^([0-9a-z.\-]+):(\d+)$/) +# { +# $status->{$1} = $2; +# } +# } +# close IN; +#} + +if (! -d $configdir) +{ + print "sum.value U\n"; + foreach my $i (@status_list) + { + print "r$i.value U\n"; + } + exit 0; +} + + +parseLogfile($configdir); + +if ($ARGV[0] and $ARGV[0] eq "config") +{ + # descriptions for the rrd file + my %descriptions = ( + 'crefused' => 'Connection refused', + 'ctimeout' => 'Connection timed out', + 'rtimeout' => 'Lost connection', + 'refusedtalk' => 'Host refused connection', + 'nohost' => 'Host not found', + 'msrefused' => 'Mail service refused', + 'noroute' => 'Route not found', + 'usernotfound' => 'User not found', + 'err450' => '450 mailbox not okay (REJECT)', + 'err452' => '452 mailbox is full', + 'err421' => '421 service not okay (REJECT)', + 'err421a' => '421 service not okay (REJECT, SB)', + 'err4' => 'General 4xx error', + 'lostc' => 'Lost connection', + 'active' => 'Active running', + 'other' => 'Other error' + ); + + + print "graph_title Postfix mailqueue log for $postfix\n"; + print "graph_args --base 1000 -l 0\n"; # numbers not bytes + print "graph_vlabel Mails in Queue log\n"; + print "graph_scale no\n"; # so we do not print "micro, milli, kilo, etc" +# print "graph_total Total\n"; + print "graph_category postfix\n"; + foreach my $i (@status_list) + { + if ($descriptions{$i}) + { + print "r$i.label ".$descriptions{$i}."\n"; + print "r$i.type GAUGE\n"; + print "r$i.draw ".(!$field ? 'AREA' : 'STACK')."\n"; + print "r$i.min 0\n"; + $field = 'AREA'; + } + } + print "sum.label Sum\n"; + print "sum.type GAUGE\n"; + print "sum.draw LINE2\n"; + print "sum.min 0\n"; + exit 0; +} + +print "sum.value $sum\n"; +foreach my $i (@status_list) +{ + print "r$i.value ".($status->{$i} ? $status->{$i} : 0)."\n"; +} + +#if(-l $statefile) { +# die("$statefile is a symbolic link, refusing to touch it."); +#} +#open (OUT, '>', $statefile) or die "Unable to open statefile: $!\n"; +#print OUT "sum:$sum\n"; +#foreach my $i (@status_list) +#{ +# print OUT "$i:".($status->{$i} ? $status->{$i} : 0)."\n"; +#} +#close OUT; + +sub parseLogfile +{ + my ($fname) = @_; + + # the search parts + %search = ( + 'crefused' => 'Connection refused', + 'ctimeout' => 'Connection timed out', + 'rtimeout' => 'read timeout', + 'refusedtalk' => 'refused to talk to me: 554', + 'nohost' => 'Host not found', + 'msrefused' => 'server refused mail service"', + 'noroute' => 'No route to host', + 'usernotfound' => 'address rejected', + 'err450' => ': 450 ', + 'err452' => ': 452 ', + 'err421' => ': 421 ', + 'err421a' => ': 421)', + 'err4' => 'said: 4', + 'lostc' => 'lost connection with', + ); + + my $command = "mailq -C $fname"; + + open(FILE, "$command|") || die ("Cannot open $command: $!"); + while () + { + if (/^\w{10,}\*\s/o) + { + $status->{'active'} ++; + } + elsif (/^\s*\(/o) + { + $set = 0; + foreach $i (@status_list) + { + if ($search{$i} && index($_, $search{$i}) >= 0 && !$set) + { + $status->{$i} ++; + $set = 1; + } + } + if (!$set) + { + $status->{'other'} ++; + } + } + } + close(FILE) || die ("Cannot close $command: $!"); + + foreach $i (keys %{$status}) + { + $sum += $status->{$i}; + } +} + +# vim:syntax=perl diff --git a/plugins/postfix/postfix_mailstats_ b/plugins/postfix/postfix_mailstats_ new file mode 100755 index 00000000..a77e73fc --- /dev/null +++ b/plugins/postfix/postfix_mailstats_ @@ -0,0 +1,208 @@ +#!/usr/bin/perl -w +# -*- perl -*- + +=head1 NAME + +postfix_mailstats_ - Plugin to monitor the number of mails delivered and +rejected by postfix + +=head1 CONFIGURATION + +Uses the last part of the symlink name for grepping the correct data from the +postfix log file. The name must be syslog_name from the postfix config. +The environment settings still applay to this plugin. + +Configuration parameters for /etc/munin/postfix_mailstats_, +if you need to override the defaults below: + + [postfix_mailstats] + env.logdir - Which logfile to use + env.logfile - What file to read in logdir + +=head2 DEFAULT CONFIGURATION + + [postfix_mailstats] + env.logdir /var/log + env.logfile mail.log + +=head1 AUTHOR + +Records show that the plugin was contributed by Nicolai Langfeldt in +2003. Nicolai can't find anything in his email about this and expects +the plugin is based on the corresponding exim plugin - to which it now +bears no resemblence. + +Extended for multiple queue use by Clemens Schwaighofer (gullevek@gullevek.org) in 2010. + +=head1 LICENSE + +GPLv2 + +=head1 MAGIC MARKERS + +=begin comment + +These magic markers are used by munin-node-configure when installing +munin-node. + +=end comment + + #%# family=manual + #%# capabilities=autoconf + +=head1 RANDOM COMMENTS + +Would be cool if someone ported this to Munin::Plugin for both state +file and log tailing. + +=cut + +# get the postfix queue number to look for +$0 =~ /postfix_mailstats_([\w\d]+)$/; +my $postfix = $1; +my $statefile = "$ENV{MUNIN_PLUGSTATE}/munin-plugin-".$postfix."_mailstats.state"; +my $pos; +my $delivered = 0; +my $rejects = {}; +my $LOGDIR = $ENV{'logdir'} || '/var/log'; +my $LOGFILE = $ENV{'logfile'} || 'mail.log'; + +my $logfile = "$LOGDIR/$LOGFILE"; + +if ($ARGV[0] and $ARGV[0] eq "autoconf") +{ + my $logfile; + if (-d $LOGDIR) + { + if (-f $logfile) + { + if (-r $logfile) + { + print "yes\n"; + exit 0; + } + else + { + print "no (logfile '$logfile' not readable)\n"; + } + } + else + { + print "no (logfile '$logfile' not found)\n"; + } + } + else + { + print "no (could not find logdir '$LOGDIR')\n"; + } + + exit 0; +} + + +if (-f $statefile) +{ + open (IN, '<', $statefile) or die "Unable to open state-file: $!\n"; + if ( =~ /^(\d+):(\d+)/) + { + ($pos, $delivered) = ($1, $2); + } + while () + { + if (/^([0-9a-z.\-]+):(\d+)$/) + { + $rejects->{$1} = $2; + } + } + close IN; +} + +if (! -f $logfile) +{ + print "delivered.value U\n"; + foreach my $i (sort keys %{$rejects}) + { + print "r$i.value U\n"; + } + exit 0; +} + +$startsize = (stat $logfile)[7]; + +if (!defined $pos) +{ + # Initial run. + $pos = $startsize; +} + +parseLogfile($logfile, $pos, $startsize); +$pos = $startsize; + +if ( $ARGV[0] and $ARGV[0] eq "config" ) +{ + print "graph_title Postfix message throughput for $postfix\n"; + print "graph_args --base 1000 -l 0\n"; + print "graph_vlabel mails / \${graph_period}\n"; + print "graph_scale no\n"; + print "graph_total Total\n"; + print "graph_category postfix\n"; + print "delivered.label delivered\n"; + print "delivered.type DERIVE\n"; + print "delivered.draw AREA\n"; + print "delivered.min 0\n"; + foreach my $i (sort keys %{$rejects}) + { + print "r$i.label reject $i\n"; + print "r$i.type DERIVE\n"; + print "r$i.draw STACK\n"; + print "r$i.min 0\n"; + } + exit 0; +} + +print "delivered.value $delivered\n"; +foreach my $i (sort keys %{$rejects}) +{ + print "r$i.value ", $rejects->{$i}, "\n"; +} + +if (-l $statefile) +{ + die ("$statefile is a symbolic link, refusing to touch it."); +} +open (OUT, '>', $statefile) or die "Unable to open statefile: $!\n"; +print OUT "$pos:$delivered\n"; +foreach my $i (sort keys %{$rejects}) +{ + print OUT "$i:", $rejects->{$i}, "\n"; +} +close OUT; + +sub parseLogfile +{ + my ($fname, $start, $stop) = @_; + open (LOGFILE, $fname) + or die "Unable to open logfile $fname for reading: $!\n"; + seek (LOGFILE, $start, 0) + or die "Unable to seek to $start in $fname: $!\n"; + + while (tell (LOGFILE) < $stop) + { + my $line = ; + chomp ($line); + + if ($line =~ /qmgr.*from=.*size=[0-9]*/ || + $line =~ /$postfix\/smtp.* status=sent /) + { + $delivered++; + } + elsif ($line =~ /$postfix\/smtpd.*reject: \S+ \S+ \S+ (\S+)/ || + $line =~ /$postfix\/cleanup.* reject: (\S+)/) + { + $rejects->{$1}++; + } + } + close(LOGFILE) or warn "Error closing $fname: $!\n"; +} + +# vim:syntax=perl diff --git a/plugins/postfix/postfix_mailvolume_multi b/plugins/postfix/postfix_mailvolume_multi new file mode 100755 index 00000000..4054d4a6 --- /dev/null +++ b/plugins/postfix/postfix_mailvolume_multi @@ -0,0 +1,220 @@ +#!/usr/bin/perl -w +# -*- perl -*- + +=head1 NAME + +postfix_mailvolume - Plugin to monitor the volume of mails delivered + by multiple postfix and stores per postfix delivered data. + +=head1 APPLICABLE SYSTEMS + +Any postfix. + +=head1 CONFIGURATION + +The following shows the default configuration. + + [postfix*] + env.logdir /var/log + env.logfile syslog + +=head2 Needed additional configuration + +To correctly get all the postfix log data, the postfix system_log prefix names need to be defined with the env.postfix config setting. +If this is not set, the script tries to find all the postfix config folders in /etc/postfix* and get the syslog names from there + + env.postfix postfix10 postfix11 postfix12 + +=head1 INTERPRETATION + +The plugin shows the number of bytes of mail that has passed through +the postfix installation per postfix mailer running. + +=head1 MAGIC MARKERS + + #%# family=auto + #%# capabilities=autoconf + +=head1 BUGS + +None known + +=head1 VERSION + + $Id: postfix_mailvolume.in 2314 2009-08-03 11:28:34Z ssm $ + +=head1 AUTHOR + +Copyright (C) 2011. + +Clemens Schwaighofer (gullevek@gullevek.org) + +=head1 LICENSE + +GPLv2 + +=cut + +use strict; +use Munin::Plugin; + +my $pos = undef; +my $syslog_name = ''; +my @postfix_syslog_name = (); +my %volume = (); +my @restore_state = (); +my $i = 1; +my $LOGDIR = $ENV{'logdir'} || '/var/log'; +my $LOGFILE = $ENV{'logfile'} || 'syslog'; +my $POSTFIX = $ENV{'postfix'} || ''; +# get the postfix syslog_name from the POSTFIX env var, if not set, find them in the /etc/postfix* type +if (!$POSTFIX) +{ + foreach my $dir (grep -d, glob "/etc/postfix*") + { + # remove the leading etc + $dir =~ s/\/etc\///g; + # add data to the postfix string + $POSTFIX .= ' ' if ($POSTFIX); + $POSTFIX .= $dir; + } +} +if ($POSTFIX) +{ + foreach my $config (split(/ /, $POSTFIX)) + { + # find the syslog name + $syslog_name = `postconf -c /etc/$config | grep "syslog_name"`; + # remove any pending whitespace or line breaks + chomp($syslog_name); + $syslog_name =~ s/syslog_name = //g; + # add this to the postfix syslog name array + push(@postfix_syslog_name, $syslog_name); + # also init set the syslog name 0 + $volume{$syslog_name} = 0; + } +} +else +{ + print "Cannot get any postfix syslog_name data\n"; + exit 1; +} + +sub parseLogfile +{ + my ($fname, $start) = @_; + + my ($LOGFILE, $rotated) = tail_open($fname, $start); + + my $line; + + while ($line =<$LOGFILE>) + { + chomp ($line); + # get the postfix syslog name and the size + if ($line =~ /\ (\w+)\/qmgr.*from=.*size=([0-9]+)/) + { + $volume{$1} += $2; + } + } + return tail_close($LOGFILE); +} + +if ($ARGV[0] and $ARGV[0] eq "autoconf") +{ + my $logfile; + `which postconf >/dev/null 2>/dev/null`; + if (!$?) + { + $logfile = "$LOGDIR/$LOGFILE"; + + if (-f $logfile) + { + if (-r "$logfile") + { + print "yes\n"; + exit 0; + } + else + { + print "no (logfile '$logfile' not readable)\n"; + } + } + else + { + print "no (logfile '$logfile' not found)\n"; + } + } + else + { + print "no (postfix not found)\n"; + } + exit 0; +} + +if ($ARGV[0] and $ARGV[0] eq "config") +{ + print "graph_title Postfix bytes throughput per postfix\n"; + print "graph_args --base 1000 -l 0\n"; + print "graph_vlabel bytes / \${graph_period}\n"; + print "graph_scale yes\n"; + print "graph_category postfix\n"; + print "graph_total Throughput sum\n"; + # loop through the postfix names and create per config an entry + foreach $syslog_name (@postfix_syslog_name) + { + print $syslog_name."_volume.label ".$syslog_name." throughput\n"; + print $syslog_name."_volume.type DERIVE\n"; + print $syslog_name."_volume.min 0\n"; + } + exit 0; +} + + +my $logfile = "$LOGDIR/$LOGFILE"; + +if (! -f $logfile) { + print "delivered.value U\n"; + exit 1; +} + +@restore_state = restore_state(); +# first is pos, rest is postfix entries +$pos = $restore_state[0]; +# per postfix values are store: postfix config,value +for ($i = 1; $i < @restore_state; $i ++) +{ + my ($key, $value) = split(/,/, $restore_state[$i]); + $volume{$key} = $value; +} + +if (!$pos) +{ + # No state file present. Avoid startup spike: Do not read log + # file up to now, but remember how large it is now, and next + # time read from there. + + $pos = (stat $logfile)[7]; # File size + foreach $syslog_name (@postfix_syslog_name) + { + $volume{$syslog_name} = 0; + } +} +else +{ + $pos = parseLogfile($logfile, $pos); +} + +@restore_state = ($pos); +foreach $syslog_name (sort keys %volume) +{ + print $syslog_name."_volume.value ".$volume{$syslog_name}."\n"; + push(@restore_state, $syslog_name.','.$volume{$syslog_name}); +} + +# save the current state +save_state(@restore_state); + +# vim:syntax=perl + +__END__ From 647632f7af05b5d82ceecaca2ee19befc8c72483 Mon Sep 17 00:00:00 2001 From: Clemens Schwaighofer Date: Wed, 14 Mar 2012 13:27:44 +0900 Subject: [PATCH 38/42] pgbouncer_ added strict for perl --- plugins/postgresql/pgbouncer_ | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/plugins/postgresql/pgbouncer_ b/plugins/postgresql/pgbouncer_ index 51d2c91b..208d663e 100755 --- a/plugins/postgresql/pgbouncer_ +++ b/plugins/postgresql/pgbouncer_ @@ -3,6 +3,7 @@ # re-write of python version of pgbouncer stats # data from stats, pools (cleint, server) +use strict; use Munin::Plugin; use DBD::Pg; @@ -16,10 +17,10 @@ my $db_port = $ENV{'pgbouncer_port'} || '6432'; my $db_host = $ENV{'pgbouncer_host'} || 'localhost'; my $db_pass = $ENV{'pgbouncer_pass'} || ''; my $db_name = 'pgbouncer'; - +my @data = (); # get the DB (pool) name we want to fetch $plugin_name =~ /pgbouncer_(.*)$/; -$pool_name = $1; +my $pool_name = $1; # bail if no name if (!$pool_name) { @@ -33,7 +34,7 @@ if (defined($ARGV[0])) # autoconf, nothing to do if ($ARGV[0] eq 'autoconf') { - $dbh = DBI->connect("DBI:Pg:dbname=$db_name;host=$db_host;port=$db_port", $db_user, $db_pass); + my $dbh = DBI->connect("DBI:Pg:dbname=$db_name;host=$db_host;port=$db_port", $db_user, $db_pass); if (!$dbh) { print "no\n"; @@ -156,12 +157,12 @@ if (defined($ARGV[0])) } # connect to data -$dbh = DBI->connect("DBI:Pg:dbname=$db_name;host=$db_host;port=$db_port", $db_user, $db_pass); +my $dbh = DBI->connect("DBI:Pg:dbname=$db_name;host=$db_host;port=$db_port", $db_user, $db_pass); # go trough each set and get the data -foreach $get ('pools', 'stats') +foreach my $get ('pools', 'stats') { # prep and execute the show query - $pre = $dbh->prepare("SHOW $get"); + my $pre = $dbh->prepare("SHOW $get"); $pre->execute(); while (@data = $pre->fetchrow) { From 6facd3c33183820b13da40cb962dc2f9dee5eb76 Mon Sep 17 00:00:00 2001 From: Clemens Schwaighofer Date: Wed, 14 Mar 2012 17:26:15 +0900 Subject: [PATCH 39/42] die commands if no DB connection can be made graceful die script if DB connection cannot be created. Also for prepare & execute command --- plugins/postgresql/pgbouncer_ | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/plugins/postgresql/pgbouncer_ b/plugins/postgresql/pgbouncer_ index 208d663e..610c4834 100755 --- a/plugins/postgresql/pgbouncer_ +++ b/plugins/postgresql/pgbouncer_ @@ -157,13 +157,16 @@ if (defined($ARGV[0])) } # connect to data -my $dbh = DBI->connect("DBI:Pg:dbname=$db_name;host=$db_host;port=$db_port", $db_user, $db_pass); +my $dbh = DBI->connect("DBI:Pg:dbname=$db_name;host=$db_host;port=$db_port", $db_user, $db_pass) + or die ("Cannot connect to database"); # go trough each set and get the data foreach my $get ('pools', 'stats') { # prep and execute the show query - my $pre = $dbh->prepare("SHOW $get"); - $pre->execute(); + my $pre = $dbh->prepare("SHOW $get") + or die ("Cannot prepare query"); + $pre->execute() + or die ("Cannot execute statement") while (@data = $pre->fetchrow) { # first defines the pool From 8de0f08367a148761be79b4fe8ce4f5c35131c96 Mon Sep 17 00:00:00 2001 From: Clemens Schwaighofer Date: Wed, 14 Mar 2012 19:27:04 +0900 Subject: [PATCH 40/42] postgres database size in detail shows the data, index, sequence and view size of a single database. Uses the default munin postgresql perl plugin. --- plugins/postgresql/postgres_size_detail_ | 83 ++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100755 plugins/postgresql/postgres_size_detail_ diff --git a/plugins/postgresql/postgres_size_detail_ b/plugins/postgresql/postgres_size_detail_ new file mode 100755 index 00000000..0959e358 --- /dev/null +++ b/plugins/postgresql/postgres_size_detail_ @@ -0,0 +1,83 @@ +#!/usr/bin/perl +# -*- cperl -*- +# +# Copyright (C) 2012, Clemens Schwaighofer +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; version 2 dated June, +# 1991. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. + +=head1 NAME + +postgres_size_detail_ - Plugin to monitor PostgreSQL database size for one database in detail (data, index, sequence, view) + +=head1 CONFIGURATION + +Configuration is done through libpq environment variables, for example +PGUSER, PGDATABASE, etc. For more information, see L. + +To monitor a specific database, link to postgres_size_. +This script cannot monitor all database detail sizes at the same time, a valid database name needs to be given. + +=head1 SEE ALSO + +L + +=head1 MAGIC MARKERS + + #%# family=auto + #%# capabilities=autoconf suggest + +=head1 AUTHOR + +Magnus Hagander , Redpill Linpro AB + +=head1 COPYRIGHT/License. + +Copyright (c) 2012, Clemens Schwaighofer (gullevek@gullevek.org) + +All rights reserved. This program is free software; you can +redistribute it and/or modify it under the terms of the GNU General +Public License as published by the Free Software Foundation; version 2 +dated June, 1991. + +=cut + +use strict; +use warnings; + +use Munin::Plugin::Pgsql; + +my $pg = Munin::Plugin::Pgsql->new( + basename => 'postgres_size_detail_', + title => 'PostgreSQL detail database size', + info => 'Size of database in detail', + vlabel => 'Size', + paramdatabase => 1, + basequery => "SELECT CASE WHEN relkind = 'r' OR relkind = 't' THEN 'db_detail_data' WHEN relkind = 'i' THEN 'db_detail_index' WHEN relkind = 'v' THEN 'db_detail_view' WHEN relkind = 'S' THEN 'db_detail_sequence' ELSE 'db_detail_other' END AS state, + SUM(relpages::bigint * 8 * 1024) AS size + FROM pg_class pg, pg_namespace pgn WHERE pg.relnamespace = pgn.oid AND pgn.nspname NOT IN ('information_schema', 'pg_catalog') GROUP BY state", + configquery => [ + "VALUES ('db_detail_data','Data size'),('db_detail_index','Index size'),('db_detail_sequence','Sequence size'),('db_detail_view','View size')", + ], + suggestquery => + "SELECT datname FROM pg_database WHERE datallowconn AND NOT datistemplate AND NOT datname='postgres' UNION ALL SELECT 'ALL' ORDER BY 1 LIMIT 10", + stack => 1, + graphmin => 0, + graphdraw => 'AREA', + extraconfig => 'graph_total Total' +); + +$pg->Process(); + From f1952dc3b06b8299758d058bedff829c3f32685d Mon Sep 17 00:00:00 2001 From: Clemens Schwaighofer Date: Wed, 14 Mar 2012 19:44:11 +0900 Subject: [PATCH 41/42] postgres size detail Added "other" in case this data exists. --- plugins/postgresql/postgres_size_detail_ | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/postgresql/postgres_size_detail_ b/plugins/postgresql/postgres_size_detail_ index 0959e358..520b3aac 100755 --- a/plugins/postgresql/postgres_size_detail_ +++ b/plugins/postgresql/postgres_size_detail_ @@ -69,7 +69,7 @@ my $pg = Munin::Plugin::Pgsql->new( SUM(relpages::bigint * 8 * 1024) AS size FROM pg_class pg, pg_namespace pgn WHERE pg.relnamespace = pgn.oid AND pgn.nspname NOT IN ('information_schema', 'pg_catalog') GROUP BY state", configquery => [ - "VALUES ('db_detail_data','Data size'),('db_detail_index','Index size'),('db_detail_sequence','Sequence size'),('db_detail_view','View size')", + "VALUES ('db_detail_data','Data size'),('db_detail_index','Index size'),('db_detail_sequence','Sequence size'),('db_detail_view','View size'),('db_detail_other','Other size')", ], suggestquery => "SELECT datname FROM pg_database WHERE datallowconn AND NOT datistemplate AND NOT datname='postgres' UNION ALL SELECT 'ALL' ORDER BY 1 LIMIT 10", From decb17155654ecd4c7ad9c91c17eacdb6403e6ae Mon Sep 17 00:00:00 2001 From: Andrey Pankov Date: Wed, 14 Mar 2012 14:56:33 +0300 Subject: [PATCH 42/42] adding new plugin for resque monitoring --- plugins/redis/resque | 136 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100755 plugins/redis/resque diff --git a/plugins/redis/resque b/plugins/redis/resque new file mode 100755 index 00000000..4abbfde9 --- /dev/null +++ b/plugins/redis/resque @@ -0,0 +1,136 @@ +#!/usr/bin/perl + +# +## Copyright (C) 2012 Andrey Pankov +## +## This program is free software; you can redistribute it and/or +## modify it under the terms of the GNU General Public License +## as published by the Free Software Foundation; version 2 dated June, +## 1991. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program; if not, write to the Free Software +## Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +## +## +## $Log$ +## +## Based on resque monitoring code from https://github.com/caius/redis-munin +## +## Installation process: +## +## 1. Download the plugin to your plugins directory (e.g. /usr/share/munin/plugins) +## 2. Create 4 symlinks at the directory that is used by munin for plugins detection (e.g. /etc/munin/plugins): resque_failed, resque_queues, resque_workers_count, resque_workers_working +## 3. Edit plugin-conf.d/munin-node if it is needed (env.host and env.port variables are accepted) +## 4. Restart munin-node service + +use strict; +use Redis; + +my $HOST = exists $ENV{'host'} ? $ENV{'host'} : "127.0.0.1"; +my $PORT = exists $ENV{'port'} ? $ENV{'port'} : 6379; + +my $server = "$HOST:$PORT"; +my $r = Redis->new( server => sprintf("%s:%d", $HOST, $PORT) ); + +my $config = ( defined $ARGV[0] and $ARGV[0] eq "config" ); + + +$0 =~ s/(.+)resque_//g; +my $opt = $0 ? $0 : 'default'; + +if ($opt eq 'failed') { + if ($config) { + print "graph_title Resque Failure rate\n"; + print "graph_category Resque\n"; + print "graph_info This graph shows resque jobs that failed\n"; + print "graph_args --lower-limit 0\n"; + print 'graph_vlabel fails/s\n'; + + print "failed.label Failed per/s\n"; + print "failed.type COUNTER\n"; + } + else { + my $value = $r->get('resque:stat:failed') || 0; + print "failed.value $value\n"; + } +} +elsif ($opt eq 'queues') { + if ($config) { + print "graph_title Resque queue rates\n"; + print "graph_category Resque\n"; + print "graph_vlabel queue rates/s\n"; + print "graph_info This graph monitors the in and out rate of the queues\n"; + print "graph_args --lower-limit 0\n"; + + my @queues = $r->smembers( 'resque:queues' ); + for my $name (@queues) { + $name =~ s/:/_/; + + print "${name}_pushed.label ${name}_pushed\n"; + print "${name}_pushed.type COUNTER\n"; + + print "${name}_finished.label ${name}_finished\n"; + print "${name}_finished.type COUNTER\n"; + } + } + else { + my @queues = $r->smembers( 'resque:queues' ); + for my $queue (@queues) { + my $name = $queue; + $name =~ s/:/_/; + + my $pushed = $r->get("resque:stat:${queue}:pushed") || 0; + print "${name}_pushed.value ${pushed}\n"; + + my $finished = $r->get("resque:stat:${queue}:finished") || 0; + print "${name}_finished.value ${finished}\n"; + } + } +} +elsif ($opt eq 'workers_count') { + if ($config) { + print "graph_title Resque Workers Count\n"; + print "graph_category Resque\n"; + print "graph_info This graph shows number of resque workers\n"; + print "graph_args --lower-limit 0\n"; + print "graph_vlabel workers\n"; + + print "workers_count.label No. of workers\n"; + print "workers_count.type COUNTER\n"; + } + else { + my @workers = $r->smembers('resque:workers'); + print "workers_count.value " . (scalar @workers) . "\n"; + } +} +elsif ($opt eq 'workers_working') { + if ($config) { + print "graph_title Resque Workers in use\n"; + print "graph_category Resque\n"; + print "graph_info This graph shows the \%age of resque workers busy\n"; + print "graph_args --lower-limit 0 --upper-limit 100\n"; + print "graph_vlabel %\n"; + + print "workers_working.label Workers Busy\n"; + print "workers_working.type GAUGE\n"; + } + else { + my @workers = $r->smembers('resque:workers'); + my $working = 0; + for my $worker (@workers) { + my $value = $r->get("worker:$worker") || 0; + $working++ if $value; + } + my $value = scalar @workers; + if ($value) { + $value = $working * 100 / $value; + } + print "workers_working.value $value\n"; + } +}