Merge remote-tracking branch 'upstream/master'
|
@ -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
|
@ -0,0 +1,2 @@
|
|||
Please **don't** put screenshots of your plugins here.
|
||||
Put them right next to your plugins.
|
|
@ -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
|
@ -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
|
@ -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()
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 36 KiB |
94
plugins/forum/vbulletin4_users
Normal 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
|
|
@ -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
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
?>
|
28
plugins/games/munin-game.ini
Normal 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
|
22
plugins/java/jmx/examples/java/java_cpu.conf
Normal 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,/
|
46
plugins/java/jmx/examples/java/java_process_memory.conf
Normal 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
|
||||
|
||||
|
14
plugins/java/jmx/examples/java/java_threads.conf
Normal 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
|
||||
|
||||
|
16
plugins/java/jmx/examples/tomcat/catalina_requests.conf
Normal 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
|
12
plugins/java/jmx/examples/tomcat/catalina_threads.conf
Normal 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
|
32
plugins/java/jmx/examples/tomcat/catalina_times.conf
Normal 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
|
||||
|
||||
|
18
plugins/java/jmx/examples/tomcat/catalina_traffic.conf
Normal 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
|
@ -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
|
BIN
plugins/java/jmx/plugin/jmxquery.jar
Normal file
66
plugins/java/jmx/readme.txt
Normal 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
|
@ -0,0 +1,8 @@
|
|||
.DS_Store
|
||||
.classpath
|
||||
.project
|
||||
.fatjar
|
||||
target
|
||||
eclipse
|
||||
old
|
||||
bin
|
79
plugins/java/jmx2munin/README.md
Normal 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
|
|
@ -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
|
55
plugins/java/jmx2munin/contrib/jmx2munin.sh
Normal 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
|
121
plugins/java/jmx2munin/pom.xml
Normal 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>
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
package org.vafer.jmx;
|
||||
|
||||
import javax.management.ObjectName;
|
||||
|
||||
public interface Filter {
|
||||
|
||||
public boolean include(ObjectName bean, String attribute);
|
||||
|
||||
}
|
|
@ -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) {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
package org.vafer.jmx;
|
||||
|
||||
import javax.management.ObjectName;
|
||||
|
||||
public interface Output {
|
||||
|
||||
public void output(ObjectName beanName, String attributeName, Object value);
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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
|
@ -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)
|
|
@ -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.
|
||||
#
|
||||
|
|
|
@ -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
|
||||
#
|
||||
|
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
|
@ -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";
|
|
@ -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} . '"';
|
||||
|
|
138
plugins/postfix/postfix_mailqueue_
Executable 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
|
239
plugins/postfix/postfix_mailqueuelog_
Executable 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
|
208
plugins/postfix/postfix_mailstats_
Executable 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
|
220
plugins/postfix/postfix_mailvolume_multi
Executable 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
|
@ -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
|
83
plugins/postgresql/postgres_size_detail_
Executable 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();
|
||||
|
|
@ -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
|
@ -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";
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
@ -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
|
@ -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";
|
|
@ -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();
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
@ -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
|
|
@ -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
|
||||
|
|
@ -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;
|
|
@ -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 }'
|
|
@ -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
|
||||
|
|
@ -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
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
@ -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
|
||||
}'
|
|
@ -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`
|
|
@ -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";
|
||||
|
|
@ -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
|
@ -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)
|
|
@ -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
|
||||
|
||||
|
|