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.**
|
||||
|
||||
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
|
||||
|
||||
@ -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.
|
||||
|
||||
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
|
||||
#
|
||||
# $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
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
|
||||
#
|
||||
# 2011/05/20 - pmoranga - initial version
|
||||
#
|
||||
# 2012/01/27 - Sébastien Gross
|
||||
# - Fix lvdisplay path
|
||||
|
||||
lvdisplay=$(which lvdisplay)
|
||||
|
||||
if [ "$1" = "autoconf" ]; then
|
||||
/usr/sbin/lvdisplay 2>/dev/null >/dev/null
|
||||
if [ $? -eq 0 ]
|
||||
then
|
||||
if test -n "${lvdisplay}"; then
|
||||
echo yes
|
||||
exit 0
|
||||
else
|
||||
echo "no lvdisplay found"
|
||||
fi
|
||||
echo "no lvdisplay found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@ -34,9 +36,9 @@ if [ "$1" = "config" ]; then
|
||||
echo 'graph_vlabel %'
|
||||
echo 'graph_category disk'
|
||||
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
|
||||
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)
|
||||
EJVER=$($EJCTL status | awk '/^ejabberd / {print $2}')
|
||||
MUNIN_DEBUG=${MUNIN_DEBUG:-0}
|
||||
|
||||
if [ "$1" == "autoconf" ]; 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