1
0
Fork 0
mirror of https://github.com/munin-monitoring/contrib.git synced 2025-07-21 18:41:03 +00:00

Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Artem Sheremet 2012-03-17 02:11:13 +03:00
commit 5ea6583374
88 changed files with 3694 additions and 2496 deletions

View file

@ -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.

2
images/README.md Normal file
View file

@ -0,0 +1,2 @@
Please **don't** put screenshots of your plugins here.
Put them right next to your plugins.

View file

@ -1,8 +1,6 @@
#!/bin/sh
#
# $Id: cyrus-imapd 18 2011-07-15 09:14:04Z ixs $
#
# Copyright (C) 2009-2011 Andreas Thienemann <andreas@bawue.net>
# Copyright (C) 2009 - 2012 Andreas Thienemann <andreas@bawue.net>
#
# 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'

133
plugins/disk/file_age Executable file
View file

@ -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 <it@sverigedemokraterna.se>
=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

145
plugins/disk/smart_raw__ Executable file
View file

@ -0,0 +1,145 @@
#!/usr/bin/python
#
# Copyright (C) 2011 Andreas Thienemann <andreas@bawue.net>
#
# 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 <http://www.gnu.org/licenses/>.
#
"""
=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 <andreas@bawue.net>
=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()

View file

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 36 KiB

Before After
Before After

View file

@ -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"=> "<h1\>(\d+)[^\d]+(\d+)\sguests\</h1>",
"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 : <h1>42 members and 420 guests</h1> --> 42 - 420
if ($response->content =~ /<h1>(\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

View file

@ -43,7 +43,7 @@ my %regexp = ("vbulletin"=> "<strong\>(\d+)[^\d]+(\d+)\sguests\</strong>",
"phpbb" => "");
my $type = undef;
my $timoeout = 30;
my $timeout = 30;
if ( defined $ARGV[0] and $ARGV[0] eq "autoconf" )
{

216
plugins/games/game Normal file
View file

@ -0,0 +1,216 @@
#!/usr/bin/php
<?php
/*
* README
* This plugin makes use of the GameQ library to be able to query many different
* types of game servers and display the results in munin. You can see a list of
* supported games at http://gameq.sourceforge.net/#games
*
* This plugin has 2 environment variables:
* game_config_file - Defaults to /etc/munin/munin-game.ini. The location of the config file
* game_state_file - Defaults to /var/lib/munin/plugin-state/game. The location of the munin state directory
*
* (Probably) FAQ
* Q: Why PHP?
* A: The GameQ library is written in PHP, so it was either that or rewrite the entire library.
*
* Q: Why have you used a config file instead of environment variables?
* A: Way too much to fit data inside an environment variable unfortunately. Imagine
* trying to fit information about 50 servers all inside one variable.
*
* Q: I want to ask you a question!
* A: I'm on most IRC networks under the nick Azelphur, or email me. support@azelphur.com
*
* Q: It's not working!
* A: Try running ./game autoconf, it'll probably tell you what's up.
*/
// Check environment variables
$var = getenv('game_config_file');
$config = ($var) ? $var : "/etc/munin/munin-game.ini";
$var = getenv('game_state_file');
$state = ($var) ? $var : "/var/lib/munin/plugin-state/game";
function p($str) {
// Quick function to print a string with an EOL on the end
echo $str . PHP_EOL;
}
function printMultigraph($ini_array, $machine_name, $title, $info, $max) {
// Print out a standard graph config.
p("multigraph $machine_name");
p("graph_title $title");
p("graph_vlabel players");
p("graph_category gameserver");
p("graph_info $info");
p("graph_printf %6.0lf");
if (isset($ini_array['settings'][$machine_name . '_colour']))
p("players.colour " . $ini_array['settings'][$machine_name . '_colour']);
if (isset($ini_array['settings'][$machine_name . '_draw']))
p("players.draw " . $ini_array['settings'][$machine_name . '_draw']);
p("players.label players");
p("players.info Number of players");
p("players.min 0");
p("players.max $max");
}
function printValue($machine_name, $value) {
// Print a value
p("multigraph $machine_name");
p("players.value " . $value);
}
function queryServers($ini_array) {
// Query all game servers and return the results
require "gameq/GameQ.php";
// Populate the $servers array from the ini file, ready to pass to GameQ
$servers = array();
foreach ($ini_array as $section => $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);
}
?>

View file

@ -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

View file

@ -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,/

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

54
plugins/java/jmx/plugin/jmx_ Executable file
View file

@ -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 <config_name> 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=<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=<PORT> -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false
#
# By default, the plugin monitors localhost on <PORT> = 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

Binary file not shown.

View file

@ -0,0 +1,66 @@
-------- JMX plugin for Munin ---------
Java JMX Munin plugin enables you to monitor JMX attributes in Munin.
As soon as JMX embedded in Java 5, any Java process may expose parameters to be monitored using JMX interface,
look http://java.sun.com/j2se/1.5.0/docs/guide/management/agent.html and http://java.sun.com/jmx for details
In Java version < 5 it is still possible to expose JMX interface using third party libraries
To see what can be monitored by JMX, run <JDK>/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_<configname>,
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:
<fieldname>.jmxObjectName JMX object name, e.g. java.lang:type=Memory
<fieldname>.jmxAttributeName JMX attribute name, e.g. NonHeapMemoryUsage
<fieldname>.jmxAttributeKey If attribute is a composed data (structure), the name of the field in structure, e.g. max
% separates comments in file

8
plugins/java/jmx2munin/.gitignore vendored Normal file
View file

@ -0,0 +1,8 @@
.DS_Store
.classpath
.project
.fatjar
target
eclipse
old
bin

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -0,0 +1,121 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.vafer</groupId>
<artifactId>jmx2munin</artifactId>
<name>jmx2munin</name>
<version>1.0</version>
<description>
Munin plugin to access JMX information
</description>
<url>http://github.com/tcurdt/jmx2munin</url>
<developers>
<developer>
<id>tcurdt</id>
<name>Torsten Curdt</name>
<email>tcurdt at vafer.org</email>
<timezone>+1</timezone>
</developer>
</developers>
<licenses>
<license>
<name>Apache License 2</name>
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
</license>
</licenses>
<scm>
<connection>scm:git:git://github.com:tcurdt/jmx2munin.git</connection>
<developerConnection>scm:git:git://github.com:tcurdt/jmx2munin.git</developerConnection>
<url>http://github.com/tcurdt/jmx2munin/tree/master</url>
</scm>
<dependencies>
<dependency>
<groupId>com.beust</groupId>
<artifactId>jcommander</artifactId>
<version>1.17</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.5</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.5</source>
<target>1.5</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<forkMode>never</forkMode>
<includes>
<include>**/*TestCase.java</include>
</includes>
<excludes>
<exclude>**/Abstract*</exclude>
</excludes>
<testFailureIgnore>true</testFailureIgnore>
<skip>false</skip>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>2.1</version>
<configuration>
<attach>true</attach>
</configuration>
<executions>
<execution>
<id>create-source-jar</id>
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>1.4</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<minimizeJar>false</minimizeJar>
<artifactSet>
<includes>
<include>com.beust:jcommander</include>
</includes>
</artifactSet>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>org.vafer.jmx.munin.Munin</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View file

@ -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<String, LinkedHashMap<Integer, Pattern>> sections = new TreeMap<String, LinkedHashMap<Integer, Pattern>>();
public boolean load(String filePath) throws IOException {
BufferedReader input = null;
LinkedHashMap<Integer, Pattern> section = new LinkedHashMap<Integer, Pattern>();
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<Integer, Pattern>();
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<Integer, Pattern> section = sections.get(id);
if (section == null) {
return null;
}
for(Map.Entry<Integer, Pattern> entry : section.entrySet()) {
if (entry.getValue().matcher(value).matches()) {
return entry.getKey();
}
}
return null;
}
}

View file

@ -0,0 +1,9 @@
package org.vafer.jmx;
import javax.management.ObjectName;
public interface Filter {
public boolean include(ObjectName bean, String attribute);
}

View file

@ -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<String> seen = new HashSet<String>();
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) {
}
});
}
}

View file

@ -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;
}
}

View file

@ -0,0 +1,9 @@
package org.vafer.jmx;
import javax.management.ObjectName;
public interface Output {
public void output(ObjectName beanName, String attributeName, Object value);
}

View file

@ -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<ObjectInstance> 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();
}
}

View file

@ -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<list.size(); i++) {
flatten(beanName, attributeName + "[" + i + "]", list.get(i), listener);
}
} else if (value instanceof Map) {
final Map<?,?> 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);
}
}
}

View file

@ -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<String> args = new ArrayList<String>();
@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<String> attributes = new ArrayList<String>();
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();
}
}

View file

@ -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<String> attributes = new HashSet<String>();
public MuninAttributesFilter(List<String> 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));
}
}

View file

@ -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<String, String> 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<String> 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);
}
});
}
}

View file

@ -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

View file

@ -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}"

77
plugins/network/arris-tm502g_ Executable file
View file

@ -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 <kenyon@kenyonralph.com>
#
# 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)

View file

@ -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_<interface> to this file. E.g.
# To monitor an # interface, link ifem_<interface> 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.
#

View file

@ -2,11 +2,11 @@
#
# Copyright (C) 2009 Alexey Illarionov <littlesavage@rambler.ru>
#
# Wildcard plugin to monitor ipfw rules counters
# Wildcard plugin to monitor ipfw rules counters
# Usage:
#
# Method 1:
#
#
# Link ipfwcnt_<rule number> 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
#

View file

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Before After
Before After

View file

@ -1,91 +0,0 @@
#!/usr/bin/perl
#
# Copyright (C) 2005-2007 Rodolphe Quiedeville <rodolphe@quiedeville.org>
#
# 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(<IN>) {
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";

View file

@ -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} . '"';

Binary file not shown.

Binary file not shown.

View file

@ -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<http://www.postfix.org/QSHAPE_README.html#queues>
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 <<EOF
deferred.value `(test -d deferred && find deferred -type f) | wc -l`
active.value `(test -d active && find active -type f) | wc -l`
maildrop.value `(test -d maildrop && find maildrop -type f) | wc -l`
incoming.value `(test -d incoming && find incoming -type f) | wc -l`
corrupt.value `(test -d corrupt && find corrupt -type f) | wc -l`
hold.value `( test -d hold && find hold -type f) | wc -l`
EOF

View file

@ -0,0 +1,239 @@
#!/usr/bin/perl -w
# -*- perl -*-
=head1 NAME
postfix_mailqueuelog_ - detailed stats for postfix queue data
=head1 CONFIGURATION
Use the last part in the symlink to define which postfix you want to get stats from.
The user has to be set as root, else there might be some errors if
the postfix config is not correctly set witht he alternate directories
[postfix_mailqueuelog_*]
env.etcdir /etc/
user root
=head2 DEFAULT CONFIGURATION
[postfix_mailqueuelog_*]
user root
=head1 AUTHOR
Written in 2010 by Clemens Schwaighofer (gullevek@gullevek.org), based on the HoSaNIC module written by me
=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 state file.
=cut
# get the postfix queue number to look for
$0 =~ /postfix_mailqueuelog_([\w\d]+)$/;
my $postfix = $1;
#my $statefile = "$ENV{MUNIN_PLUGSTATE}/munin-plugin-".$postfix."_mailqueuelog.state";
my $sum = 0;
my $status = {};
my @status_list = ('crefused', 'ctimeout', 'rtimeout', 'refusedtalk', 'nohost', 'msrefused', 'noroute', 'usernotfound', 'err450', 'err452', 'err421', 'err421a', 'err4', 'lostc', 'active', 'other');
my $ETCDIR = $ENV{'etcdir'} || '/etc';
my $configdir = "$ETCDIR/$postfix/";
if ( $ARGV[0] and $ARGV[0] eq "autoconf" )
{
if (-d $ETCDIR)
{
if (-d $configdir)
{
if (-r $configdir)
{
print "yes\n";
exit 0;
}
else
{
print "no (config dir '$configdir' not readable)\n";
}
}
else
{
print "no (config dir '$configdir' not found)\n";
}
}
else
{
print "no (could not find etcdir '$ETCDIR')\n";
}
exit 0;
}
#if ( -f $statefile)
#{
# open (IN, '<', $statefile) or die "Unable to open state-file: $!\n";
# if (<IN> =~ /^sum:(\d+)/)
# {
# $sum = $1;
# }
# while (<IN>)
# {
# 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 (<FILE>)
{
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

View file

@ -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 (<IN> =~ /^(\d+):(\d+)/)
{
($pos, $delivered) = ($1, $2);
}
while (<IN>)
{
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 = <LOGFILE>;
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

View file

@ -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__

298
plugins/postgresql/pgbouncer_ Executable file
View file

@ -0,0 +1,298 @@
#!/usr/bin/perl -w
# re-write of python version of pgbouncer stats
# data from stats, pools (cleint, server)
use strict;
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';
my @data = ();
# get the DB (pool) name we want to fetch
$plugin_name =~ /pgbouncer_(.*)$/;
my $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')
{
my $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
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")
or die ("Cannot prepare query");
$pre->execute()
or die ("Cannot execute statement")
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 <gullevek@gullevek.org>
=head1 LICENSE
GPLv2
=cut

View file

@ -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<Munin::Plugin::Pgsql>.
To monitor a specific database, link to postgres_size_<databasename>.
This script cannot monitor all database detail sizes at the same time, a valid database name needs to be given.
=head1 SEE ALSO
L<Munin::Plugin::Pgsql>
=head1 MAGIC MARKERS
#%# family=auto
#%# capabilities=autoconf suggest
=head1 AUTHOR
Magnus Hagander <magnus@hagander.net>, 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'),('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",
stack => 1,
graphmin => 0,
graphdraw => 'AREA',
extraconfig => 'graph_total Total'
);
$pg->Process();

View file

@ -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

136
plugins/redis/resque Executable file
View file

@ -0,0 +1,136 @@
#!/usr/bin/perl
#
## Copyright (C) 2012 Andrey Pankov <a.pankov@gmail.com>
##
## 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";
}
}

View file

@ -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 <cleware@falk-petersen.no>
# 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

View file

@ -1,6 +1,6 @@
#!/usr/bin/python
#
# Copyright (C) 2011 Andreas Thienemann <andreas@bawue.net>
# Copyright (C) 2011,2012 Andreas Thienemann <andreas@bawue.net>
#
# 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.

292
plugins/sensors/snmp__areca_ Executable file
View file

@ -0,0 +1,292 @@
#!/usr/bin/python
# Copyright (C) 2009 - 2012 Andreas Thienemann <andreas@bawue.net>
#
# 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 <andreas@bawue.net>
=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)

104
plugins/sensors/snmp__thecus_fans Executable file
View file

@ -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 <andreas@bawue.net>
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";

View file

@ -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<Documentation/filesystems/proc.txt> 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 <gombasg@sztaki.hu>
=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 = <FH>) {
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();

View file

@ -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 <sheridan@sheridan-home.ru> (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} = <FH>;
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 (<FH>)
{
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 (<FH>)
{
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 (<FH>)
{
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 (<FH>)
{
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

View file

@ -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 <micah@riseup.net>
# - Add dynamic arch page size determination
# - Some cleanup and clarification
# version 0.3 - 2006 May 3 - Micah Anderson <micah@riseup.net>
# - 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 <micah@riseup.net>
# - 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

View file

@ -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

View file

@ -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

View file

@ -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 <<CONFIGTXT
graph_title CPU frequency(s)
graph_args --base 1000 -l 0
graph_vlabel Hz
graph_category system
graph_info This graph shows the CPU frequency(s).
CONFIGTXT
getAvail
}
function autoconf ()
{
if [ -x $CINFOBIN ]; then
echo "yes"
else
echo "no"
fi
exit 0
}
case $1 in
"autoconf")
autoconf
;;
"config")
config
;;
*)
getFreq
;;
esac

View file

@ -1,193 +0,0 @@
#!/usr/bin/perl -w
#
# munin plugin for monitoring cpu frequency
# Since multiple cpus on one graph doesn't really work, this plugin links to a cpu, i.e. cpufreq_cpu0
#
# written by BenV / JuNe
# email: benv-munin@junerules.com
#
# Version: 0.1
#
# Requires:
# Storable (saving state)
#
# Parameters:
#
# config required
#
# Configurable variables
# statedir location where the state file is saved. Defaults to /var/lib/munin/plugin-state.
# statefile name that's appended to statedir as filename for saving state. Defaults to "cpufreq_$cpu.state"
#
#%# family=auto
#%# capabilities=autoconf suggest
use strict;
if ($ARGV[0] and $ARGV[0] eq "autoconf" ) {
# Check required perl modules
unless (eval "require Storable")
{
print "No (Missing Storable)\n";
exit 1;
}
# Check for required proc/sys files
if (-d "/sys/devices/system/cpu/cpu0/cpufreq")
{
print "Yes\n";
exit 0;
}
print "No (Couldn't find /sys/devices/system/cpu/cpu0/cpufreq dir)";
exit 1;
}
if ($ARGV[0] and $ARGV[0] eq 'suggest' ) {
foreach my $d (</sys/devices/system/cpu/cpu[0-9]*/cpufreq/>)
{
$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 (</sys/devices/system/cpu/$target/cpufreq/stats/>)
{
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(<TIS>)
{
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;

View file

@ -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 <at> 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 }'

View file

@ -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

View file

@ -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

View file

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Before After
Before After

View file

@ -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 <<EOT
graph_title Uptime
graph_args --base 1000 -l 0
graph_vlabel days
graph_category System
compile.label kernel age
compile.type GAUGE
compile.min 0
compile.max 1000
compile.draw AREA
uptime.label uptime
uptime.type GAUGE
uptime.min 0
uptime.max 1000
uptime.draw AREA
EOT
exit 0
fi
LANG=C
(
/bin/date -j +'%s'
/bin/date -jf '%a %b %d %T %Z %Y' \
"`/sbin/sysctl kern.version | /usr/bin/sed -Ee '2,9d;s/^kern.version: [^:]+: //'`" +'%s'
/sbin/sysctl kern.boottime | /usr/bin/sed -Ee 's/.* sec = ([0-9+].*)\,.*/\1/'
) | /usr/bin/awk '
BEGIN {
getline; now=$0;
getline; compile=$0
getline; boot=$0;
printf "compile.value %.3f\n", (now-compile)/86400
printf "uptime.value %.3f\n", (now-boot)/86400
}'

View file

@ -1,30 +0,0 @@
#!/bin/sh
#Plugin created by:
# Stephen Hodgson
# Malone College - CPSC
#This script is provided as-is with no warranty or guarantee of any kind.
#-This simple plugin figures out how many users are logged into the linux box and writes it to a simple Gauge-style graph.
#-The plugin takes advantage of the linux 'who' command.
# -The who command is cut into the first field (to the first space).
# -This is the username field.
# -Unfortunately this will log multiples for the same user if multiple terminals are open.
# -Then we need to sort the results since uniq only deals with unique elements next in line to each other.
# -We find the uniqe usernames logged on.
# -Then wc -l counts how many lines (users) we're left with.
if [ "$1" = "config" ]; then
echo 'graph_title Users Online'
echo 'graph_args --base 1000 -l 0 '
echo 'graph_vlabel Number of users'
echo 'graph_category system'
echo 'users.label users'
echo 'graph_args --base 1000 -l 0'
echo 'graph_scale no'
exit 0
fi
echo -n "users.value "
echo `who | cut -f -1 -d ' ' | sort | uniq | wc -l`

View file

@ -1,61 +0,0 @@
#!/usr/bin/perl -w
#Plugin created by:
# Stephen Hodgson
# Malone College - CPSC
# ported to perl and extended by Thomas Gutzler, 2008 (thomas.gutzler@gmail.com)
# Feel free to modify this plugin, but if you do, be kind and email it back to me. I'm all for improvements that anyone can make.
# - This simple plugin figures out how many users are logged into the linux box
# - The plugin takes advantage of the linux 'who' command.
# - The who command is used to list all login names and number of users logged on.
# - This information is used to output to values:
# 1. total amount of users logged in
# 2. amount of unique users logged in
# Even though this plugin should run without configuration, it is possible to tell it the location of the who command
# Configuration example
# [usersv2]
# env.who_cmd /usr/bin/who
#%# family=auto
#%# capabilities=autoconf
use strict;
my $who_cmd = exists $ENV{who_cmd} ? $ENV{who_cmd} : 'who';
if ((exists $ARGV[0]) && ($ARGV[0] eq "autoconf")) {
my @who = ("$who_cmd -q >/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";

View file

@ -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
}

187
tools/nagios/check_munin Normal file
View file

@ -0,0 +1,187 @@
#!/usr/bin/python
#
# Copyright (C) 2009 Andreas Thienemann <andreas@bawue.net>
#
# 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 <Host> -M <Module> [-P <Port>] -D [<warn>] [<crit>]")
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)

View file

@ -1,6 +1,7 @@
#! /usr/bin/perl
# Poor man's Munin Node
# Usable with only Perl 5
#(c) 2012 LGPL - Steve Schnepp <steve.schnepp@pwkf.org>
use warnings;
use strict;
@ -12,12 +13,14 @@ use Pod::Usage;
$| = 1;
my $VERSION = "0.0.1";
my $VERSION = "1.0.0";
my $port; # Default is stdin/stdout
my $verbose;
my $host;
my $plugin_dir = "plugins";
my $spoolfetch_dir;
{
my $man = 0;
my $help = 0;
@ -28,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);
@ -54,6 +59,7 @@ while(my $line = <>) {
next;
} elsif ($cmd eq "nodes") {
print "$host\n";
print ".";
next;
} elsif ($cmd eq "quit") {
exit(0);
@ -67,21 +73,28 @@ 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;
} 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";
};
@ -103,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