mirror of
https://github.com/munin-monitoring/contrib.git
synced 2018-11-08 00:59:34 +01:00
Merge branch 'master' of github.com:munin-monitoring/contrib
Conflicts: README.md
This commit is contained in:
commit
5c3aeabe76
10
README.md
10
README.md
@ -5,7 +5,9 @@
|
|||||||
**This is usually where you want to begin your journey.**
|
**This is usually where you want to begin your journey.**
|
||||||
|
|
||||||
Here you'll find all the plugins coming from http://exchange.munin-monitoring.org/.
|
Here you'll find all the plugins coming from http://exchange.munin-monitoring.org/.
|
||||||
It as evolved since then, but
|
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
|
||||||
|
|
||||||
@ -25,3 +27,9 @@ Here, you can put just any kind of tool. Please use this directory instead of a
|
|||||||
It makes things way more easy to search for others.
|
It makes things way more easy to search for others.
|
||||||
|
|
||||||
And, it serves as an incubator of SVN `trunk/contrib` :-)
|
And, it serves as an incubator of SVN `trunk/contrib` :-)
|
||||||
|
|
||||||
|
## Notes to contributors
|
||||||
|
|
||||||
|
We like to have ''elementary'' commits (a good rationale is : one per Changelog entry), as it is much easier to manage for reviewing. Debugging is also usually easier that way.
|
||||||
|
|
||||||
|
So please **don't** be afraid to make as many commits as needed.
|
||||||
|
2
images/README.md
Normal file
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
|
#!/bin/sh
|
||||||
#
|
#
|
||||||
# $Id: cyrus-imapd 18 2011-07-15 09:14:04Z ixs $
|
# Copyright (C) 2009 - 2012 Andreas Thienemann <andreas@bawue.net>
|
||||||
#
|
|
||||||
# Copyright (C) 2009-2011 Andreas Thienemann <andreas@bawue.net>
|
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or modify
|
# 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
|
# 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
|
=head1 VERSION
|
||||||
|
|
||||||
$Revision: 18 $
|
0.0.20120307
|
||||||
|
|
||||||
=head1 BUGS
|
=head1 BUGS
|
||||||
|
|
||||||
@ -71,7 +69,7 @@ GPLv2
|
|||||||
CONFIGDIR=$(awk -F : '/^configdirectory:/ { gsub(/ /, "", $2); print $2 }' /etc/imapd.conf 2> /dev/null)
|
CONFIGDIR=$(awk -F : '/^configdirectory:/ { gsub(/ /, "", $2); print $2 }' /etc/imapd.conf 2> /dev/null)
|
||||||
PROCDIR="${CONFIGDIR}/proc"
|
PROCDIR="${CONFIGDIR}/proc"
|
||||||
|
|
||||||
if [ "$1" = "autoconf" ]; then
|
if [ "$1" == "autoconf" ]; then
|
||||||
if [ "x${CONFIGDIR}x" != "xx" ] && [ -d ${PROCDIR} ]; then
|
if [ "x${CONFIGDIR}x" != "xx" ] && [ -d ${PROCDIR} ]; then
|
||||||
echo yes
|
echo yes
|
||||||
else
|
else
|
||||||
@ -81,14 +79,14 @@ if [ "$1" = "autoconf" ]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Check if we actually got some sensible data
|
# Check if we actually got some sensible data
|
||||||
if [ "x${CONFIGDIR}x" = "xx" ]; then
|
if [ "x${CONFIGDIR}x" == "xx" ]; then
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# If run with the "config"-parameter, give out information on how the
|
# If run with the "config"-parameter, give out information on how the
|
||||||
# graphs should look.
|
# graphs should look.
|
||||||
|
|
||||||
if [ "$1" = "config" ]; then
|
if [ "$1" == "config" ]; then
|
||||||
echo 'graph_title Cyrus IMAPd Load'
|
echo 'graph_title Cyrus IMAPd Load'
|
||||||
echo 'graph_args --base 1000 -l 0'
|
echo 'graph_args --base 1000 -l 0'
|
||||||
echo 'graph_vlabel connections'
|
echo 'graph_vlabel connections'
|
133
plugins/disk/file_age
Executable file
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
|
@ -14,16 +14,18 @@
|
|||||||
#%# capabilities=autoconf
|
#%# capabilities=autoconf
|
||||||
#
|
#
|
||||||
# 2011/05/20 - pmoranga - initial version
|
# 2011/05/20 - pmoranga - initial version
|
||||||
|
#
|
||||||
|
# 2012/01/27 - Sébastien Gross
|
||||||
|
# - Fix lvdisplay path
|
||||||
|
|
||||||
|
lvdisplay=$(which lvdisplay)
|
||||||
|
|
||||||
if [ "$1" = "autoconf" ]; then
|
if [ "$1" = "autoconf" ]; then
|
||||||
/usr/sbin/lvdisplay 2>/dev/null >/dev/null
|
if test -n "${lvdisplay}"; then
|
||||||
if [ $? -eq 0 ]
|
|
||||||
then
|
|
||||||
echo yes
|
echo yes
|
||||||
exit 0
|
exit 0
|
||||||
else
|
|
||||||
echo "no lvdisplay found"
|
|
||||||
fi
|
fi
|
||||||
|
echo "no lvdisplay found"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@ -34,9 +36,9 @@ if [ "$1" = "config" ]; then
|
|||||||
echo 'graph_vlabel %'
|
echo 'graph_vlabel %'
|
||||||
echo 'graph_category disk'
|
echo 'graph_category disk'
|
||||||
echo 'graph_args --base 100'
|
echo 'graph_args --base 100'
|
||||||
/usr/sbin/lvdisplay -C | awk '$3 ~ /^s/{print $1".label "$1" snapshot of "$5} '
|
${lvdisplay} -C | awk '$3 ~ /^s/{print $1".label "$1" snapshot of "$5} '
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
||||||
/usr/sbin/lvdisplay -C | awk '$3 ~ /^s/{print $1".value",int($6)} '
|
${lvdisplay} -C | awk '$3 ~ /^s/{print $1".value",int($6)} '
|
145
plugins/disk/smart_raw__
Executable file
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()
|
285
plugins/djabberd/djabberd_
Normal file
285
plugins/djabberd/djabberd_
Normal file
@ -0,0 +1,285 @@
|
|||||||
|
#!/usr/bin/perl -w
|
||||||
|
#
|
||||||
|
# Copyright (C) 2012 Dominik Schulz <dominik.schulz@gauner.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.
|
||||||
|
#
|
||||||
|
# Script to monitor DJabberd servers.
|
||||||
|
# Mostly an adaptation of Guillaume Blairon's mogliefsd_activity script.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# ln -s /usr/share/munin/plugins/djabberd_ \
|
||||||
|
# /etc/munin/plugins/djabberd_{connections,memory,latency,counters}
|
||||||
|
#
|
||||||
|
# Configuration variables:
|
||||||
|
#
|
||||||
|
# host (default: '127.0.0.1')
|
||||||
|
# port (default: '5200')
|
||||||
|
#
|
||||||
|
# Parameters:
|
||||||
|
#
|
||||||
|
# config (required)
|
||||||
|
# autoconf (optional - only used by munin-config)
|
||||||
|
#
|
||||||
|
#%# family=auto
|
||||||
|
#%# capabilities=autoconf
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
use IO::Socket;
|
||||||
|
|
||||||
|
my $djabberd_host = $ENV{host} || "127.0.0.1";
|
||||||
|
my $djabberd_port = $ENV{port} || 5200;
|
||||||
|
my $mode = undef;
|
||||||
|
my $mode_name = undef;
|
||||||
|
|
||||||
|
# mapping mode to command from which we get the information
|
||||||
|
my $mode_ref = {
|
||||||
|
'connections' => {
|
||||||
|
'cmd' => 'stats',
|
||||||
|
'title' => 'DJabberd Connections',
|
||||||
|
'vlabel' => 'connections',
|
||||||
|
'fields' => {
|
||||||
|
'connections' => {
|
||||||
|
'key' => 'connections',
|
||||||
|
'label' => 'Connections',
|
||||||
|
'description' => 'Number of current connections',
|
||||||
|
'draw' => 'LINE1',
|
||||||
|
'type' => 'GAUGE',
|
||||||
|
},
|
||||||
|
'users' => {
|
||||||
|
'key' => 'users',
|
||||||
|
'label' => 'Users',
|
||||||
|
'description' => 'Number of connected users',
|
||||||
|
'draw' => 'LINE1',
|
||||||
|
'type' => 'GAUGE',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'memory' => {
|
||||||
|
'cmd' => 'stats',
|
||||||
|
'title' => 'DJabberd Memory Statistics',
|
||||||
|
'vlabel' => 'Bytes',
|
||||||
|
'fields' => {
|
||||||
|
'mem_total' => {
|
||||||
|
'key' => 'mem_total',
|
||||||
|
'label' => '',
|
||||||
|
'description' => 'Total memory used by DJabberd',
|
||||||
|
'draw' => 'LINE1',
|
||||||
|
'type' => 'GAUGE',
|
||||||
|
'filter' => \&kb2bytes,
|
||||||
|
},
|
||||||
|
'mem_connections' => {
|
||||||
|
'key' => 'mem_connections',
|
||||||
|
'label' => '',
|
||||||
|
'description' => 'Memory used for handling connections',
|
||||||
|
'draw' => 'LINE1',
|
||||||
|
'type' => 'GAUGE',
|
||||||
|
'filter' => \&kb2bytes,
|
||||||
|
},
|
||||||
|
'mem_per_connection' => {
|
||||||
|
'key' => 'mem_per_connection',
|
||||||
|
'label' => '',
|
||||||
|
'description' => 'Memory used per connection',
|
||||||
|
'draw' => 'LINE1',
|
||||||
|
'type' => 'GAUGE',
|
||||||
|
'filter' => \&kb2bytes,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'latency' => {
|
||||||
|
'cmd' => 'latency',
|
||||||
|
'title' => 'DJabberd Latency Statistics',
|
||||||
|
'vlabel' => 'reqs.',
|
||||||
|
'fields' => {
|
||||||
|
'dotzerozerozerofive' => {
|
||||||
|
'key' => '-0.0005',
|
||||||
|
'label' => 'lt. 0.0005',
|
||||||
|
'description' => 'Requests handled in lt. 0.0005s',
|
||||||
|
'draw' => 'AREA',
|
||||||
|
'type' => 'COUNTER',
|
||||||
|
},
|
||||||
|
'dotzerozeroone' => {
|
||||||
|
'key' => '-0.001',
|
||||||
|
'label' => 'lt. 0.001',
|
||||||
|
'description' => 'Requests handled int lt. 0.001s',
|
||||||
|
'draw' => 'STACK',
|
||||||
|
'type' => 'COUNTER',
|
||||||
|
},
|
||||||
|
'dotzerozerotwo' => {
|
||||||
|
'key' => '-0.002',
|
||||||
|
'label' => 'lt. 0.002',
|
||||||
|
'description' => 'Requests handled int lt. 0.002s',
|
||||||
|
'draw' => 'STACK',
|
||||||
|
'type' => 'COUNTER',
|
||||||
|
},
|
||||||
|
'dotzerozerofive' => {
|
||||||
|
'key' => '-0.005',
|
||||||
|
'label' => 'lt. 0.005',
|
||||||
|
'description' => 'Requests handled int lt. 0.005s',
|
||||||
|
'draw' => 'STACK',
|
||||||
|
'type' => 'COUNTER',
|
||||||
|
},
|
||||||
|
'dotzeroone' => {
|
||||||
|
'key' => '-0.01',
|
||||||
|
'label' => 'lt. 0.01',
|
||||||
|
'description' => 'Requests handled int lt. 0.01s',
|
||||||
|
'draw' => 'STACK',
|
||||||
|
'type' => 'COUNTER',
|
||||||
|
},
|
||||||
|
'dotzerotwo' => {
|
||||||
|
'key' => '-0.02',
|
||||||
|
'label' => 'lt. 0.02',
|
||||||
|
'description' => 'Requests handled int lt. 0.02s',
|
||||||
|
'draw' => 'STACK',
|
||||||
|
'type' => 'COUNTER',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'counters' => {
|
||||||
|
'cmd' => 'counters',
|
||||||
|
'title' => 'DJabberd Counters',
|
||||||
|
'vlabel' => 'msgs.',
|
||||||
|
'fields' => {
|
||||||
|
'clientin_djabberd_iq' => { 'key' => 'ClientIn:DJabberd::IQ', 'type' => 'COUNTER', },
|
||||||
|
'clientin_djabberd_message' => { 'key' => 'ClientIn:DJabberd::Message', 'type' => 'COUNTER', },
|
||||||
|
'clientin_djabberd_presence' => { 'key' => 'ClientIn:DJabberd::Presence', 'type' => 'COUNTER', },
|
||||||
|
'clientin_djabberd_stanza_sasl' => { 'key' => 'ClientIn:DJabberd::Stanza::SASL', 'type' => 'COUNTER', },
|
||||||
|
'clientin_djabberd_stanza_starttls' => { 'key' => 'ClientIn:DJabberd::Stanza::StartTLS', 'type' => 'COUNTER', },
|
||||||
|
'iniq_get_info_query' => { 'key' => 'InIQ:get-{http://jabber.org/protocol/disco#info}query', 'type' => 'COUNTER', },
|
||||||
|
'iniq_get_items_query' => { 'key' => 'InIQ:get-{http://jabber.org/protocol/disco#items}query', 'type' => 'COUNTER', },
|
||||||
|
'iniq_get_roster_query' => { 'key' => 'InIQ:get-{jabber:iq:roster}query', 'type' => 'COUNTER', },
|
||||||
|
'iniq_get_bind' => { 'key' => 'InIQ:set-{urn:ietf:params:xml:ns:xmpp-bind}bind', 'type' => 'COUNTER', },
|
||||||
|
'iniq_get_session' => { 'key' => 'InIQ:set-{urn:ietf:params:xml:ns:xmpp-session}session', 'type' => 'COUNTER', },
|
||||||
|
'serverin_djabberd_stanza_dialback_result' => { 'key' => 'ServerIn:DJabberd::Stanza::DialbackResult', 'type' => 'COUNTER', },
|
||||||
|
'serverin_djabberd_stanza_dialback_verify' => { 'key' => 'ServerIn:DJabberd::Stanza::DialbackVerify', 'type' => 'COUNTER', },
|
||||||
|
'auth_success' => { 'key' => 'auth_success', 'type' => 'COUNTER', },
|
||||||
|
'c2s_message' => { 'key' => 'c2s-Message', 'type' => 'COUNTER', },
|
||||||
|
'c2s_presence' => { 'key' => 'c2s-Presence', 'type' => 'COUNTER', },
|
||||||
|
'connect' => { 'key' => 'connect', 'type' => 'COUNTER', },
|
||||||
|
'deliver_local' => { 'key' => 'deliver_local', 'type' => 'COUNTER', },
|
||||||
|
'deliver_s2s' => { 'key' => 'deliver_s2s', 'type' => 'COUNTER', },
|
||||||
|
'disconnect' => { 'key' => 'disconnect', 'type' => 'COUNTER', },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
if ( $0 =~ m/djabberd_(.*)$/ && $mode_ref->{$1} ) {
|
||||||
|
$mode_name = $1;
|
||||||
|
$mode = $mode_ref->{$mode_name};
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
print STDERR "ERROR: Unknown mode '$mode'. Exiting.\n";
|
||||||
|
exit -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( $ARGV[0] && $ARGV[0] eq 'suggest' ) {
|
||||||
|
print join( "\n", keys %$mode_ref );
|
||||||
|
|
||||||
|
exit 0;
|
||||||
|
}
|
||||||
|
elsif ( $ARGV[0] && $ARGV[0] eq "autoconf" ) {
|
||||||
|
my $result_ref = &query_djabberd( $djabberd_host, $djabberd_port, $mode );
|
||||||
|
|
||||||
|
if ($result_ref) {
|
||||||
|
print "yes\n";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
print "no\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
exit 0;
|
||||||
|
}
|
||||||
|
elsif ( $ARGV[0] and $ARGV[0] eq "config" ) {
|
||||||
|
print "graph_title " . $mode->{'title'} . "\n";
|
||||||
|
print "graph_vlabel " . $mode->{'vlabel'} . "\n";
|
||||||
|
print "graph_args -l 0\n";
|
||||||
|
print "graph_category DJabberd\n";
|
||||||
|
foreach my $field_name ( keys %{ $mode->{'fields'} } ) {
|
||||||
|
my $label = $mode->{'fields'}->{$field_name}->{'label'} || $field_name;
|
||||||
|
my $desc = $mode->{'fields'}->{$field_name}->{'description'} || $mode->{'fields'}->{$field_name}->{'key'};
|
||||||
|
my $draw = $mode->{'fields'}->{$field_name}->{'draw'} || 'LINE1';
|
||||||
|
my $type = $mode->{'fields'}->{$field_name}->{'type'} || 'COUNTER';
|
||||||
|
|
||||||
|
print $field_name. '.label ' . $label . "\n";
|
||||||
|
print $field_name. '.description ' . $desc . "\n";
|
||||||
|
print $field_name. '.draw ' . $draw . "\n";
|
||||||
|
print $field_name. '.type ' . $type . "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
exit 0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
my $result_ref = &query_djabberd( $djabberd_host, $djabberd_port, $mode );
|
||||||
|
|
||||||
|
foreach my $field_name ( keys %{ $mode->{'fields'} } ) {
|
||||||
|
my $key = $mode->{'fields'}->{$field_name}->{'key'};
|
||||||
|
if ( defined( $result_ref->{$key} ) ) { # check for definedness, may well be zero (false for perl)
|
||||||
|
my $value = $result_ref->{$key};
|
||||||
|
|
||||||
|
# if there is a filter defined for this key apply it now
|
||||||
|
if ( exists( $mode->{'fields'}->{$field_name}->{'filter'} ) && ref( $mode->{'fields'}->{$field_name}->{'filter'} ) eq 'CODE' ) {
|
||||||
|
$value = &{ $mode->{'fields'}->{$field_name}->{'filter'} }($value);
|
||||||
|
}
|
||||||
|
print $field_name. ".value " . $value . "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub query_djabberd {
|
||||||
|
my ( $host, $port, $mode ) = @_;
|
||||||
|
|
||||||
|
my $conn = IO::Socket::INET::->new(
|
||||||
|
PeerAddr => $host,
|
||||||
|
PeerPort => $port,
|
||||||
|
Proto => 'tcp',
|
||||||
|
Timeout => 5,
|
||||||
|
) or die($!);
|
||||||
|
|
||||||
|
my $request = $mode->{'cmd'} . "\n";
|
||||||
|
|
||||||
|
$conn->syswrite( $request, length($request) );
|
||||||
|
|
||||||
|
my @lines = ();
|
||||||
|
while ( my $line = $conn->getline() ) {
|
||||||
|
if ( $line =~ /^\./ ) {
|
||||||
|
last;
|
||||||
|
}
|
||||||
|
push( @lines, $line );
|
||||||
|
}
|
||||||
|
close($conn);
|
||||||
|
|
||||||
|
my $result_ref = {};
|
||||||
|
foreach my $line (@lines) {
|
||||||
|
my ( $key, $value, $unit ) = split /\s+/, $line;
|
||||||
|
if ( $key && $value ) {
|
||||||
|
$result_ref->{$key} = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result_ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
# transform kb => bytes
|
||||||
|
sub kb2bytes {
|
||||||
|
|
||||||
|
my $num = shift;
|
||||||
|
|
||||||
|
if ( $num && $num =~ m/^\d+$/ ) {
|
||||||
|
$num *= 1024;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $num;
|
||||||
|
}
|
@ -43,7 +43,6 @@ shopt -s extglob
|
|||||||
|
|
||||||
EJCTL=$(which ejabberdctl)
|
EJCTL=$(which ejabberdctl)
|
||||||
EJVER=$($EJCTL status | awk '/^ejabberd / {print $2}')
|
EJVER=$($EJCTL status | awk '/^ejabberd / {print $2}')
|
||||||
MUNIN_DEBUG=${MUNIN_DEBUG:-0}
|
|
||||||
|
|
||||||
if [ "$1" == "autoconf" ]; then
|
if [ "$1" == "autoconf" ]; then
|
||||||
if [ -x $EJCTL > /dev/null ]; then
|
if [ -x $EJCTL > /dev/null ]; then
|
148
plugins/ejabberd/ejabberd_scanlog/ejabberd_scanlog
Executable file
148
plugins/ejabberd/ejabberd_scanlog/ejabberd_scanlog
Executable file
@ -0,0 +1,148 @@
|
|||||||
|
#!/usr/bin/env ruby
|
||||||
|
require 'yaml'
|
||||||
|
|
||||||
|
# ejabberd_scanlog revision 1 (Feb 2012)
|
||||||
|
#
|
||||||
|
# Scans ejabberd 2.1.x log for known error signatures and counts them
|
||||||
|
#
|
||||||
|
# Required privileges: read ejabberd log (user ejabberd or, in some cases, root)
|
||||||
|
#
|
||||||
|
# OS: Unix
|
||||||
|
#
|
||||||
|
# Configuration:
|
||||||
|
# env.log: ejabberd log file (defaults to /var...)
|
||||||
|
#
|
||||||
|
# Author: Artem Sheremet <dot.doom@gmail.com>
|
||||||
|
#
|
||||||
|
|
||||||
|
LOG_FILE = ENV['log'] || '/var/log/ejabberd/ejabberd.log'
|
||||||
|
CACHE_FILE = '/tmp/ejabberd_scanlog_cache' # cache file position
|
||||||
|
|
||||||
|
DEFAULT_CACHE = { :start => 0 }
|
||||||
|
|
||||||
|
debug_mode = ARGV.first == 'debug'
|
||||||
|
|
||||||
|
begin
|
||||||
|
log_info = YAML.load IO.read(CACHE_FILE)
|
||||||
|
rescue
|
||||||
|
log_info = DEFAULT_CACHE
|
||||||
|
end
|
||||||
|
|
||||||
|
if File.size(LOG_FILE) < log_info[:start]
|
||||||
|
# logrotate?
|
||||||
|
log_info = DEFAULT_CACHE
|
||||||
|
end
|
||||||
|
|
||||||
|
new_data = ''
|
||||||
|
File.open(LOG_FILE, 'r') do |flog|
|
||||||
|
flog.seek(log_info[:start])
|
||||||
|
new_data = flog.read
|
||||||
|
end
|
||||||
|
|
||||||
|
LABELS = {
|
||||||
|
:wait_for => 'EJAB-1482 Crash when waiting for item',
|
||||||
|
:ejabberd_odbc_failure => 'EJAB-1483 ODBC sup failure (wrong PID?)',
|
||||||
|
:ejabberd_odbc_failure_echo => 'EJAB-1483 ODBC sup wrong PID failure echo',
|
||||||
|
:dns => 'DNS failure',
|
||||||
|
:database => 'Database unavailable/too slow',
|
||||||
|
:auth_error => 'The auth module returned an error',
|
||||||
|
:timeout => 'State machine terminated: timeout',
|
||||||
|
:mysql_shutdown => 'MySQL disconnected',
|
||||||
|
:mysql_refused => 'Connecting to MySQL: failed',
|
||||||
|
:hook_timeout => 'Timeout while running a hook',
|
||||||
|
:sql_transactions_exceeded => 'SQL transaction restarts exceeded',
|
||||||
|
:unexpected_info => 'Unexpected info',
|
||||||
|
:other_sql_cmd_timeout => 'Other sql_cmd timeout',
|
||||||
|
:UNKNOWN => 'Unknown error/warning'
|
||||||
|
}
|
||||||
|
def log_type(text)
|
||||||
|
if text.include? 'ejabberd_odbc_sup'
|
||||||
|
:ejabberd_odbc_failure
|
||||||
|
elsif text.include? "mod_pubsub_odbc,'-unsubscribe"
|
||||||
|
:ejabberd_odbc_failure_echo
|
||||||
|
elsif text.include? 'You should check your DNS configuration'
|
||||||
|
:dns
|
||||||
|
elsif text.include? 'Database was not available or too slow'
|
||||||
|
:database
|
||||||
|
elsif text.include? 'wait_for_'
|
||||||
|
:wait_for
|
||||||
|
elsif text.include?('State machine') and
|
||||||
|
text.include?('terminating') and
|
||||||
|
text.include?('Reason for') and
|
||||||
|
text.include?('timeout')
|
||||||
|
:timeout
|
||||||
|
elsif text.include?('The authentication module') and
|
||||||
|
text.include?('returned an error')
|
||||||
|
:auth_error
|
||||||
|
elsif text.include?('mysql') and text.include?('Received unknown signal, exiting')
|
||||||
|
:mysql_shutdown
|
||||||
|
elsif text.include?('mysql') and text.include?('Failed connecting to')
|
||||||
|
:mysql_refused
|
||||||
|
elsif text.include?('ejabberd_hooks') and text.include?('timeout')
|
||||||
|
:hook_timeout
|
||||||
|
elsif text.include?('SQL transaction restarts exceeded')
|
||||||
|
:sql_transactions_exceeded
|
||||||
|
elsif text.include?('nexpected info')
|
||||||
|
:unexpected_info
|
||||||
|
elsif text.include?('timeout') and text.include?('sql_cmd')
|
||||||
|
:other_sql_cmd_timeout
|
||||||
|
else
|
||||||
|
puts "Cannot parse text: #{text}" if debug_mode
|
||||||
|
:UNKNOWN
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
new_data.split("\n=").each { |report|
|
||||||
|
next if report.empty?
|
||||||
|
report =~ /\A(\w+) REPORT==== (.*) ===\n(.*)\z/m
|
||||||
|
type, time, text = $1, $2, $3
|
||||||
|
next unless type and time and text
|
||||||
|
|
||||||
|
log_info[type] = (log_info[type] || 0) + 1
|
||||||
|
if sub_type = log_type(text)
|
||||||
|
log_info[sub_type] = (log_info[sub_type] || 0) + 1
|
||||||
|
end
|
||||||
|
}
|
||||||
|
|
||||||
|
log_info[:start] += new_data.size
|
||||||
|
File.open(CACHE_FILE, 'w') { |f| f.write log_info.to_yaml }
|
||||||
|
|
||||||
|
if ARGV.first == 'config'
|
||||||
|
puts <<CONFIG
|
||||||
|
graph_title Ejabberd Log
|
||||||
|
graph_vtitle Report count
|
||||||
|
graph_category ejabberd
|
||||||
|
graph_args -l 0
|
||||||
|
graph_order #{(LABELS.keys + log_info.keys.select { |k| k.is_a? String }.sort).join(' ')}
|
||||||
|
CONFIG
|
||||||
|
end
|
||||||
|
|
||||||
|
first = true
|
||||||
|
LABELS.each_pair { |type,label|
|
||||||
|
if ARGV.first == 'config'
|
||||||
|
puts "#{type}.label #{label}"
|
||||||
|
puts "#{type}.draw #{
|
||||||
|
if first
|
||||||
|
first = false
|
||||||
|
'AREA'
|
||||||
|
else
|
||||||
|
'STACK'
|
||||||
|
end
|
||||||
|
}"
|
||||||
|
else
|
||||||
|
puts "#{type}.value #{log_info[type] or 0}"
|
||||||
|
end
|
||||||
|
}
|
||||||
|
|
||||||
|
log_info.each_pair { |k,value|
|
||||||
|
unless k == :start
|
||||||
|
if k.is_a? String
|
||||||
|
if ARGV.first == 'config'
|
||||||
|
puts "#{k}.label #{k}"
|
||||||
|
puts "#{k}.draw LINE2"
|
||||||
|
else
|
||||||
|
puts "#{k}.value #{value}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
}
|
BIN
plugins/ejabberd/ejabberd_scanlog/ejabberd_scanlog.png
Normal file
BIN
plugins/ejabberd/ejabberd_scanlog/ejabberd_scanlog.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 36 KiB |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user