diff --git a/README.md b/README.md index 84652e99..e9b3d4cc 100644 --- a/README.md +++ b/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. diff --git a/images/README.md b/images/README.md new file mode 100644 index 00000000..fedf12ba --- /dev/null +++ b/images/README.md @@ -0,0 +1,2 @@ +Please **don't** put screenshots of your plugins here. +Put them right next to your plugins. diff --git a/plugins/other/amule_queue b/plugins/amule/amule_queue similarity index 100% rename from plugins/other/amule_queue rename to plugins/amule/amule_queue diff --git a/plugins/other/amule_shares b/plugins/amule/amule_shares similarity index 100% rename from plugins/other/amule_shares rename to plugins/amule/amule_shares diff --git a/plugins/other/amule_transfers b/plugins/amule/amule_transfers similarity index 100% rename from plugins/other/amule_transfers rename to plugins/amule/amule_transfers diff --git a/plugins/other/amule_uptime b/plugins/amule/amule_uptime similarity index 100% rename from plugins/other/amule_uptime rename to plugins/amule/amule_uptime diff --git a/plugins/other/apache_activity b/plugins/apache/apache_activity similarity index 100% rename from plugins/other/apache_activity rename to plugins/apache/apache_activity diff --git a/plugins/other/apache_average_time_last_n_requests b/plugins/apache/apache_average_time_last_n_requests similarity index 100% rename from plugins/other/apache_average_time_last_n_requests rename to plugins/apache/apache_average_time_last_n_requests diff --git a/plugins/other/apache_request_rate b/plugins/apache/apache_request_rate similarity index 100% rename from plugins/other/apache_request_rate rename to plugins/apache/apache_request_rate diff --git a/plugins/other/apache_smaps b/plugins/apache/apache_smaps similarity index 100% rename from plugins/other/apache_smaps rename to plugins/apache/apache_smaps diff --git a/plugins/other/apache_users b/plugins/apache/apache_users similarity index 100% rename from plugins/other/apache_users rename to plugins/apache/apache_users diff --git a/plugins/other/apache_watch_ b/plugins/apache/apache_watch_ similarity index 100% rename from plugins/other/apache_watch_ rename to plugins/apache/apache_watch_ diff --git a/plugins/other/eaccelerator b/plugins/apache/eaccelerator similarity index 100% rename from plugins/other/eaccelerator rename to plugins/apache/eaccelerator diff --git a/plugins/other/http-response-times b/plugins/apache/http-response-times similarity index 100% rename from plugins/other/http-response-times rename to plugins/apache/http-response-times diff --git a/plugins/other/php-cgi b/plugins/apache/php-cgi similarity index 100% rename from plugins/other/php-cgi rename to plugins/apache/php-cgi diff --git a/plugins/other/php_eaccelerator b/plugins/apache/php_eaccelerator similarity index 100% rename from plugins/other/php_eaccelerator rename to plugins/apache/php_eaccelerator diff --git a/plugins/other/php_sessions b/plugins/apache/php_sessions similarity index 100% rename from plugins/other/php_sessions rename to plugins/apache/php_sessions diff --git a/plugins/other/apt-proxy b/plugins/apt/apt-proxy similarity index 100% rename from plugins/other/apt-proxy rename to plugins/apt/apt-proxy diff --git a/plugins/other/asterisk_inuse b/plugins/asterisk/asterisk_inuse similarity index 100% rename from plugins/other/asterisk_inuse rename to plugins/asterisk/asterisk_inuse diff --git a/plugins/other/asterisk_sippeers b/plugins/asterisk/asterisk_sippeers similarity index 100% rename from plugins/other/asterisk_sippeers rename to plugins/asterisk/asterisk_sippeers diff --git a/plugins/other/portaudit b/plugins/audit/portaudit similarity index 100% rename from plugins/other/portaudit rename to plugins/audit/portaudit diff --git a/plugins/other/bacula_job b/plugins/bacula/bacula_job similarity index 100% rename from plugins/other/bacula_job rename to plugins/bacula/bacula_job diff --git a/plugins/other/bacula_sd b/plugins/bacula/bacula_sd similarity index 100% rename from plugins/other/bacula_sd rename to plugins/bacula/bacula_sd diff --git a/plugins/other/boinc_credit b/plugins/boinc/boinc_credit similarity index 100% rename from plugins/other/boinc_credit rename to plugins/boinc/boinc_credit diff --git a/plugins/other/boinc_estwk b/plugins/boinc/boinc_estwk similarity index 100% rename from plugins/other/boinc_estwk rename to plugins/boinc/boinc_estwk diff --git a/plugins/other/boinc_projs b/plugins/boinc/boinc_projs similarity index 100% rename from plugins/other/boinc_projs rename to plugins/boinc/boinc_projs diff --git a/plugins/other/boinc_wus b/plugins/boinc/boinc_wus similarity index 100% rename from plugins/other/boinc_wus rename to plugins/boinc/boinc_wus diff --git a/plugins/other/cacti-host b/plugins/cacti/cacti-host similarity index 100% rename from plugins/other/cacti-host rename to plugins/cacti/cacti-host diff --git a/plugins/other/cacti_poller_time b/plugins/cacti/cacti_poller_time similarity index 100% rename from plugins/other/cacti_poller_time rename to plugins/cacti/cacti_poller_time diff --git a/plugins/other/cacti_rrds b/plugins/cacti/cacti_rrds similarity index 100% rename from plugins/other/cacti_rrds rename to plugins/cacti/cacti_rrds diff --git a/plugins/other/celery_tasks b/plugins/celery/celery_tasks similarity index 100% rename from plugins/other/celery_tasks rename to plugins/celery/celery_tasks diff --git a/plugins/other/celery_tasks-2 b/plugins/celery/celery_tasks-2 similarity index 100% rename from plugins/other/celery_tasks-2 rename to plugins/celery/celery_tasks-2 diff --git a/plugins/other/dell_fans b/plugins/chassis/dell_fans similarity index 100% rename from plugins/other/dell_fans rename to plugins/chassis/dell_fans diff --git a/plugins/other/munin-plugin-for-cherokee b/plugins/cherokee/munin-plugin-for-cherokee similarity index 100% rename from plugins/other/munin-plugin-for-cherokee rename to plugins/cherokee/munin-plugin-for-cherokee diff --git a/plugins/other/condor_activity_ b/plugins/condor/condor_activity_ similarity index 100% rename from plugins/other/condor_activity_ rename to plugins/condor/condor_activity_ diff --git a/plugins/other/condor_ops_ b/plugins/condor/condor_ops_ similarity index 100% rename from plugins/other/condor_ops_ rename to plugins/condor/condor_ops_ diff --git a/plugins/other/condor_queue_ b/plugins/condor/condor_queue_ similarity index 100% rename from plugins/other/condor_queue_ rename to plugins/condor/condor_queue_ diff --git a/plugins/other/condor_states_ b/plugins/condor/condor_states_ similarity index 100% rename from plugins/other/condor_states_ rename to plugins/condor/condor_states_ diff --git a/plugins/other/cpanp_o b/plugins/cpan/cpanp_o similarity index 100% rename from plugins/other/cpanp_o rename to plugins/cpan/cpanp_o diff --git a/plugins/other/currentcost b/plugins/currentcost/currentcost similarity index 100% rename from plugins/other/currentcost rename to plugins/currentcost/currentcost diff --git a/plugins/other/cyrus-imapd b/plugins/cyrus/cyrus-imapd similarity index 93% rename from plugins/other/cyrus-imapd rename to plugins/cyrus/cyrus-imapd index 5345caa0..d765af6e 100755 --- a/plugins/other/cyrus-imapd +++ b/plugins/cyrus/cyrus-imapd @@ -1,8 +1,6 @@ #!/bin/sh # -# $Id: cyrus-imapd 18 2011-07-15 09:14:04Z ixs $ -# -# Copyright (C) 2009-2011 Andreas Thienemann +# Copyright (C) 2009 - 2012 Andreas Thienemann # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU Library General Public License as published by @@ -51,7 +49,7 @@ It displays the following three datapoints: =head1 VERSION - $Revision: 18 $ + 0.0.20120307 =head1 BUGS @@ -71,7 +69,7 @@ GPLv2 CONFIGDIR=$(awk -F : '/^configdirectory:/ { gsub(/ /, "", $2); print $2 }' /etc/imapd.conf 2> /dev/null) PROCDIR="${CONFIGDIR}/proc" -if [ "$1" = "autoconf" ]; then +if [ "$1" == "autoconf" ]; then if [ "x${CONFIGDIR}x" != "xx" ] && [ -d ${PROCDIR} ]; then echo yes else @@ -81,14 +79,14 @@ if [ "$1" = "autoconf" ]; then fi # Check if we actually got some sensible data -if [ "x${CONFIGDIR}x" = "xx" ]; then +if [ "x${CONFIGDIR}x" == "xx" ]; then exit 1 fi # If run with the "config"-parameter, give out information on how the # graphs should look. -if [ "$1" = "config" ]; then +if [ "$1" == "config" ]; then echo 'graph_title Cyrus IMAPd Load' echo 'graph_args --base 1000 -l 0' echo 'graph_vlabel connections' diff --git a/plugins/other/df_abs_bsd b/plugins/disk/df_abs_bsd similarity index 100% rename from plugins/other/df_abs_bsd rename to plugins/disk/df_abs_bsd diff --git a/plugins/other/df_bsd b/plugins/disk/df_bsd similarity index 100% rename from plugins/other/df_bsd rename to plugins/disk/df_bsd diff --git a/plugins/other/df_with_nfs b/plugins/disk/df_with_nfs similarity index 100% rename from plugins/other/df_with_nfs rename to plugins/disk/df_with_nfs diff --git a/plugins/other/dirsizes b/plugins/disk/dirsizes similarity index 100% rename from plugins/other/dirsizes rename to plugins/disk/dirsizes diff --git a/plugins/other/du b/plugins/disk/du similarity index 100% rename from plugins/other/du rename to plugins/disk/du diff --git a/plugins/other/du-2 b/plugins/disk/du-2 similarity index 100% rename from plugins/other/du-2 rename to plugins/disk/du-2 diff --git a/plugins/other/du_multidirs b/plugins/disk/du_multidirs similarity index 100% rename from plugins/other/du_multidirs rename to plugins/disk/du_multidirs diff --git a/plugins/disk/file_age b/plugins/disk/file_age new file mode 100755 index 00000000..29636d14 --- /dev/null +++ b/plugins/disk/file_age @@ -0,0 +1,133 @@ +#!/bin/bash + +. $MUNIN_LIBDIR/plugins/plugin.sh + +case $1 in + config) + GRAPH_ORDER="" + COUNTER=1 + while [ $COUNTER -gt 0 ]; do + FILE_PATH="file${COUNTER}_path" + + # Is the path for this file specified? + eval FILE=\$$FILE_PATH + if [ "$FILE" == "" ]; then + break; + fi + + # It is! Add it to the graphs. + GRAPH_ORDER="$GRAPH_ORDER file_$COUNTER" + + # Does this file have a specified label? + LABEL_COUNTER="file${COUNTER}_label" + eval LABEL=\$$LABEL_COUNTER + if [ "$LABEL" == "" ]; then + LABEL=`basename $FILE` + fi + + # Associated warning level? + WARNING="file${COUNTER}_warning" + eval WARNING=\$$WARNING + if [ "$WARNING" != "" ]; then + echo "file_$COUNTER.warning $WARNING" + fi + + # Associated critical level? + CRITICAL="file${COUNTER}_critical" + eval CRITICAL=\$$CRITICAL + if [ "$CRITICAL" != "" ]; then + echo "file_$COUNTER.critical $CRITICAL" + fi + + echo "file_$COUNTER.label $LABEL" + echo "file_$COUNTER.type GAUGE" + echo "file_$COUNTER.min 0" + let COUNTER=COUNTER+1 + done; + + echo "graph_order $GRAPH_ORDER" + echo "graph_title File age" + echo 'graph_args --base 1000 -l 0' + echo 'graph_vlabel seconds' + echo 'graph_category disk' + + exit 0 + ;; +esac + + +COUNTER=1 +while [ $COUNTER -gt 0 ]; do + FILE_COUNTER="file${COUNTER}_path" + eval FILE=\$$FILE_COUNTER + if [ "$FILE" == "" ]; then + break; + fi + + # If the file isn't readable, say it's zero. + if [ ! -r "$FILE" ]; then + VALUE=0 + else + VALUE=$(($(date +%s) - $(stat -c '%Y' "$FILE"))) + fi + + echo "file_$COUNTER.value $VALUE" + let COUNTER=COUNTER+1 +done; +exit + +# -*- sh -*- + +: << =cut + +=head1 NAME + +file_age - Monitors the age of files. + +=head1 CONFIGURATION + +Since there is no way for the plugin to guess which files you want monitored, you're going to have to set each file up separately. Put the following in a file in your plugin-conf.d directory. + +[file_age] + user root # May not be necessary, depending on which files you want monitored. + + env.file1_path /var/log/syslog # Mandatory, complete path to file. + env.file1_label System syslog # Optional label if you don't want the file name to be displayed. + env.file1_warning 86400 # Optional warning level. Measured in seconds. 86400 is one day of seconds. + env.file1_critical 864000 # Optional critical level. Measured in seconds. + +Continue with file2, file3, etc... + +Here, have some seconds: + + 3600 One hour + 7300 Two hours + 10800 Three hours + 21600 Six hours + 43200 Twelve hours + 86400 One day + 172800 Two days + 259200 Three days + 604800 One week + +=head1 AUTHOR + +Edward Plainview + +=head1 DONATIONS + +If you wish to donate money for this plugin, please read https://it.sverigedemokraterna.se/donera/ + +=head1 LICENSE + +GPLv3 + +=head1 MAGIC MARKERS + + #%# family=auto + +=head1 VERSION + +1.0 released 2012-02-26 + +=cut diff --git a/plugins/other/freedisk b/plugins/disk/freedisk similarity index 100% rename from plugins/other/freedisk rename to plugins/disk/freedisk diff --git a/plugins/other/iostat b/plugins/disk/iostat similarity index 100% rename from plugins/other/iostat rename to plugins/disk/iostat diff --git a/plugins/other/iostat-xfrs b/plugins/disk/iostat-xfrs similarity index 100% rename from plugins/other/iostat-xfrs rename to plugins/disk/iostat-xfrs diff --git a/plugins/other/linux_diskstat_ b/plugins/disk/linux_diskstat_ similarity index 100% rename from plugins/other/linux_diskstat_ rename to plugins/disk/linux_diskstat_ diff --git a/plugins/other/linux_diskstats_ b/plugins/disk/linux_diskstats_ similarity index 100% rename from plugins/other/linux_diskstats_ rename to plugins/disk/linux_diskstats_ diff --git a/plugins/other/log_sizes b/plugins/disk/log_sizes similarity index 100% rename from plugins/other/log_sizes rename to plugins/disk/log_sizes diff --git a/plugins/other/lvm_ b/plugins/disk/lvm_ similarity index 100% rename from plugins/other/lvm_ rename to plugins/disk/lvm_ diff --git a/plugins/other/lvm_snap_used b/plugins/disk/lvm_snap_used similarity index 56% rename from plugins/other/lvm_snap_used rename to plugins/disk/lvm_snap_used index cfcdc241..87574a24 100755 --- a/plugins/other/lvm_snap_used +++ b/plugins/disk/lvm_snap_used @@ -14,17 +14,19 @@ #%# 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 - echo yes - exit 0 - else - echo "no lvdisplay found" - fi - exit 1 + if test -n "${lvdisplay}"; then + echo yes + exit 0 + 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)} ' diff --git a/plugins/other/lvm_usage b/plugins/disk/lvm_usage similarity index 100% rename from plugins/other/lvm_usage rename to plugins/disk/lvm_usage diff --git a/plugins/other/md_iostat_ b/plugins/disk/md_iostat_ similarity index 100% rename from plugins/other/md_iostat_ rename to plugins/disk/md_iostat_ diff --git a/plugins/other/megaraid-controller-information b/plugins/disk/megaraid-controller-information similarity index 100% rename from plugins/other/megaraid-controller-information rename to plugins/disk/megaraid-controller-information diff --git a/plugins/other/raid b/plugins/disk/raid similarity index 100% rename from plugins/other/raid rename to plugins/disk/raid diff --git a/plugins/other/raid-mismatch-count b/plugins/disk/raid-mismatch-count similarity index 100% rename from plugins/other/raid-mismatch-count rename to plugins/disk/raid-mismatch-count diff --git a/plugins/other/scsi_queue b/plugins/disk/scsi_queue similarity index 100% rename from plugins/other/scsi_queue rename to plugins/disk/scsi_queue diff --git a/plugins/other/smart b/plugins/disk/smart similarity index 100% rename from plugins/other/smart rename to plugins/disk/smart diff --git a/plugins/other/smart-by-id_ b/plugins/disk/smart-by-id_ similarity index 100% rename from plugins/other/smart-by-id_ rename to plugins/disk/smart-by-id_ diff --git a/plugins/other/smart_ b/plugins/disk/smart_ similarity index 100% rename from plugins/other/smart_ rename to plugins/disk/smart_ diff --git a/plugins/disk/smart_raw__ b/plugins/disk/smart_raw__ new file mode 100755 index 00000000..e466a7dd --- /dev/null +++ b/plugins/disk/smart_raw__ @@ -0,0 +1,145 @@ + #!/usr/bin/python +# +# Copyright (C) 2011 Andreas Thienemann +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +""" +=head1 NAME + +smart_raw__ - Munin plugin to retreive raw SMART values from a disk. + +=head1 APPLICABLE SYSTEMS + +All machines with a SMART capable disk and smartmontools installed. + +This plugin is very useful if you want to need to monitor a single raw S.M.A.R.T. +value reported by a disk. Load Cycle Counts or Pending Sectors come to mind as +these are a good indicator of problems with a disk. + +=head1 CONFIGURATION + +The plugin should be installed as smart_raw_sda_193 which means that the smart value +numbered 193 will be read from the disk sda. + + +Basic configuration for every system is that the plugin needs to be called as root +in order to execute smartmontools. + +Add the following to your /etc/munin/plugin-conf.d/smart_raw: + + [smart_raw_sda_193] + user root + +=head1 INTERPRETATION + +Smart RAW values are provided as is. You need to undertand what you are monitoring... + +=head1 MAGIC MARKERS + + #%# family=contrib + #%# capabilities= + +=head1 VERSION + +0.0.1 + +=head1 BUGS + +None known. + +=head1 AUTHOR + +Andreas Thienemann + +=head1 LICENSE + +GPLv3+ + +=cut +""" + +import subprocess +import sys +import os +import re +import pprint + +# We are a wildcard plugin, figure out what we are being called for +try: + disk = sys.argv[0].split('_')[2] + value = sys.argv[0].split('_')[3] +except IndexError: + sys.exit(1) + +def normalize_name(name): + name = re.sub("[^a-z0-9A-Z]","_", name) + return name + +# Code sniplet from Philipp Keller +# http://code.pui.ch/2007/02/19/set-timeout-for-a-shell-command-in-python/ +def timeout_command(command, timeout): + """call shell-command and either return its output or kill it + if it doesn't normally exit within timeout seconds and return None""" + import subprocess, datetime, os, time, signal + start = datetime.datetime.now() + process = subprocess.Popen(command.split(), stdout=subprocess.PIPE, stderr=subprocess.PIPE) + while process.poll() is None: + time.sleep(0.1) + now = datetime.datetime.now() + if (now - start).seconds> timeout: + os.kill(process.pid, signal.SIGKILL) + os.waitpid(-1, os.WNOHANG) + return None + return process.stdout.read() + +def read_smart(): + """Read SMART attributes""" + out = timeout_command("/usr/sbin/smartctl -A -d ata /dev/%s" % (disk), 2) + smart_attribs = dict() + for line in out.split('\n')[7:-2]: # Cut away the header and the footer + line = line.split() + smart_attribs[line[0]] = { + 'name' : line[1], + 'label' : normalize_name(line[1]), + 'raw' : line [-1], + } + return smart_attribs + +def print_config(): + """Return configuration arguments for munin""" + attribs = read_smart() + print "graph_title S.M.A.R.T raw value %s for drive %s" % (attribs[value]['name'], disk) + print "graph_vlabel Raw value" + print "graph_info RAW smartctl value" + print "graph_category disk" + print "graph_args --base 1000 -l 0" + + print "%s.label %s" % (value, attribs[value]['label']) + sys.exit(0) + +def fetch(): + attribs = read_smart() + print "%s.value %s" % (value, attribs[value]['raw']) + sys.exit(0) + +if "config" in sys.argv[1:]: + print_config() +elif "autoconf" in sys.argv[1:]: + pass +elif "suggest" in sys.argv[1:]: + pass +else: + fetch() diff --git a/plugins/other/snmp__netapp_diskusage_ b/plugins/disk/snmp__netapp_diskusage_ similarity index 100% rename from plugins/other/snmp__netapp_diskusage_ rename to plugins/disk/snmp__netapp_diskusage_ diff --git a/plugins/other/snmp__netapp_inodeusage_ b/plugins/disk/snmp__netapp_inodeusage_ similarity index 100% rename from plugins/other/snmp__netapp_inodeusage_ rename to plugins/disk/snmp__netapp_inodeusage_ diff --git a/plugins/other/snmp__swap b/plugins/disk/snmp__swap similarity index 100% rename from plugins/other/snmp__swap rename to plugins/disk/snmp__swap diff --git a/plugins/other/xfs_frag b/plugins/disk/xfs_frag similarity index 100% rename from plugins/other/xfs_frag rename to plugins/disk/xfs_frag diff --git a/plugins/djabberd/djabberd_ b/plugins/djabberd/djabberd_ new file mode 100644 index 00000000..247af90c --- /dev/null +++ b/plugins/djabberd/djabberd_ @@ -0,0 +1,285 @@ +#!/usr/bin/perl -w +# +# Copyright (C) 2012 Dominik Schulz +# +# 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; +} diff --git a/plugins/other/drbd b/plugins/drbd/drbd similarity index 100% rename from plugins/other/drbd rename to plugins/drbd/drbd diff --git a/plugins/other/drbd-stat b/plugins/drbd/drbd-stat similarity index 100% rename from plugins/other/drbd-stat rename to plugins/drbd/drbd-stat diff --git a/plugins/other/dspam_ b/plugins/dspam/dspam_ similarity index 100% rename from plugins/other/dspam_ rename to plugins/dspam/dspam_ diff --git a/plugins/other/dspam_activity b/plugins/dspam/dspam_activity similarity index 100% rename from plugins/other/dspam_activity rename to plugins/dspam/dspam_activity diff --git a/plugins/other/femon b/plugins/dvb/femon similarity index 100% rename from plugins/other/femon rename to plugins/dvb/femon diff --git a/plugins/other/hadoop-under_replicated-blocks b/plugins/dxtv/hadoop-under_replicated-blocks similarity index 100% rename from plugins/other/hadoop-under_replicated-blocks rename to plugins/dxtv/hadoop-under_replicated-blocks diff --git a/plugins/other/ejabberd_ b/plugins/ejabberd/ejabberd_ similarity index 99% rename from plugins/other/ejabberd_ rename to plugins/ejabberd/ejabberd_ index 0fee887e..b7757d42 100755 --- a/plugins/other/ejabberd_ +++ b/plugins/ejabberd/ejabberd_ @@ -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 diff --git a/plugins/ejabberd/ejabberd_scanlog/ejabberd_scanlog b/plugins/ejabberd/ejabberd_scanlog/ejabberd_scanlog new file mode 100755 index 00000000..1daf8626 --- /dev/null +++ b/plugins/ejabberd/ejabberd_scanlog/ejabberd_scanlog @@ -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 +# + +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 < $value) { + if ($section != 'settings') + $servers[$section] = array($value['game'], $value['address'], $value['port']); + } + + // Create a new GameQ object and pass it a list of servers + $gq = new GameQ(); + $gq->addServers($servers); + + // Set timeout from the config file + $gq->setOption('timeout', $ini_array['settings']['timeout']); + $gq->setOption('sock_count', $ini_array['settings']['sock_count']); + $gq->setOption('sock_start', $ini_array['settings']['sock_start']); + + // This filter makes sure a subset of data is always available, next to the normal data + $gq->setFilter('normalise'); + + // Send request(s) + $results = $gq->requestData(); + + return $results; +} + +// Parse command line arguments if required. +if (isset($_SERVER['argv'][1])) { + if ($_SERVER['argv'][1] == 'config') { + // Load our config.ini + $ini_array = parse_ini_file($config, true); + + // Load games.ini so we can show pretty game names + $games = parse_ini_file('gameq/GameQ/games.ini', true); + + // Query the game servers + $results = queryServers($ini_array); + // Cache the query in the state file + $fp = fopen($state, 'w+') or die("I could not open state file."); + fwrite($fp, serialize($results)); + fclose($fp); + + + // Loop through each server, printing graphs. + foreach ($results as $name => $server) { + // If sub graphs and the game total graphs are enabled, make this be a sub-graph. + if ($ini_array['settings']['show_game_total'] && $ini_array['settings']['enable_sub_graphs']) + $machine_name = "gameserver_" . $ini_array[$name]['game'] . ".$name"; + else + $machine_name = "gameserver_" . $ini_array[$name]['game'] . "_$name"; + $title = $ini_array[$name]['name'] . " players"; + $info = "The number of players connected to the " . $games[$ini_array[$name]['game']]['name'] . " server"; + printMultigraph($ini_array, $machine_name, $title, $info, $server['gq_maxplayers']); + } + + // Game total graphs. + if ($ini_array['settings']['show_game_total']) { + $game_total_max = array(); + + // Count players connected to each game + foreach ($results as $name => $server) { + if (!isset($game_total_max[$ini_array[$name]['game']])) + $game_total_max[$ini_array[$name]['game']] = $server['gq_maxplayers']; + else + $game_total_max[$ini_array[$name]['game']] += $server['gq_maxplayers']; + } + + // Print all the game total graphs. + foreach ($game_total_max as $game => $total) + { + $machine_name = "gameserver_" . $game; + $title = "Total " . $games[$game]['name'] . " players"; + $info = "Total players connected to all " . $games[$game]['name'] . " servers"; + printMultigraph($ini_array, $machine_name, $title, $info, $total); + } + } + + // Global total graph + if ($ini_array['settings']['show_global_total']) { + $total_max = 0; + // Add up all the players connected to all the servers + foreach ($results as $name => $server) { + $total_max += $server['gq_maxplayers']; + } + + printMultigraph($ini_array, "gameserver", "Total Players", "Total players connected to all servers", $total_max); + } + } + if ($_SERVER['argv'][1] == 'autoconf') { + if (!@include("gameq/GameQ.php")) + p("no (GameQ Library not found)"); + elseif (!file_exists($config)) + p("no ($config not found)"); + else + p("yes"); + } + die(); +} + +// No command line arguments, print values. + +// Load our config.ini +$ini_array = parse_ini_file($config, true); + +// Load games.ini so we can show pretty game names +$games = parse_ini_file('gameq/GameQ/games.ini', true); + +$results = unserialize(file_get_contents($state)); + +// Print individual game values +foreach ($results as $name => $server){ + if ($ini_array['settings']['show_game_total'] && $ini_array['settings']['enable_sub_graphs']) + $machine_name = "gameserver_" . $ini_array[$name]['game'] . ".$name"; + else + $machine_name = "gameserver_" . $ini_array[$name]['game'] . "_$name"; + printValue($machine_name, $server['gq_numplayers']); +} + +// Print game total values +if ($ini_array['settings']['show_game_total']) { + $game_total = array(); + foreach ($results as $name => $server) { + // Add up counts for the total graph + if (!isset($game_total_max[$ini_array[$name]['game']])) + $game_total[$ini_array[$name]['game']] = $server['gq_numplayers']; + else + $game_total[$ini_array[$name]['game']] += $server['gq_numplayers']; + } + foreach ($game_total as $game => $total) + { + $machine_name = "gameserver_" . $game; + printValue($machine_name, $total); + } +} + +// Are global totals enabled? +if ($ini_array['settings']['show_global_total']) { + $total = 0; + foreach ($results as $name => $server) { + $total += $server['gq_numplayers']; + } + printValue("gameserver", $total); +} + + +?> diff --git a/plugins/games/munin-game.ini b/plugins/games/munin-game.ini new file mode 100644 index 00000000..d03d1d0a --- /dev/null +++ b/plugins/games/munin-game.ini @@ -0,0 +1,28 @@ +[settings] +timeout = 1000 ; Timeout in ms when attempting to connect to servers +sock_count = 64 ; Maximum number of sockets that are used simultaneously +sock_start = 0 ; Start of port range to use + +; Note that changing any of the below options may change your graph names, which may reset your statistics. +show_global_total = True ; Show a graph that shows the total players connected to all servers? +show_game_total = True ; Show a graph that shows the total players connected to all servers of the same type? +enable_sub_graphs = True ; Make each game be a sub-graph of it's game total graph. + +; The following variables allow you to override the style of graphs. +; The server graphs are named gameserver_gamename.machinename, for example gameserver_tf2.myamazingserver +; The game total graphs are named gameserver_gamename, for example gameserver_tf2 +; The global total graph is named gameserver. +;graph_name_colour = 000000 ; Override the color of graph_name, for example gameserver_et.myetserver_color +;graph_name_draw = LINE1 ; Override the draw method of graph_name, for example gameserver_ + +[mytf2server] ; Machine name of the server +name = "My TF2 Server" ; Display name of the server +game = tf2 ; Game type, see GameQ/games.ini inside the GameQ library for a list of valid options. +address = 1.2.3.4 ; Server IP or hostname. +port = 27015 ; Server port + +[myetserver] +name = "My Enemy Territory Server" +game = et +address = 1.2.3.4 +port = 27960 diff --git a/plugins/other/qstat b/plugins/games/qstat similarity index 100% rename from plugins/other/qstat rename to plugins/games/qstat diff --git a/plugins/other/qstatet_ b/plugins/games/qstatet_ similarity index 100% rename from plugins/other/qstatet_ rename to plugins/games/qstatet_ diff --git a/plugins/other/qstatqw_ b/plugins/games/qstatqw_ similarity index 100% rename from plugins/other/qstatqw_ rename to plugins/games/qstatqw_ diff --git a/plugins/other/geowebcache-bandwidth b/plugins/geowebcache/geowebcache-bandwidth similarity index 100% rename from plugins/other/geowebcache-bandwidth rename to plugins/geowebcache/geowebcache-bandwidth diff --git a/plugins/other/geowebcache-blankitems b/plugins/geowebcache/geowebcache-blankitems similarity index 100% rename from plugins/other/geowebcache-blankitems rename to plugins/geowebcache/geowebcache-blankitems diff --git a/plugins/other/geowebcache-cache-hits-ratio b/plugins/geowebcache/geowebcache-cache-hits-ratio similarity index 100% rename from plugins/other/geowebcache-cache-hits-ratio rename to plugins/geowebcache/geowebcache-cache-hits-ratio diff --git a/plugins/other/googlecode b/plugins/google/googlecode similarity index 100% rename from plugins/other/googlecode rename to plugins/google/googlecode diff --git a/plugins/other/nvidia_smi_ b/plugins/gpu/nvidia_smi_ similarity index 100% rename from plugins/other/nvidia_smi_ rename to plugins/gpu/nvidia_smi_ diff --git a/plugins/other/snmp__gwia_msgs_ b/plugins/groupwise/snmp__gwia_msgs_ similarity index 100% rename from plugins/other/snmp__gwia_msgs_ rename to plugins/groupwise/snmp__gwia_msgs_ diff --git a/plugins/other/snmp__gwmta_msgs_ b/plugins/groupwise/snmp__gwmta_msgs_ similarity index 100% rename from plugins/other/snmp__gwmta_msgs_ rename to plugins/groupwise/snmp__gwmta_msgs_ diff --git a/plugins/gunicorn/gunicorn_memory_status b/plugins/gunicorn/gunicorn_memory_status new file mode 100755 index 00000000..49e8719c --- /dev/null +++ b/plugins/gunicorn/gunicorn_memory_status @@ -0,0 +1,98 @@ +#!/usr/bin/env python +""" + gunicorn_status - A munin plugin for Linux to monitor the memory + usage of gunicorn processes + + Copyright (C) 2012 Azavea, Inc. + Author: Andrew Jennings + + Like Munin, this plugin is licensed under the GNU GPL v2 license + http://www.opensource.org/licenses/GPL-2.0 + + If you've put your gunicorn pid somewhere other than the + default /var/run/gunicorn.pid, you can add a section like + this to your munin-node's plugin configuration: + + [gunicorn_*] + env.gunicorn_pid_path [path to your gunicorn pid] + + This plugin supports the following munin configuration parameters: + #%# family=auto contrib + #%# capabilities=autoconf + +""" + +import sys, os +from subprocess import check_output + +# set path to your gunicorn pid +try: + GUNICORN_PID_PATH = os.environ['gunicorn_pid_path'] +except: + GUNICORN_PID_PATH = "/var/run/gunicorn.pid" + + +class GunicornMemoryStatus(): + master_pid = '' + """ + The Gunicorn master process pid, as a string + """ + + def __init__(self): + try: + self._get_master_pid() + except: + raise Exception("Couldn't read gunicorn pid information") + + + def print_total_memory(self): + print ('total_memory.value %d' % self._get_total_memory()) + + + def _get_master_pid(self): + master_pid_file = open(GUNICORN_PID_PATH) + self.master_pid = master_pid_file.read().rstrip() + master_pid_file.close() + return True + + def _get_total_memory(self): + master = self._get_master_memory() + total = master +self. _get_worker_memory() + total_in_mb = total / 1024 + return total_in_mb + + def _get_master_memory(self): + master = int(check_output( + ['ps', '--pid', self.master_pid, '-o', 'rss', '--no-headers'])) + return master + + def _get_worker_memory(self): + worker_processes = check_output( + ['ps', '--ppid', self.master_pid, '-o', 'rss', '--no-headers']) + process_memory_usage = [int(rss) for rss in worker_processes.splitlines()] + worker_memory_usage = sum(process_memory_usage) + return worker_memory_usage + +def print_config(): + print "graph_title Gunicorn - Memory Usage" + print "graph_args --base 1024 -l 0" + print "graph_vlabel Megabytes" + print "graph_category gunicorn" + print "total_memory.label Total Memory" + +if __name__ == "__main__": + if len(sys.argv) == 2 and sys.argv[1] == 'config': + print_config() + elif len(sys.argv) == 2 and sys.argv[1] == 'autoconf': + try: + open(GUNICORN_PID_PATH).close() + print "yes" + except: + print "no" + # Some docs say it'll be called with fetch, some say no arg at all + elif len(sys.argv) == 1 or (len(sys.argv) == 2 and sys.argv[1] == 'fetch'): + try: + status = GunicornMemoryStatus() + status.print_total_memory() + except: + sys.exit("Couldn't retrieve gunicorn memory usage information") diff --git a/plugins/gunicorn/gunicorn_status b/plugins/gunicorn/gunicorn_status new file mode 100755 index 00000000..2c9fc888 --- /dev/null +++ b/plugins/gunicorn/gunicorn_status @@ -0,0 +1,113 @@ +#!/usr/bin/env python +""" + gunicorn_status - A munin plugin for Linux to monitor gunicorn processes + + Copyright (C) 2012 Azavea, Inc. + Author: Andrew Jennings + + Like Munin, this plugin is licensed under the GNU GPL v2 license + http://www.opensource.org/licenses/GPL-2.0 + + If you've put your gunicorn pid somewhere other than the + default /var/run/gunicorn.pid, you can add a section like + this to your munin-node's plugin configuration: + + [gunicorn_*] + env.gunicorn_pid_path [path to your gunicorn pid] + + This plugin supports the following munin configuration parameters: + #%# family=auto contrib + #%# capabilities=autoconf + +""" + +import sys, os +from subprocess import check_output +from time import sleep + +# set path to your gunicorn pid +try: + GUNICORN_PID_PATH = os.environ['gunicorn_pid_path'] +except: + GUNICORN_PID_PATH = "/var/run/gunicorn.pid" + +class GunicornStatus(): + master_pid = '' + """ + The gunicorn master process pid, as a string + """ + + worker_pids = '' + """ + The list of gunicorn processes as strings + """ + + def __init__(self): + try: + self._get_master_pid() + self._get_worker_pids(self.master_pid) + except: + sys.exit("Couldn't read gunicorn pid") + + def print_total_workers(self): + print ('total_workers.value %d' % self._worker_count()) + + def print_idle_workers(self): + print ('idle_workers.value %d' % self._idle_worker_count()) + + + def _get_master_pid(self): + master_pid_file = open(GUNICORN_PID_PATH) + self.master_pid = master_pid_file.read().rstrip() + master_pid_file.close() + + def _get_worker_pids(self, master_pid): + children = check_output( + ['ps', '--ppid', master_pid, '-o', 'pid', '--no-headers']) + self.worker_pids = [pid.strip() for pid in children.splitlines()] + + def _worker_count(self): + return len(self.worker_pids) + + def _idle_worker_count(self): + idle_workers = 0 + for pid in self.worker_pids: + before = self._cpu_time(pid) + sleep(0.50) + after = self._cpu_time(pid) + if before == after: + idle_workers += 1 + return idle_workers + + def _cpu_time(self, pid): + proc_info = open('/proc/%s/stat' % pid).read() + proc_info = [field.rstrip() for field in proc_info.split()] + user_time = int(proc_info[13].rstrip()) + kernel_time = int(proc_info[14].rstrip()) + return user_time + kernel_time + +def print_config(): + print "graph_title Gunicorn - Status" + print "graph_args -l 0" + print "graph_vlabel Number of workers" + print "graph_category gunicorn" + print "total_workers.label Total Workers" + print "idle_workers.label Idle Workers" + +if __name__ == "__main__": + if len(sys.argv) == 2 and sys.argv[1] == 'config': + print_config() + elif len(sys.argv) == 2 and sys.argv[1] == 'autoconf': + try: + open(GUNICORN_PID_PATH).close() + print "yes" + except: + print "no" + # Some docs say it'll be called with fetch, some say no arg at all + elif len(sys.argv) == 1 or (len(sys.argv) == 2 and sys.argv[1] == 'fetch'): + status = GunicornStatus() + try: + status.print_total_workers() + status.print_idle_workers() + except: + sys.exit("Couldn't retrieve gunicorn status") diff --git a/plugins/other/hadoop-dfs-plugin b/plugins/hadoop/hadoop-dfs-plugin similarity index 100% rename from plugins/other/hadoop-dfs-plugin rename to plugins/hadoop/hadoop-dfs-plugin diff --git a/plugins/other/haproxy-bytes b/plugins/haproxy/haproxy-bytes similarity index 100% rename from plugins/other/haproxy-bytes rename to plugins/haproxy/haproxy-bytes diff --git a/plugins/other/haproxy-connection-errors b/plugins/haproxy/haproxy-connection-errors similarity index 100% rename from plugins/other/haproxy-connection-errors rename to plugins/haproxy/haproxy-connection-errors diff --git a/plugins/other/haproxy-downtime b/plugins/haproxy/haproxy-downtime similarity index 100% rename from plugins/other/haproxy-downtime rename to plugins/haproxy/haproxy-downtime diff --git a/plugins/other/haproxy-errors b/plugins/haproxy/haproxy-errors similarity index 100% rename from plugins/other/haproxy-errors rename to plugins/haproxy/haproxy-errors diff --git a/plugins/other/haproxy-failed-checks b/plugins/haproxy/haproxy-failed-checks similarity index 100% rename from plugins/other/haproxy-failed-checks rename to plugins/haproxy/haproxy-failed-checks diff --git a/plugins/other/haproxy-response-errors b/plugins/haproxy/haproxy-response-errors similarity index 100% rename from plugins/other/haproxy-response-errors rename to plugins/haproxy/haproxy-response-errors diff --git a/plugins/other/haproxy-sessions b/plugins/haproxy/haproxy-sessions similarity index 100% rename from plugins/other/haproxy-sessions rename to plugins/haproxy/haproxy-sessions diff --git a/plugins/other/haproxy-sessions-by-servers b/plugins/haproxy/haproxy-sessions-by-servers similarity index 100% rename from plugins/other/haproxy-sessions-by-servers rename to plugins/haproxy/haproxy-sessions-by-servers diff --git a/plugins/other/apc_status b/plugins/hardware/apc_status similarity index 100% rename from plugins/other/apc_status rename to plugins/hardware/apc_status diff --git a/plugins/other/healthcheck_log b/plugins/healthcheck/healthcheck_log similarity index 100% rename from plugins/other/healthcheck_log rename to plugins/healthcheck/healthcheck_log diff --git a/plugins/other/healthcheck_process b/plugins/healthcheck/healthcheck_process similarity index 100% rename from plugins/other/healthcheck_process rename to plugins/healthcheck/healthcheck_process diff --git a/plugins/other/healthcheck_url b/plugins/healthcheck/healthcheck_url similarity index 100% rename from plugins/other/healthcheck_url rename to plugins/healthcheck/healthcheck_url diff --git a/plugins/other/heimdal_kdc_bandwidth b/plugins/heimdal/heimdal_kdc_bandwidth similarity index 100% rename from plugins/other/heimdal_kdc_bandwidth rename to plugins/heimdal/heimdal_kdc_bandwidth diff --git a/plugins/other/heimdal_kdc_requests b/plugins/heimdal/heimdal_kdc_requests similarity index 100% rename from plugins/other/heimdal_kdc_requests rename to plugins/heimdal/heimdal_kdc_requests diff --git a/plugins/other/hp2600_count_ b/plugins/hp2600/hp2600_count_ similarity index 100% rename from plugins/other/hp2600_count_ rename to plugins/hp2600/hp2600_count_ diff --git a/plugins/other/hp2600_status_ b/plugins/hp2600/hp2600_status_ similarity index 100% rename from plugins/other/hp2600_status_ rename to plugins/hp2600/hp2600_status_ diff --git a/plugins/other/http_load_ b/plugins/http/http_load_ similarity index 100% rename from plugins/other/http_load_ rename to plugins/http/http_load_ diff --git a/plugins/other/http_request_time b/plugins/http/http_request_time similarity index 100% rename from plugins/other/http_request_time rename to plugins/http/http_request_time diff --git a/plugins/other/http_responsetime b/plugins/http/http_responsetime similarity index 100% rename from plugins/other/http_responsetime rename to plugins/http/http_responsetime diff --git a/plugins/other/multi_http_responsetime b/plugins/http/multi_http_responsetime similarity index 100% rename from plugins/other/multi_http_responsetime rename to plugins/http/multi_http_responsetime diff --git a/plugins/other/speedport_300 b/plugins/http/speedport_300 similarity index 100% rename from plugins/other/speedport_300 rename to plugins/http/speedport_300 diff --git a/plugins/other/wget_page b/plugins/http/wget_page similarity index 100% rename from plugins/other/wget_page rename to plugins/http/wget_page diff --git a/plugins/other/icecast2_ b/plugins/icecast/icecast2_ similarity index 100% rename from plugins/other/icecast2_ rename to plugins/icecast/icecast2_ diff --git a/plugins/other/icecast2_all b/plugins/icecast/icecast2_all similarity index 100% rename from plugins/other/icecast2_all rename to plugins/icecast/icecast2_all diff --git a/plugins/other/icecast2_simple b/plugins/icecast/icecast2_simple similarity index 100% rename from plugins/other/icecast2_simple rename to plugins/icecast/icecast2_simple diff --git a/plugins/other/icecast_ b/plugins/icecast/icecast_ similarity index 100% rename from plugins/other/icecast_ rename to plugins/icecast/icecast_ diff --git a/plugins/other/ddclient b/plugins/ip/ddclient similarity index 100% rename from plugins/other/ddclient rename to plugins/ip/ddclient diff --git a/plugins/other/ipmi_ b/plugins/ipmi/ipmi_ similarity index 100% rename from plugins/other/ipmi_ rename to plugins/ipmi/ipmi_ diff --git a/plugins/java/jmx/examples/java/java_cpu.conf b/plugins/java/jmx/examples/java/java_cpu.conf new file mode 100644 index 00000000..1e56cd34 --- /dev/null +++ b/plugins/java/jmx/examples/java/java_cpu.conf @@ -0,0 +1,22 @@ +graph_args --upper-limit 100 -l 0 +graph_scale no +graph_title CPU Usage +graph_vlabel 1000* CPU time % +graph_category Java +graph_order java_cpu_time java_cpu_user_time + +java_cpu_time.label cpu +java_cpu_time.jmxObjectName java.lang:type=Threading +java_cpu_time.jmxAttributeName CurrentThreadCpuTime +java_cpu_time.type DERIVE +java_cpu_time.min 0 +java_cpu_time.graph yes +java_cpu_time.cdef java_cpu_time,3000000,/ + +java_cpu_user_time.label user +java_cpu_user_time.jmxObjectName java.lang:type=Threading +java_cpu_user_time.jmxAttributeName CurrentThreadUserTime +java_cpu_user_time.type DERIVE +java_cpu_user_time.min 0 +java_cpu_user_time.graph yes +java_cpu_user_time.cdef java_cpu_user_time,3000000,/ \ No newline at end of file diff --git a/plugins/java/jmx/examples/java/java_process_memory.conf b/plugins/java/jmx/examples/java/java_process_memory.conf new file mode 100644 index 00000000..ddbdc4d0 --- /dev/null +++ b/plugins/java/jmx/examples/java/java_process_memory.conf @@ -0,0 +1,46 @@ +graph_title Process Memory +graph_vlabel Bytes +graph_category Java +graph_order java_memory_nonheap_committed java_memory_nonheap_max java_memory_nonheap_used java_memory_heap_committed java_memory_heap_max java_memory_heap_used os_memory_physical os_memory_vm + +java_memory_nonheap_committed.label non-heap committed +java_memory_nonheap_committed.jmxObjectName java.lang:type=Memory +java_memory_nonheap_committed.jmxAttributeName NonHeapMemoryUsage +java_memory_nonheap_committed.jmxAttributeKey committed + +java_memory_nonheap_max.label non-heap max +java_memory_nonheap_max.jmxObjectName java.lang:type=Memory +java_memory_nonheap_max.jmxAttributeName NonHeapMemoryUsage +java_memory_nonheap_max.jmxAttributeKey max + +java_memory_nonheap_used.label non-heap used +java_memory_nonheap_used.jmxObjectName java.lang:type=Memory +java_memory_nonheap_used.jmxAttributeName NonHeapMemoryUsage +java_memory_nonheap_used.jmxAttributeKey used + +java_memory_heap_committed.label heap committed +java_memory_heap_committed.jmxObjectName java.lang:type=Memory +java_memory_heap_committed.jmxAttributeName HeapMemoryUsage +java_memory_heap_committed.jmxAttributeKey committed + +java_memory_heap_max.label heap max +java_memory_heap_max.jmxObjectName java.lang:type=Memory +java_memory_heap_max.jmxAttributeName HeapMemoryUsage +java_memory_heap_max.jmxAttributeKey max + +java_memory_heap_used.label heap used +java_memory_heap_used.jmxObjectName java.lang:type=Memory +java_memory_heap_used.jmxAttributeName HeapMemoryUsage +java_memory_heap_used.jmxAttributeKey used + +os_memory_physical.label os free mem +os_memory_physical.jmxObjectName java.lang:type=OperatingSystem +os_memory_physical.jmxAttributeName FreePhysicalMemorySize +os_memory_physical.graph no + +os_memory_vm.label os vmem committed +os_memory_vm.jmxObjectName java.lang:type=OperatingSystem +os_memory_vm.jmxAttributeName CommittedVirtualMemorySize +os_memory_vm.graph no + + diff --git a/plugins/java/jmx/examples/java/java_threads.conf b/plugins/java/jmx/examples/java/java_threads.conf new file mode 100644 index 00000000..f8b64cd4 --- /dev/null +++ b/plugins/java/jmx/examples/java/java_threads.conf @@ -0,0 +1,14 @@ +graph_title Thread Count +graph_vlabel Thread Count +graph_category Java +graph_order java_thread_count java_thread_count_peak + +java_thread_count.label count +java_thread_count.jmxObjectName java.lang:type=Threading +java_thread_count.jmxAttributeName ThreadCount + +java_thread_count_peak.label peak +java_thread_count_peak.jmxObjectName java.lang:type=Threading +java_thread_count_peak.jmxAttributeName PeakThreadCount + + diff --git a/plugins/java/jmx/examples/tomcat/catalina_requests.conf b/plugins/java/jmx/examples/tomcat/catalina_requests.conf new file mode 100644 index 00000000..acb48175 --- /dev/null +++ b/plugins/java/jmx/examples/tomcat/catalina_requests.conf @@ -0,0 +1,16 @@ +graph_title Requests Per Second +graph_vlabel requests per second +graph_category Tomcat +graph_order catalina_request_count catalina_error_count + +catalina_request_count.label requests +catalina_request_count.jmxObjectName Catalina:name=http-8080,type=GlobalRequestProcessor +catalina_request_count.jmxAttributeName requestCount +catalina_request_count.type DERIVE +catalina_request_count.min 0 + +catalina_error_count.label errors +catalina_error_count.jmxObjectName Catalina:name=http-8080,type=GlobalRequestProcessor +catalina_error_count.jmxAttributeName errorCount +catalina_error_count.type DERIVE +catalina_error_count.min 0 diff --git a/plugins/java/jmx/examples/tomcat/catalina_threads.conf b/plugins/java/jmx/examples/tomcat/catalina_threads.conf new file mode 100644 index 00000000..32ab44c2 --- /dev/null +++ b/plugins/java/jmx/examples/tomcat/catalina_threads.conf @@ -0,0 +1,12 @@ +graph_title Thread Count +graph_vlabel Thread Count +graph_category Tomcat +graph_order catalina_threads_count catalina_threads_busy + +catalina_threads_busy.label busy +catalina_threads_busy.jmxObjectName Catalina:name=http-8080,type=ThreadPool +catalina_threads_busy.jmxAttributeName currentThreadsBusy + +catalina_threads_count.label current +catalina_threads_count.jmxObjectName Catalina:name=http-8080,type=ThreadPool +catalina_threads_count.jmxAttributeName currentThreadCount diff --git a/plugins/java/jmx/examples/tomcat/catalina_times.conf b/plugins/java/jmx/examples/tomcat/catalina_times.conf new file mode 100644 index 00000000..e3790259 --- /dev/null +++ b/plugins/java/jmx/examples/tomcat/catalina_times.conf @@ -0,0 +1,32 @@ +graph_title Response Time +graph_vlabel Time, ms +graph_category Rules Engine +graph_args --upper-limit 100 -l 0 +graph_scale no +graph_category Tomcat +graph_order catalina_request_count catalina_max_time catalina_proc_time catalina_proc_tpr + +catalina_request_count.label requests +catalina_request_count.jmxObjectName Catalina:name=http-8080,type=GlobalRequestProcessor +catalina_request_count.jmxAttributeName requestCount +catalina_request_count.graph no +catalina_request_count.type DERIVE +catalina_request_count.min 0 + +catalina_proc_time.label time +catalina_proc_time.jmxObjectName Catalina:name=http-8080,type=GlobalRequestProcessor +catalina_proc_time.jmxAttributeName processingTime +catalina_proc_time.type DERIVE +catalina_proc_time.min 0 +catalina_proc_time.graph no + +catalina_proc_tpr.label avg +catalina_proc_tpr.jmxObjectName Catalina:name=http-8080,type=GlobalRequestProcessor +catalina_proc_tpr.jmxAttributeName processingTime +catalina_proc_tpr.cdef catalina_request_count,0,EQ,0,catalina_proc_time,catalina_request_count,/,IF + +catalina_max_time.label peak +catalina_max_time.jmxObjectName Catalina:name=http-8080,type=GlobalRequestProcessor +catalina_max_time.jmxAttributeName maxTime + + diff --git a/plugins/java/jmx/examples/tomcat/catalina_traffic.conf b/plugins/java/jmx/examples/tomcat/catalina_traffic.conf new file mode 100644 index 00000000..f6d9498d --- /dev/null +++ b/plugins/java/jmx/examples/tomcat/catalina_traffic.conf @@ -0,0 +1,18 @@ +graph_title Traffic +graph_vlabel Bytes rec(-)/sent(+) per second +graph_category Tomcat +graph_order catalina_bytes_received catalina_bytes_sent + +catalina_bytes_sent.label bps +catalina_bytes_sent.jmxObjectName Catalina:name=http-8080,type=GlobalRequestProcessor +catalina_bytes_sent.jmxAttributeName bytesSent +catalina_bytes_sent.type DERIVE +catalina_bytes_sent.min 0 +catalina_bytes_sent.negative catalina_bytes_received + +catalina_bytes_received.label received +catalina_bytes_received.jmxObjectName Catalina:name=http-8080,type=GlobalRequestProcessor +catalina_bytes_received.jmxAttributeName bytesReceived +catalina_bytes_received.type DERIVE +catalina_bytes_received.min 0 +catalina_bytes_received.graph no \ No newline at end of file diff --git a/plugins/java/jmx/plugin/jmx_ b/plugins/java/jmx/plugin/jmx_ new file mode 100755 index 00000000..9fe33f2f --- /dev/null +++ b/plugins/java/jmx/plugin/jmx_ @@ -0,0 +1,54 @@ +#!/bin/sh +# +# Wildcard-plugin to monitor Java JMX (http://java.sun.com/jmx)attributes. +# To monitor a # specific set of JMX attributes, +# link to this file. E.g. +# +# ln -s /usr/share/plugins/jmx_ /etc/munin/plugins/jmx_java_threads +# + +# ...will monitor Java thread count, assuming java_threads.conf file is located in +# /etc/munin/plugins folder. +# +# For Java process to be monitored, it must expose JMX remote interface. +# With Java 1.5 it can be done by adding parameters as: +# +# -Dcom.sun.management.jmxremote.port= -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false +# +# For Tomcat to be monitored, add the following line in catalina.bat script: +# set JAVA_OPTS=%JAVA_OPTS% -Dcom.sun.management.jmxremote.port= -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false +# +# By default, the plugin monitors localhost on = 1616 using URL +# service:jmx:rmi:///jndi/rmi://localhost:1616/jmxrmi +# It can be changed by specifying parameters in /etc/munin/plugin-conf.d/munin-node +# +# [jmx_*] +# env.jmxurl service:jmx:rmi:///jndi/rmi://localhost:1616/jmxrmi +# +# Read more on JMX configuring at http://java.sun.com/j2se/1.5.0/docs/guide/management/agent.html +# $Log$ +# +LINK=`readlink $0` +CONFIGNAME=`basename $0 | sed 's/^jmx_//g'`.conf +RDIR=`dirname $LINK` + +if [ "$jmxurl" = "" ]; then +SERVICE=service:jmx:rmi:///jndi/rmi://localhost:1616/jmxrmi +else +SERVICE="$jmxurl" +fi + +if [ "$jmxuser" != "" ]; then +CREDS="--user=$jmxuser --pass=$jmxpass" +fi + +# checks +test -z $CONFIGNAME || test -z "$RDIR" && exit 1 + +JMXQUERY="java -cp $RDIR/jmxquery.jar org.munin.JMXQuery --url=$SERVICE $CREDS --conf=$RDIR/$CONFIGNAME" + + +case $1 in + (config) $JMXQUERY config;; + (*) $JMXQUERY ;; +esac diff --git a/plugins/java/jmx/plugin/jmxquery.jar b/plugins/java/jmx/plugin/jmxquery.jar new file mode 100644 index 00000000..5c37d4bd Binary files /dev/null and b/plugins/java/jmx/plugin/jmxquery.jar differ diff --git a/plugins/java/jmx/readme.txt b/plugins/java/jmx/readme.txt new file mode 100644 index 00000000..cae58a28 --- /dev/null +++ b/plugins/java/jmx/readme.txt @@ -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 /bin/jconsole.exe and connect to +the host/port you setup in your Java process. + +Some examples are: +* standard Java JMX implementation exposes memory, threads, OS, garbage collector parameters +* Tomcat exposes multiple parameters - requests, processing time, threads, etc.. +* spring framework allows to expose Java beans parameters to JMX +* your application may expose any attributes for JMX by declaration or explicitly. +* can monitor localhost or remote processes + +-------- Installation --------- + +Pre-requsisites are: +- installed munin-node +- Java version 5 JRE + +1) Files from "plugin" folder must be copied to /usr/share/munin/plugins (or another - where your munin plugins located) +2) Make sure that jmx_ executable : chmod a+x /usr/share/munin/plugins/jmx_ +3) Copy configuration files that you want to use, from "examples" folder, into /usr/share/munin/plugins folder +4) create links from the /etc/munin/plugins folder to the /usr/share/munin/plugins/jmx_ +The name of the link must follow wildcard pattern: +jmx_, +where configname is the name of the configuration (config filename without extension), for example: +ln -s /usr/share/munin/plugins/jmx_ /etc/munin/plugins/jmx_process_memory +5) optionally specify the environment variable for JMX URL. The default URL corresponds to localhost:1616. +If you have different port listening by JMX or different hostname to monitor, specify jmxurl parameter +in /etc/munin/plugin-conf.d/munin-node: + +[jmx_*] +env.jmxurl service:jmx:rmi:///jndi/rmi://localhost:1616/jmxrmi + +-------- Check Installation --------- + +To check that all installed properly, try invoke plugins from command line, using links like: + +root@re:/etc/munin/plugins# ./jmx_java_process_memory config +graph_category Java +... +root@re:/etc/munin/plugins# ./jmx_java_process_memory +java_memory_nonheap_committed.value 35291136 +... + +If you have configured environment for jmxurl, do not forget to export it before! + +-------- Configuration Files --------- + +Folder "examples" contains configuration files for Java and Tomcat monitoring examples. +The format of configuration file is a superset of Munin plugin "config" command output +(http://munin.projects.linpro.no/wiki/protocol-config) +It has the following additions: + +.jmxObjectName JMX object name, e.g. java.lang:type=Memory +.jmxAttributeName JMX attribute name, e.g. NonHeapMemoryUsage +.jmxAttributeKey If attribute is a composed data (structure), the name of the field in structure, e.g. max + +% separates comments in file + + + diff --git a/plugins/java/jmx2munin/.gitignore b/plugins/java/jmx2munin/.gitignore new file mode 100644 index 00000000..0a99f329 --- /dev/null +++ b/plugins/java/jmx2munin/.gitignore @@ -0,0 +1,8 @@ +.DS_Store +.classpath +.project +.fatjar +target +eclipse +old +bin diff --git a/plugins/java/jmx2munin/README.md b/plugins/java/jmx2munin/README.md new file mode 100644 index 00000000..d1899db0 --- /dev/null +++ b/plugins/java/jmx2munin/README.md @@ -0,0 +1,79 @@ +# jmx2munin + +The [jmx2munin](http://github.com/tcurdt/jmx2munin) project exposes JMX MBean attributes to [Munin](http://munin-monitoring.org/). +Some of it's features: + + * strictly complies to the plugin format + * exposes composite types like Lists, Maps, Set as useful as possible + * String values can be mapped to numbers + +# How to use + +This is what the Munin script will call. So you should test this first. Of course with your parameters. This example expose all Cassandra information to Munin. + + java -jar jmx2munin.jar \ + -url service:jmx:rmi:///jndi/rmi://localhost:8080/jmxrmi \ + -query "org.apache.cassandra.*:*" + +The "url" parameters specifies the JMX URL, the query selects the MBeans (and optionally also the attributes) to expose. + + java -jar jmx2munin.jar \ + -url service:jmx:rmi:///jndi/rmi://localhost:8080/jmxrmi \ + -query "org.apache.cassandra.*:*" \ + -attribute org_apache_cassandra_db_storageservice_livenodes_size + +The script that does the actual interaction with munin you can find in the contrib section. It's the one you should link in the your Munin plugin directory. + + :/etc/munin/plugins$ ls -la cassandra_* + lrwxrwxrwx 1 root root 37 2011-04-07 19:58 cassandra_nodes_in_cluster -> /usr/share/munin/plugins/jmx2munin.sh + +In the plugin conf you point to the correct configuration + + [cassandra_*] + env.query org.apache.cassandra.*:* + + [cassandra_nodes_in_cluster] + env.config cassandra/nodes_in_cluster + +A possible configuration could look like this + + graph_title Number of Nodes in Cluster + graph_vlabel org_apache_cassandra_db_storageservice_livenodes_size + org_apache_cassandra_db_storageservice_livenodes_size.label number of nodes + +The script will extract the attributes from the config and caches the JMX results to reduce the load when showing many values. + +# More advanced + +Sometimes it can be useful to track String values by mapping them into an enum as they really describe states. To find this possible candidates you can call: + + java -jar jmx2munin.jar \ + -url service:jmx:rmi:///jndi/rmi://localhost:8080/jmxrmi \ + -query "org.apache.cassandra.*:*" \ + list + +It should output a list of possible candidates. This can now be turned into a enum configuration file: + + [org.apache.cassandra.db.StorageService:OperationMode] + 0 = ^Normal + 1 = ^Client + 2 = ^Joining + 3 = ^Bootstrapping + 4 = ^Leaving + 5 = ^Decommissioned + 6 = ^Starting drain + 7 = ^Node is drained + +Which you then can provide: + + java -jar jmx2munin.jar \ + -url service:jmx:rmi:///jndi/rmi://localhost:8080/jmxrmi \ + -query "org.apache.cassandra.*:*" \ + -enums /path/to/enums.cfg + +Now matching values get replaced by their numerical representation. On the left needs to be a unique number on the right side is a regular expression. If a string cannot be matched according to the spec "U" for "undefined" will be returned. + +# License + +Licensed under the Apache License, Version 2.0 (the "License") +You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 diff --git a/plugins/java/jmx2munin/contrib/jmx2munin.cfg/cassandra/nodes_in_cluster b/plugins/java/jmx2munin/contrib/jmx2munin.cfg/cassandra/nodes_in_cluster new file mode 100644 index 00000000..7fe323e3 --- /dev/null +++ b/plugins/java/jmx2munin/contrib/jmx2munin.cfg/cassandra/nodes_in_cluster @@ -0,0 +1,3 @@ +graph_title Number of Nodes in Cluster +graph_vlabel org_apache_cassandra_db_storageservice_livenodes_size +org_apache_cassandra_db_storageservice_livenodes_size.label number of nodes diff --git a/plugins/java/jmx2munin/contrib/jmx2munin.sh b/plugins/java/jmx2munin/contrib/jmx2munin.sh new file mode 100644 index 00000000..2ccb1841 --- /dev/null +++ b/plugins/java/jmx2munin/contrib/jmx2munin.sh @@ -0,0 +1,55 @@ +#!/bin/bash +# [cassandra_nodes_in_cluster] +# env.config cassandra/nodes_in_cluster +# env.query org.apache.cassandra.*:* + +if [ -z "$MUNIN_LIBDIR" ]; then + MUNIN_LIBDIR="`dirname $(dirname "$0")`" +fi + +if [ -f "$MUNIN_LIBDIR/plugins/plugin.sh" ]; then + . $MUNIN_LIBDIR/plugins/plugin.sh +fi + +if [ "$1" = "autoconf" ]; then + echo yes + exit 0 +fi + +if [ -z "$url" ]; then + # this is very common so make it a default + url="service:jmx:rmi:///jndi/rmi://127.0.0.1:8080/jmxrmi" +fi + +if [ -z "$config" -o -z "$query" -o -z "$url" ]; then + echo "Configuration needs attributes config, query and optinally url" + exit 1 +fi + +JMX2MUNIN_DIR="$MUNIN_LIBDIR/plugins" +CONFIG="$JMX2MUNIN_DIR/jmx2munin.cfg/$config" + +if [ "$1" = "config" ]; then + cat "$CONFIG" + exit 0 +fi + +JAR="$JMX2MUNIN_DIR/jmx2munin.jar" +CACHED="/tmp/jmx2munin" + +if test ! -f $CACHED || test `find "$CACHED" -mmin +2`; then + + java -jar "$JAR" \ + -url "$url" \ + -query "$query" \ + $ATTRIBUTES \ + > $CACHED + + echo "cached.value `date +%s`" >> $CACHED +fi + +ATTRIBUTES=`awk '/\.label/ { gsub(/\.label/,""); print $1 }' $CONFIG` + +for ATTRIBUTE in $ATTRIBUTES; do + grep $ATTRIBUTE $CACHED +done \ No newline at end of file diff --git a/plugins/java/jmx2munin/pom.xml b/plugins/java/jmx2munin/pom.xml new file mode 100644 index 00000000..2bbbd026 --- /dev/null +++ b/plugins/java/jmx2munin/pom.xml @@ -0,0 +1,121 @@ + + + 4.0.0 + + org.vafer + jmx2munin + jmx2munin + 1.0 + + Munin plugin to access JMX information + + http://github.com/tcurdt/jmx2munin + + + + tcurdt + Torsten Curdt + tcurdt at vafer.org + +1 + + + + + + Apache License 2 + http://www.apache.org/licenses/LICENSE-2.0.txt + + + + + scm:git:git://github.com:tcurdt/jmx2munin.git + scm:git:git://github.com:tcurdt/jmx2munin.git + http://github.com/tcurdt/jmx2munin/tree/master + + + + + com.beust + jcommander + 1.17 + + + + junit + junit + 4.5 + test + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.5 + 1.5 + UTF-8 + + + + org.apache.maven.plugins + maven-surefire-plugin + + never + + **/*TestCase.java + + + **/Abstract* + + true + false + + + + org.apache.maven.plugins + maven-source-plugin + 2.1 + + true + + + + create-source-jar + + jar-no-fork + + + + + + org.apache.maven.plugins + maven-shade-plugin + 1.4 + + + package + + shade + + + false + + + com.beust:jcommander + + + + + org.vafer.jmx.munin.Munin + + + + + + + + + diff --git a/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/Enums.java b/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/Enums.java new file mode 100644 index 00000000..ab5b4831 --- /dev/null +++ b/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/Enums.java @@ -0,0 +1,77 @@ +package org.vafer.jmx; + +import java.io.BufferedReader; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.TreeMap; +import java.util.regex.Pattern; + +import javax.management.ObjectName; + +public final class Enums { + + private TreeMap> sections = new TreeMap>(); + + public boolean load(String filePath) throws IOException { + BufferedReader input = null; + LinkedHashMap section = new LinkedHashMap(); + try { + input = new BufferedReader(new InputStreamReader(new FileInputStream(filePath))); + String line; + int linenr = 0; + while((line = input.readLine()) != null) { + linenr += 1; + line = line.trim(); + if (line.startsWith("#")) { + continue; + } + if (line.startsWith("[") && line.endsWith("]")) { + // new section + String id = line.substring(1, line.length() - 1); + section = new LinkedHashMap(); + sections.put(id, section); + } else { + String[] pair = line.split("="); + if (pair.length == 2) { + Integer number = Integer.parseInt(pair[0].trim()); + Pattern pattern = Pattern.compile(pair[1].trim()); + if (section.put(number, pattern) != null) { + System.err.println("Line " + linenr + ": previous definitions of " + number); + } + } + } + } + } finally { + if (input != null) { + input.close(); + } + } + return false; + } + + public static String id(ObjectName beanName, String attributeName) { + StringBuilder sb = new StringBuilder(); + sb.append(beanName.getDomain()); + sb.append('.'); + sb.append(beanName.getKeyProperty("type")); + sb.append(':'); + sb.append(attributeName); + return sb.toString(); + } + + public Number resolve(String id, String value) { + LinkedHashMap section = sections.get(id); + if (section == null) { + return null; + } + for(Map.Entry entry : section.entrySet()) { + if (entry.getValue().matcher(value).matches()) { + return entry.getKey(); + } + } + return null; + } +} diff --git a/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/Filter.java b/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/Filter.java new file mode 100644 index 00000000..e7d67a8a --- /dev/null +++ b/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/Filter.java @@ -0,0 +1,9 @@ +package org.vafer.jmx; + +import javax.management.ObjectName; + +public interface Filter { + + public boolean include(ObjectName bean, String attribute); + +} diff --git a/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/ListOutput.java b/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/ListOutput.java new file mode 100644 index 00000000..4e050faf --- /dev/null +++ b/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/ListOutput.java @@ -0,0 +1,26 @@ +package org.vafer.jmx; + +import java.util.HashSet; +import java.util.Set; + +import javax.management.ObjectName; + +public final class ListOutput implements Output { + + private final Set seen = new HashSet(); + + public void output(ObjectName beanName, String attributeName, Object value) { + Value.flatten(beanName, attributeName, value, new Value.Listener() { + public void value(ObjectName beanName, String attributeName, String value) { + final String id = Enums.id(beanName, attributeName); + if (!seen.contains(id)) { + System.out.println("[" + id + "]"); + seen.add(id); + } + } + public void value(ObjectName beanName, String attributeName, Number value) { + } + }); + } + +} diff --git a/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/NoFilter.java b/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/NoFilter.java new file mode 100644 index 00000000..6188d5c7 --- /dev/null +++ b/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/NoFilter.java @@ -0,0 +1,10 @@ +package org.vafer.jmx; + +import javax.management.ObjectName; + +public final class NoFilter implements Filter { + + public boolean include(ObjectName bean, String attribute) { + return true; + } +} diff --git a/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/Output.java b/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/Output.java new file mode 100644 index 00000000..eb9e6ca2 --- /dev/null +++ b/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/Output.java @@ -0,0 +1,9 @@ +package org.vafer.jmx; + +import javax.management.ObjectName; + +public interface Output { + + public void output(ObjectName beanName, String attributeName, Object value); + +} diff --git a/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/Query.java b/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/Query.java new file mode 100644 index 00000000..e27bc4f5 --- /dev/null +++ b/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/Query.java @@ -0,0 +1,52 @@ +package org.vafer.jmx; + +import java.io.IOException; +import java.util.Collection; + +import javax.management.AttributeNotFoundException; +import javax.management.InstanceNotFoundException; +import javax.management.IntrospectionException; +import javax.management.MBeanAttributeInfo; +import javax.management.MBeanException; +import javax.management.MBeanInfo; +import javax.management.MBeanServerConnection; +import javax.management.MalformedObjectNameException; +import javax.management.ObjectInstance; +import javax.management.ObjectName; +import javax.management.ReflectionException; +import javax.management.remote.JMXConnector; +import javax.management.remote.JMXConnectorFactory; +import javax.management.remote.JMXServiceURL; + +public final class Query { + + public void run(String url, String expression, Filter filter, Output output) throws IOException, MalformedObjectNameException, InstanceNotFoundException, ReflectionException, IntrospectionException, AttributeNotFoundException, MBeanException { + JMXConnector connector = JMXConnectorFactory.connect(new JMXServiceURL(url)); + MBeanServerConnection connection = connector.getMBeanServerConnection(); + final Collection mbeans = connection.queryMBeans(new ObjectName(expression), null); + + for(ObjectInstance mbean : mbeans) { + final ObjectName mbeanName = mbean.getObjectName(); + final MBeanInfo mbeanInfo = connection.getMBeanInfo(mbeanName); + final MBeanAttributeInfo[] attributes = mbeanInfo.getAttributes(); + for (final MBeanAttributeInfo attribute : attributes) { + if (attribute.isReadable()) { + if (filter.include(mbeanName, attribute.getName())) { + final String attributeName = attribute.getName(); + try { + output.output( + mbean.getObjectName(), + attributeName, + connection.getAttribute(mbeanName, attributeName) + ); + } catch(Exception e) { + // System.err.println("Failed to read " + mbeanName + "." + attributeName); + } + } + } + } + + } + connector.close(); + } +} diff --git a/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/Value.java b/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/Value.java new file mode 100644 index 00000000..87af5f8a --- /dev/null +++ b/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/Value.java @@ -0,0 +1,52 @@ +package org.vafer.jmx; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.management.ObjectName; + +public final class Value { + + public interface Listener { + public void value(ObjectName beanName, String attributeName, String value); + public void value(ObjectName beanName, String attributeName, Number value); + } + + public static void flatten(ObjectName beanName, String attributeName, Object value, Listener listener) { + if (value instanceof Number) { + + listener.value(beanName, attributeName, (Number) value); + + } else if (value instanceof String) { + + listener.value(beanName, attributeName, (String) value); + + } else if (value instanceof Set) { + + final Set set = (Set) value; + flatten(beanName, attributeName + ".size", set.size(), listener); + for(Object entry : set) { + flatten(beanName, attributeName + "[" + entry + "]", 1, listener); + } + + } else if (value instanceof List) { + + final List list = (List)value; + listener.value(beanName, attributeName + ".size", list.size()); + for(int i = 0; i map = (Map) value; + listener.value(beanName, attributeName + ".size", map.size()); + for(Map.Entry entry : map.entrySet()) { + flatten(beanName, attributeName + "[" + entry.getKey() + "]", entry.getValue(), listener); + } + + } else { + // System.err.println("Failed to convert " + beanName + "." + attributeName); + } + } +} diff --git a/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/munin/Munin.java b/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/munin/Munin.java new file mode 100644 index 00000000..9f1ffdc7 --- /dev/null +++ b/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/munin/Munin.java @@ -0,0 +1,67 @@ +package org.vafer.jmx.munin; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +import org.vafer.jmx.Enums; +import org.vafer.jmx.Filter; +import org.vafer.jmx.ListOutput; +import org.vafer.jmx.NoFilter; +import org.vafer.jmx.Query; + +import com.beust.jcommander.JCommander; +import com.beust.jcommander.Parameter; + +public final class Munin { + + @Parameter(description = "") + private List args = new ArrayList(); + + @Parameter(names = "-url", description = "jmx url", required = true) + private String url; + + @Parameter(names = "-query", description = "query expression", required = true) + private String query; + + @Parameter(names = "-enums", description = "file string to enum config") + private String enumsPath; + + @Parameter(names = "-attribute", description = "attributes to return") + private List attributes = new ArrayList(); + + private void run() throws Exception { + final Filter filter; + if (attributes == null || attributes.isEmpty()) { + filter = new NoFilter(); + } else { + filter = new MuninAttributesFilter(attributes); + } + + final Enums enums = new Enums(); + if (enumsPath != null) { + enums.load(enumsPath); + } + + final String cmd = args.toString().toLowerCase(Locale.US); + if ("[list]".equals(cmd)) { + new Query().run(url, query, filter, new ListOutput()); + } else { + new Query().run(url, query, filter, new MuninOutput(enums)); + } + } + + public static void main(String[] args) throws Exception { + Munin m = new Munin(); + + JCommander cli = new JCommander(m); + try { + cli.parse(args); + } catch(Exception e) { + cli.usage(); + System.exit(1); + } + + m.run(); + } +} diff --git a/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/munin/MuninAttributesFilter.java b/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/munin/MuninAttributesFilter.java new file mode 100644 index 00000000..e1a49e83 --- /dev/null +++ b/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/munin/MuninAttributesFilter.java @@ -0,0 +1,24 @@ +package org.vafer.jmx.munin; + +import java.util.HashSet; +import java.util.List; + +import javax.management.ObjectName; + +import org.vafer.jmx.Filter; + +public final class MuninAttributesFilter implements Filter { + + private final HashSet attributes = new HashSet(); + + public MuninAttributesFilter(List pAttributes) { + for (String attribute : pAttributes) { + attributes.add(attribute.trim().replaceAll("_size$", "")); + } + } + + public boolean include(ObjectName bean, String attribute) { + return attributes.contains(MuninOutput.attributeName(bean, attribute)); + } + +} diff --git a/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/munin/MuninOutput.java b/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/munin/MuninOutput.java new file mode 100644 index 00000000..9fb50b12 --- /dev/null +++ b/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/munin/MuninOutput.java @@ -0,0 +1,93 @@ +package org.vafer.jmx.munin; + +import java.text.NumberFormat; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Hashtable; +import java.util.Locale; + +import javax.management.ObjectName; + +import org.vafer.jmx.Enums; +import org.vafer.jmx.Output; +import org.vafer.jmx.Value; + +public final class MuninOutput implements Output { + + private final Enums enums; + + public MuninOutput(Enums enums) { + this.enums = enums; + } + + public static String attributeName(ObjectName bean, String attribute) { + StringBuilder sb = new StringBuilder(); + sb.append(fieldname(beanString(bean))); + sb.append('_'); + sb.append(fieldname(attribute)); + return sb.toString().toLowerCase(Locale.US); + } + + private static String fieldname(String s) { + return s.replaceAll("[^A-Za-z0-9]", "_"); + } + + private static String beanString(ObjectName beanName) { + StringBuilder sb = new StringBuilder(); + sb.append(beanName.getDomain()); + + Hashtable properties = beanName.getKeyPropertyList(); + + String keyspace = "keyspace"; + if (properties.containsKey(keyspace)) { + sb.append('.'); + sb.append(properties.get(keyspace)); + properties.remove(keyspace); + } + + String type = "type"; + if (properties.containsKey(type)) { + sb.append('.'); + sb.append(properties.get(type)); + properties.remove(type); + } + + ArrayList keys = new ArrayList(properties.keySet()); + Collections.sort(keys); + + for(String key : keys) { + sb.append('.'); + sb.append(properties.get(key)); + } + + return sb.toString(); + // return beanName.getCanonicalName(); + } + + public void output(ObjectName beanName, String attributeName, Object value) { + Value.flatten(beanName, attributeName, value, new Value.Listener() { + public void value(ObjectName beanName, String attributeName, String value) { + final Number v = enums.resolve(Enums.id(beanName, attributeName), value); + if (v != null) { + value(beanName, attributeName, v); + } else { + value(beanName, attributeName, Double.NaN); + } + } + public void value(ObjectName beanName, String attributeName, Number value) { + final String v; + + if (Double.isNaN(value.doubleValue())) { + v = "U"; + } else { + final NumberFormat f = NumberFormat.getInstance(); + f.setMaximumFractionDigits(2); + f.setGroupingUsed(false); + v = f.format(value); + } + + System.out.println(attributeName(beanName, attributeName) + ".value " + v); + } + }); + } +} \ No newline at end of file diff --git a/plugins/other/jstat__gccount b/plugins/java/jstat__gccount similarity index 100% rename from plugins/other/jstat__gccount rename to plugins/java/jstat__gccount diff --git a/plugins/other/jstat__gctime b/plugins/java/jstat__gctime similarity index 100% rename from plugins/other/jstat__gctime rename to plugins/java/jstat__gctime diff --git a/plugins/other/jstat__heap b/plugins/java/jstat__heap similarity index 100% rename from plugins/other/jstat__heap rename to plugins/java/jstat__heap diff --git a/plugins/other/jvm_sun_memory b/plugins/java/jvm_sun_memory similarity index 100% rename from plugins/other/jvm_sun_memory rename to plugins/java/jvm_sun_memory diff --git a/plugins/other/jvm_sun_minorgcs b/plugins/java/jvm_sun_minorgcs similarity index 100% rename from plugins/other/jvm_sun_minorgcs rename to plugins/java/jvm_sun_minorgcs diff --git a/plugins/other/jvm_sun_tenuredgcs b/plugins/java/jvm_sun_tenuredgcs similarity index 100% rename from plugins/other/jvm_sun_tenuredgcs rename to plugins/java/jvm_sun_tenuredgcs diff --git a/plugins/other/joomla-sessions b/plugins/joomla/joomla-sessions similarity index 100% rename from plugins/other/joomla-sessions rename to plugins/joomla/joomla-sessions diff --git a/plugins/other/kvm_cpu b/plugins/kvm/kvm_cpu similarity index 100% rename from plugins/other/kvm_cpu rename to plugins/kvm/kvm_cpu diff --git a/plugins/other/kvm_io b/plugins/kvm/kvm_io similarity index 100% rename from plugins/other/kvm_io rename to plugins/kvm/kvm_io diff --git a/plugins/other/kvm_mem b/plugins/kvm/kvm_mem similarity index 100% rename from plugins/other/kvm_mem rename to plugins/kvm/kvm_mem diff --git a/plugins/other/kvm_net b/plugins/kvm/kvm_net similarity index 100% rename from plugins/other/kvm_net rename to plugins/kvm/kvm_net diff --git a/plugins/other/munin-libvirtpy b/plugins/kvm/munin-libvirtpy similarity index 100% rename from plugins/other/munin-libvirtpy rename to plugins/kvm/munin-libvirtpy diff --git a/plugins/other/flexlm_ b/plugins/licensing/flexlm_ similarity index 100% rename from plugins/other/flexlm_ rename to plugins/licensing/flexlm_ diff --git a/plugins/other/logins b/plugins/logins/logins similarity index 100% rename from plugins/other/logins rename to plugins/logins/logins diff --git a/plugins/other/lustre_abs b/plugins/lustre/lustre_abs similarity index 100% rename from plugins/other/lustre_abs rename to plugins/lustre/lustre_abs diff --git a/plugins/other/lustre_df b/plugins/lustre/lustre_df similarity index 100% rename from plugins/other/lustre_df rename to plugins/lustre/lustre_df diff --git a/plugins/other/lustre_df_free b/plugins/lustre/lustre_df_free similarity index 100% rename from plugins/other/lustre_df_free rename to plugins/lustre/lustre_df_free diff --git a/plugins/other/lustre_df_indodes b/plugins/lustre/lustre_df_indodes similarity index 100% rename from plugins/other/lustre_df_indodes rename to plugins/lustre/lustre_df_indodes diff --git a/plugins/other/amavis_ b/plugins/mail/amavis_ similarity index 100% rename from plugins/other/amavis_ rename to plugins/mail/amavis_ diff --git a/plugins/other/amavis_awk b/plugins/mail/amavis_awk similarity index 100% rename from plugins/other/amavis_awk rename to plugins/mail/amavis_awk diff --git a/plugins/other/clamav b/plugins/mail/clamav similarity index 100% rename from plugins/other/clamav rename to plugins/mail/clamav diff --git a/plugins/other/courier_log b/plugins/mail/courier_log similarity index 100% rename from plugins/other/courier_log rename to plugins/mail/courier_log diff --git a/plugins/other/dkimproxy_mails b/plugins/mail/dkimproxy_mails similarity index 100% rename from plugins/other/dkimproxy_mails rename to plugins/mail/dkimproxy_mails diff --git a/plugins/other/dovecot b/plugins/mail/dovecot similarity index 82% rename from plugins/other/dovecot rename to plugins/mail/dovecot index 1ce5108c..03119c26 100755 --- a/plugins/other/dovecot +++ b/plugins/mail/dovecot @@ -1,4 +1,4 @@ -#!/bin/sh +#! /bin/bash # # Munin Plugin # to count logins to your dovecot mailserver @@ -28,7 +28,7 @@ ###################### # Configuration ###################### -STAT_FILE=/var/lib/munin/plugin-state/plugin-dovecot.state +STAT_FILE=${STAT_FILE:-/var/lib/munin/plugin-state/plugin-dovecot.state} EXPR_BIN=/usr/bin/expr LOGFILE=${logfile:-/var/log/mail.log} ###################### @@ -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 diff --git a/plugins/mail/imap_bandwidth b/plugins/mail/imap_bandwidth new file mode 100755 index 00000000..fc5cd894 --- /dev/null +++ b/plugins/mail/imap_bandwidth @@ -0,0 +1,220 @@ +#!/bin/sh +# +# Revision 1.1 2012/02/26 03:43:27 +# Improved labels +# +# Revision 1.0 2012/02/25 21:31:16 +# Initial release +# + +: <<=cut +=head1 NAME + +imap_bandwidth - Munin plugin to measure the current bandwidth of one or more remote IMAP servers. + +=head1 APPLICABLE SYSTEMS + +Any Linux system with the package "isync" (or "mbsync") installed. + +=head1 CONFIGURATION + +This configuration section shows a usable example for two imap servers: + [imap_bandwidth] + env.imap_servers internal=imap-internal.example.org external=imap.example.org + env.cert_file /etc/munin-imap-cert.pem + env.username foo_user + env.password secret + env.transfer_volume_kbyte 100 + env.use_ssl yes + + +Both "use_ssl" and "transfer_volume_kbyte" are optional and default to the above +values. +All other parameters are required. + +Generate the certificate file by running "mbsync-get-cert imap.example.org". + +"imap_servers" is a space-separated list of key=value combinations. "key" is +used as a label in munin graphs. "value" is the full hostname or IP of the IMAP +server. All IMAP servers need to share the same authentication database (i.e. +accept the same username/password). + +Reduce the "transfer_volume_kbyte" parameter if you need to minimize traffic. + +Maybe you need to specify the "timeout" setting for this plugin if it takes +longer than munin's default timeout. + + +=head1 INTERPRETATION + +The plugin simply shows the average bandwidth during one IMAP upload and one +IMAP download of a file containg random bytes. + +You need to be aware that this measurement obviously increases the load on +your IMAP servers for the duration of the measurement. + +You also need to be aware of the safety implications imposed by storing +sensitive information (username and password combinations) on your monitoring +server in plaintext. + +=head1 VERSION + +Version 1.0 + +=head1 BUGS + +None known + +Set the environment variable DEBUG=1 if you need to investigate problems. + +=head1 AUTHOR + +Lars Kruse + +=head1 LICENSE + +GPLv3 or higher + +=cut + + +set -eu + +#%# family=auto +#%# capabilities=autoconf + +TRANSFER_SIZE=${transfer_volume_kbyte:-100} +USE_SSL=${use_ssl:-yes} +IMAP_USERNAME=${username} +IMAP_PASSWORD=${password} +CERT_FILE=${cert_file} + +# example value: +# internal=imap-internal.example.org external=imap.example.org +SERVERS="$imap_servers" + + +if test -n "${DEBUG:-}"; then + TRANSFER_SIZE=20 + set -x +fi + + +. $MUNIN_LIBDIR/plugins/plugin.sh + + +if [ "$1" = "autoconf" ]; then + if ( which mbsync >/dev/null 2>&1 ); then + echo yes + exit 0 + else + echo "no (could not run \"mbsync\")" + exit 0 + fi +fi + +if [ "$1" = "config" ]; then + echo 'graph_title IMAP bandwidth' + echo 'graph_vlabel to (+) / from (-) server [bit/s]' + echo 'graph_category network' + for item in $SERVERS; do + key="$(echo "$item" | cut -f 1 -d =)" + clean_name="$(clean_fieldname "$key")" + echo "download_${clean_name}.graph no" + echo "download_${clean_name}.label download" + echo "upload_${clean_name}.label $key" + echo "upload_${clean_name}.negative download_${clean_name}" + done + exit 0 +fi + + +create_dummy_file() { + dd if=/dev/urandom "of=$DUMMY_FILENAME" bs=1K count=${TRANSFER_SIZE} 2>/dev/null +} + +is_dummy_file_missing() { + test ! -e "$DUMMY_FILENAME" +} + +remove_mail_files() { + get_mail_files | while read fname; do rm "$fname"; done +} + +get_mail_files() { + find "$MAILDIR" -type f | grep -v "/\." +} + +# run the synchronization +run_sync() { + if test -n "${DEBUG:-}"; then + echo yes | mbsync --config "$SYNCRC" sync || true + else + echo yes | mbsync --config "$SYNCRC" sync >/dev/null 2>/dev/null || true + fi +} + +# run the synchronization and determine the duration of this operation +speed_sync() { + start=$(date +%s%N) + run_sync + end=$(date +%s%N) + # did we wrap a minute? + test "$end" -lt "$start" && end=$((end + 60 * 1000000000)) + delay=$((end - start)) + # use "bit" multiplier + echo "$((8 * TRANSFER_SIZE * 1024 * 1000000000 / delay))" +} + +for item in $SERVERS; do + key="$(echo "$item" | cut -f 1 -d =)" + host="$(echo "$item" | cut -f 2- -d =)" + clean_name="$(clean_fieldname "$key")" + MAILDIR="$(mktemp -d)" + # this file needs to include a dot at the beginning - otherwise it gets cleaned up ... + SYNCRC="$MAILDIR/.mbsyncrc" + SYNC_STATE_FILE_PREFIX="$MAILDIR/.syncstate-" + + cat - >"$SYNCRC" <<- EOF + SyncState $SYNC_STATE_FILE_PREFIX + Expunge Both + + MaildirStore local + Path $MAILDIR + Inbox $MAILDIR + + IMAPStore remote + Host $host + UseIMAPS $USE_SSL + User $IMAP_USERNAME + Pass $IMAP_PASSWORD + CertificateFile $CERT_FILE + + Channel sync + Master :local: + Slave :remote: +EOF + + mkdir "$MAILDIR/new" "$MAILDIR/tmp" "$MAILDIR/cur" + DUMMY_FILENAME="$MAILDIR/new/$(date +%N)" + # download all existing files + run_sync + # remove all local files -> to be purged remotely later + remove_mail_files + # create dummy file for upload + create_dummy_file "$MAILDIR" + output="upload_${clean_name}.value $(speed_sync)" + is_dummy_file_missing && echo "$output" || echo >&2 "upload failed" + # remove local file + remove_mail_files "$MAILDIR" + # persuade mbsync that we have never seen the dummy file ... + rm "$SYNC_STATE_FILE_PREFIX"* + output="download_${clean_name}.value $(speed_sync)" + get_mail_files | grep -q . && echo "$output" || echo >&2 "download failed" + # remove the new file from the imap server + remove_mail_files + run_sync + # clean up + rm -r "$MAILDIR" + done + diff --git a/plugins/other/mail_connections b/plugins/mail/mail_connections similarity index 100% rename from plugins/other/mail_connections rename to plugins/mail/mail_connections diff --git a/plugins/other/mixminion b/plugins/mail/mixminion similarity index 100% rename from plugins/other/mixminion rename to plugins/mail/mixminion diff --git a/plugins/other/postfix-policyd b/plugins/mail/postfix-policyd similarity index 100% rename from plugins/other/postfix-policyd rename to plugins/mail/postfix-policyd diff --git a/plugins/other/postfix-rbl-blocked-mails b/plugins/mail/postfix-rbl-blocked-mails similarity index 100% rename from plugins/other/postfix-rbl-blocked-mails rename to plugins/mail/postfix-rbl-blocked-mails diff --git a/plugins/other/postfix_filtered b/plugins/mail/postfix_filtered similarity index 100% rename from plugins/other/postfix_filtered rename to plugins/mail/postfix_filtered diff --git a/plugins/other/postfix_filtered_awk b/plugins/mail/postfix_filtered_awk similarity index 100% rename from plugins/other/postfix_filtered_awk rename to plugins/mail/postfix_filtered_awk diff --git a/plugins/other/postfix_mailfiltered b/plugins/mail/postfix_mailfiltered similarity index 100% rename from plugins/other/postfix_mailfiltered rename to plugins/mail/postfix_mailfiltered diff --git a/plugins/other/postfix_stats b/plugins/mail/postfix_stats similarity index 100% rename from plugins/other/postfix_stats rename to plugins/mail/postfix_stats diff --git a/plugins/other/postgrey b/plugins/mail/postgrey similarity index 100% rename from plugins/other/postgrey rename to plugins/mail/postgrey diff --git a/plugins/other/postgrey-new b/plugins/mail/postgrey-new similarity index 100% rename from plugins/other/postgrey-new rename to plugins/mail/postgrey-new diff --git a/plugins/other/procmail_ b/plugins/mail/procmail_ similarity index 100% rename from plugins/other/procmail_ rename to plugins/mail/procmail_ diff --git a/plugins/other/qmailconn b/plugins/mail/qmailconn similarity index 100% rename from plugins/other/qmailconn rename to plugins/mail/qmailconn diff --git a/plugins/other/qmailsend b/plugins/mail/qmailsend similarity index 100% rename from plugins/other/qmailsend rename to plugins/mail/qmailsend diff --git a/plugins/other/qmailsend_plesk b/plugins/mail/qmailsend_plesk similarity index 100% rename from plugins/other/qmailsend_plesk rename to plugins/mail/qmailsend_plesk diff --git a/plugins/other/qremote b/plugins/mail/qremote similarity index 100% rename from plugins/other/qremote rename to plugins/mail/qremote diff --git a/plugins/other/queuestats b/plugins/mail/queuestats similarity index 100% rename from plugins/other/queuestats rename to plugins/mail/queuestats diff --git a/plugins/other/sa-learn b/plugins/mail/sa-learn similarity index 100% rename from plugins/other/sa-learn rename to plugins/mail/sa-learn diff --git a/plugins/other/spamd-blacklist-bsd b/plugins/mail/spamd-blacklist-bsd similarity index 100% rename from plugins/other/spamd-blacklist-bsd rename to plugins/mail/spamd-blacklist-bsd diff --git a/plugins/other/spamd-tarpit-bsd b/plugins/mail/spamd-tarpit-bsd similarity index 100% rename from plugins/other/spamd-tarpit-bsd rename to plugins/mail/spamd-tarpit-bsd diff --git a/plugins/other/spamdyke b/plugins/mail/spamdyke similarity index 100% rename from plugins/other/spamdyke rename to plugins/mail/spamdyke diff --git a/plugins/other/eoc_subscribers_count b/plugins/mailinglist/eoc_subscribers_count similarity index 100% rename from plugins/other/eoc_subscribers_count rename to plugins/mailinglist/eoc_subscribers_count diff --git a/plugins/other/mailman_subscribers b/plugins/mailman/mailman_subscribers similarity index 100% rename from plugins/other/mailman_subscribers rename to plugins/mailman/mailman_subscribers diff --git a/plugins/other/memcached-multigraph-plugin b/plugins/memcached/memcached-multigraph-plugin similarity index 100% rename from plugins/other/memcached-multigraph-plugin rename to plugins/memcached/memcached-multigraph-plugin diff --git a/plugins/other/memcached_bytes_ b/plugins/memcached/memcached_bytes_ similarity index 100% rename from plugins/other/memcached_bytes_ rename to plugins/memcached/memcached_bytes_ diff --git a/plugins/other/memcached_bytes_all b/plugins/memcached/memcached_bytes_all similarity index 100% rename from plugins/other/memcached_bytes_all rename to plugins/memcached/memcached_bytes_all diff --git a/plugins/other/memcached_connections_ b/plugins/memcached/memcached_connections_ similarity index 100% rename from plugins/other/memcached_connections_ rename to plugins/memcached/memcached_connections_ diff --git a/plugins/other/memcached_hits_ b/plugins/memcached/memcached_hits_ similarity index 90% rename from plugins/other/memcached_hits_ rename to plugins/memcached/memcached_hits_ index 1c2982f6..d6697728 100755 --- a/plugins/other/memcached_hits_ +++ b/plugins/memcached/memcached_hits_ @@ -6,9 +6,16 @@ use warnings; use Cache::Memcached; +$0 =~ /memcached_hits_(\d+_\d+_\d+_\d+)_(\d+)$/; +my ($ip, $port) = ($1, $2); +$ip =~ s/_/./g; +my $address = "$ip:$port"; + +my $title = $ENV{title} || $address; + my $cmd = shift || ''; if ($cmd eq 'config') { - print "graph_title Memcached cache hits and misses\n"; + print "graph_title Memcached cache hits and misses -- $title\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel requests\n"; print "graph_category memcached\n"; @@ -25,11 +32,6 @@ if ($cmd eq 'config') { exit 0; } -$0 =~ /memcached_hits_(\d+_\d+_\d+_\d+)_(\d+)$/; -my ($ip, $port) = ($1, $2); -$ip =~ s/_/./g; -my $address = "$ip:$port"; - my $memd = new Cache::Memcached { 'servers' => [$address] }; my $memstats = $memd->stats(['misc']); diff --git a/plugins/other/memcached_items_ b/plugins/memcached/memcached_items_ similarity index 100% rename from plugins/other/memcached_items_ rename to plugins/memcached/memcached_items_ diff --git a/plugins/other/memcached-multigraph b/plugins/memcached/memcached_multi_ similarity index 67% rename from plugins/other/memcached-multigraph rename to plugins/memcached/memcached_multi_ index eac6a90c..3df9f8bb 100755 --- a/plugins/other/memcached-multigraph +++ b/plugins/memcached/memcached_multi_ @@ -1,6 +1,6 @@ #!/usr/bin/perl # -=head1 NAME +=head1 MEMCACHED Memcached - A Plugin to monitor Memcached Servers (Multigraph) @@ -36,9 +36,6 @@ items => I This graphs the current items and total items in the memc memory => I This graphs the current and max memory allocation B -The following example holds true for all graphing options in this plugin. - Example: ln -s /usr/share/munin/plugins/memcached_multi_ /etc/munin/plugins/memcached_multi_bytes - =head1 ADDITIONAL INFORMATION You will find that some of the graphs have LEI on them. This was done in order to save room @@ -52,13 +49,11 @@ The B variable formats certain graphs based on the following guidelin =head1 ACKNOWLEDGEMENTS -The core of this plugin is based on the mysql_ plugin maintained by Kjell-Magne Ãierud - -Thanks to dormando as well for putting up with me ;) +Thanks to dormando for putting up with me ;) =head1 AUTHOR -Matt West < https://code.google.com/p/memcached-munin-plugin/ > +Matt West < https://github.com/mhwest13/Memcached-Munin-Plugin > =head1 LICENSE @@ -72,54 +67,77 @@ GPLv2 =cut use strict; +use warnings; use IO::Socket; use Munin::Plugin; +use File::Basename; +if (basename($0) !~ /^memcached_multi_/) { + print "This script needs to be named memcached_multi_ and have symlinks which start the same.\n"; + exit 1; +} + +# tell munin about our multigraph capabilities need_multigraph(); +=head1 Variable Declarations + + This section of code is to declare the variables used throughout the plugin + Some of them are imported as environment variables from munin plugin conf.d + file, others are hashes used for storing information that comes from the + stats commands issued to memcached. + +=cut + +# lets import environment variables for the plugin or use the default my $host = $ENV{host} || "127.0.0.1"; my $port = $ENV{port} || 11211; -my %stats; -# This hash contains the information contained in two memcache commands -# stats and stats settings. - -my %items; -# This gives us eviction rates and other hit stats per slab -# We track this so we can see if something was evicted earlier than necessary - -my %chnks; -# This gives us the memory size and usage per slab -# We track this so we can see what slab is being used the most and has no free chunks -# so we can re-tune memcached to allocate more pages for the specified chunk size - -my $timescale = $ENV{timescale} || 3; # This gives us the ability to control the timescale our graphs are displaying. # The default it set to divide by hours, if you want to get seconds set it to 1. # Options: 1 = seconds, 2 = minutes, 3 = hours, 4 = days +my $timescale = $ENV{timescale} || 3; + +# This hash contains the information contained in two memcache commands +# stats and stats settings. +my %stats; + +# This gives us eviction rates and other hit stats per slab +# We track this so we can see if something was evicted earlier than necessary +my %items; + +# This gives us the memory size and usage per slab +# We track this so we can see what slab is being used the most and has no free chunks +# so we can re-tune memcached to allocate more pages for the specified chunk size +my %chnks; + +=head2 Graph Declarations + + This block of code builds up all of the graph info for all root / sub graphs. + + %graphs: is a container for all of the graph definition information. In here is where you'll + find the configuration information for munin's graphing procedure. + Format: + + $graph{graph_name} => { + config => { + You'll find the main graph config stored here + { key => value }, + { ... }, + }, + datasrc => [ + Name: name given to data value + Attr: Attribute and value, attribute must be valid plugin argument + { name => 'Name', info => 'info about graph', ... }, + { ... }, + ], + } + +=cut -# So I was trying to figure out how to build this up, and looking at some good examples -# I decided to use the format, or for the most part, the format from the mysql_ munin plugin -# for Innodb by Kjell-Magne Ãierud, it just spoke ease of flexibility especially with multigraphs -# thanks btw ;) -# -# %graphs is a container for all of the graph definition information. In here is where you'll -# find the configuration information for munin's graphing procedure. -# Format: -# -# $graph{graph_name} => { -# config => { -# # You'll find keys and values stored here for graph manipulation -# }, -# datasrc => [ -# # Name: name given to data value -# # Attr: Attribute for given value -# { name => 'Name', (Attr) }, -# { ... }, -# ], -# } my %graphs; +# main graph for memcached item count $graphs{items} = { config => { args => '--base 1000 --lower-limit 0', @@ -133,7 +151,7 @@ $graphs{items} = { { name => 'total_items', label => 'New Items', min => '0', type => 'DERIVE' }, ], }; - +# main graph for memcached memory usage $graphs{memory} = { config => { args => '--base 1024 --lower-limit 0', @@ -147,7 +165,7 @@ $graphs{memory} = { { name => 'bytes', draw => 'AREA', label => 'Current Bytes Used', min => '0' }, ], }; - +# main graph for memcached network usage $graphs{bytes} = { config => { args => '--base 1000', @@ -162,7 +180,7 @@ $graphs{bytes} = { { name => 'bytes_written', type => 'DERIVE', label => 'Traffic in (-) / out (+)', negative => 'bytes_read', cdef => 'bytes_written,8,*', min => '0' }, ], }; - +# graph for memcached connections $graphs{conns} = { config => { args => '--base 1000 --lower-limit 0', @@ -178,7 +196,7 @@ $graphs{conns} = { { name => 'avg_conns' , label => 'Avg Connections', min => '0' }, ], }; - +# main graph for memcached commands issued $graphs{commands} = { config => { args => '--base 1000 --lower-limit 0', @@ -200,7 +218,7 @@ $graphs{commands} = { { name => 'decr_misses', type => 'DERIVE', label => 'Decrement Misses', info => 'Number of unsuccessful decrement requests', min => '0' }, ], }; - +# main graph for memcached eviction rates $graphs{evictions} = { config => { args => '--base 1000 --lower-limit 0', @@ -215,7 +233,7 @@ $graphs{evictions} = { { name => 'reclaimed', label => 'Reclaimed Items', info => 'Cumulative Reclaimed Item Entries Across All Slabs', type => 'DERIVE', min => '0' }, ], }; - +# sub graph for breaking memory info down by slab ( sub graph of memory ) $graphs{slabchnks} = { config => { args => '--base 1000 --lower-limit 0', @@ -230,7 +248,7 @@ $graphs{slabchnks} = { { name => 'free_chunks', label => 'Total Chunks Not in Use (Free)', min => '0' }, ], }; - +# sub graph for breaking commands down by slab ( sub graph of commands ) $graphs{slabhits} = { config => { args => '--base 1000 --lower-limit 0', @@ -247,7 +265,7 @@ $graphs{slabhits} = { { name => 'decr_hits', label => 'Decrement Requests', type => 'DERIVE', min => '0' }, ], }; - +# sub graph for breaking evictions down by slab ( sub graph of evictions ) $graphs{slabevics} = { config => { args => '--base 1000 --lower-limit 0', @@ -262,7 +280,7 @@ $graphs{slabevics} = { { name => 'reclaimed', label => 'Reclaimed Expired Items', info => 'This is number of times items were stored in expired entry memory space', type => 'DERIVE', min => '0' }, ], }; - +# sub graph for showing the time between an item was last evicted and requested ( sub graph of evictions ) $graphs{slabevictime} = { config => { args => '--base 1000 --lower-limit 0', @@ -275,7 +293,7 @@ $graphs{slabevictime} = { { name => 'evicted_time', label => 'Eviction Time (LEI)', info => 'Time Since Request for Last Evicted Item', min => '0' }, ], }; - +# sub graph for breaking items down by slab ( sub graph of items ) $graphs{slabitems} = { config => { args => '--base 1000 --lower-limit 0', @@ -288,7 +306,7 @@ $graphs{slabitems} = { { name => 'number', label => 'Items', info => 'This is the amount of items stored in this slab', min => '0' }, ], }; - +# sub graph for showing the age of the eldest item stored in a slab ( sub graph of items ) $graphs{slabitemtime} = { config => { args => '--base 1000 --lower-limit 0', @@ -302,37 +320,47 @@ $graphs{slabitemtime} = { ], }; -## -#### Config Check #### -## +=head1 Munin Checks + + These checks look for config / autoconf / suggest params + +=head2 Config Check + + This block of code looks at the argument that is possibly supplied, + should it be config, it then checks to make sure the plugin + specified exists, assuming it does, it will run the do_config + subroutine for the plugin specified, otherwise it dies complaining + about an unknown plugin. + +=cut if (defined $ARGV[0] && $ARGV[0] eq 'config') { - + # Lets get our plugin from the symlink being called up, we'll also verify its a valid + # plugin that we have graph information for $0 =~ /memcached_multi_(.+)*/; my $plugin = $1; - die 'Unknown Plugin Specified: ' . ($plugin ? $plugin : '') unless $graphs{$plugin}; - # We need to fetch the stats before we do any config, cause its needed for multigraph + # subgraphs which use slab information for title / info per slab fetch_stats(); - # Now lets go ahead and print out our config. do_config($plugin); exit 0; } -## -#### Autoconf Check #### -## +=head2 Autoconf Check + + This block of code looks at the argument that is possibly supplied, + should it be autoconf, we will attempt to connect to the memcached + service specified on the host:port, upon successful connection it + prints yes, otherwise it prints no. + +=cut if (defined $ARGV[0] && $ARGV[0] eq 'autoconf') { - - my $s = IO::Socket::INET->new( - Proto => "tcp", - PeerAddr => $host, - PeerPort => $port, - ); - + # Lets attempt to connect to memcached + my $s = get_conn(); + # Lets verify that we did connect to memcached if (defined($s)) { print "yes\n"; exit 0; @@ -342,18 +370,21 @@ if (defined $ARGV[0] && $ARGV[0] eq 'autoconf') { } } -## -#### Suggest Check #### -## +=head2 Suggest Check + + This block of code looks at the argument that is possibly supplied, + should it be suggest, we are going to print the possible plugins + which can be specified. Note we only specify the root graphs for the + multigraphs, since the rest of the subgraphs will appear "behind" the + root graphs. It also attempts to connect to the memcached service to + verify it is infact running. + +=cut if (defined $ARGV[0] && $ARGV[0] eq 'suggest') { - - my $s = IO::Socket::INET->new( - Proto => "tcp", - PeerAddr => $host, - PeerPort => $port, - ); - + # Lets attempt to connect to memcached + my $s = get_conn(); + # Lets check that we did connect to memcached if (defined($s)) { my @rootplugins = ('bytes','conns','commands','evictions','items','memory'); foreach my $plugin (@rootplugins) { @@ -366,30 +397,29 @@ if (defined $ARGV[0] && $ARGV[0] eq 'suggest') { } } -## -#### Well We aren't running (auto)config/suggest so lets print some stats #### -## +=head1 Output Subroutines + Output Subroutine calls to output data values + +=head2 fetch_output + + This subroutine is the main call for printing data for the plugin. + No parameters are taken as this is the default call if no arguments + are supplied from the command line. + +=cut + +# Well, no arguments were supplied that we know about, so lets print some data fetch_output(); -## -#### Subroutines for printing info gathered from memcached #### -## - -## -#### This subroutine performs the bulk processing for printing statistics. -## - sub fetch_output { - + # Lets get our plugin from the symlink being called up, we'll also verify its a valid + # plugin that we have graph information for $0 =~ /memcached_multi_(.+)*/; my $plugin = $1; - die 'Unknown Plugin Specified: ' . ($plugin ? $plugin : '') unless $graphs{$plugin}; - # Well we need to actually fetch the stats before we do anything to them. fetch_stats(); - # Now lets go ahead and print out our output. my @subgraphs; if ($plugin eq 'memory') { @@ -423,17 +453,24 @@ sub fetch_output { return; } -## -#### This subroutine is for the root non-multigraph graphs which render on the main node page #### -## +=head2 print_root_output + + This block of code prints out the return values for our root graphs. It takes + one parameter $plugin. Returns when completed, this is the non multigraph + output subroutine. + + $plugin; graph we are calling up to print data values for + + Example: print_root_output($plugin); + +=cut sub print_root_output { + # Lets get our plugin, set our graph reference and print out info for Munin my ($plugin) = (@_); - my $graph = $graphs{$plugin}; - print "graph memcached_$plugin\n"; - + # The conns plugin has some specific needs, looking for plugin type if ($plugin ne 'conns') { foreach my $dsrc (@{$graph->{datasrc}}) { my %datasrc = %$dsrc; @@ -461,26 +498,33 @@ sub print_root_output { } } } - return; } -## -#### This subroutine is for the root multigraph graphs which render on the main node page #### -## +=head2 print_rootmulti_output + + This block of code prints out the return values for our root graphs. It takes + one parameter $plugin. Returns when completed, this is the multigraph + output subroutine + + $plugin; main(root) graph we are calling up to print data values for + + Example: print_rootmulti_output($plugin); + +=cut sub print_rootmulti_output { + # Lets get our plugin, set our graph reference and print out info for Munin my ($plugin) = (@_); - my $graph = $graphs{$plugin}; - print "multigraph memcached_$plugin\n"; - + # Lets print our data values with their appropriate name foreach my $dsrc (@{$graph->{datasrc}}) { my $output = 0; my %datasrc = %$dsrc; while ( my ($key, $value) = each(%datasrc)) { next if ($key ne 'name'); + next if (($plugin eq 'evictions') && ($value eq 'reclaimed') && ($stats{version} =~ /1\.4\.[0-2]/)); if (($plugin eq 'evictions') && ($value eq 'evicted_nonzero')) { foreach my $slabid (sort{$a <=> $b} keys %items) { $output += $items{$slabid}->{evicted_nonzero}; @@ -491,24 +535,33 @@ sub print_rootmulti_output { print "$dsrc->{name}.value $output\n"; } } - return; } -## -#### This subroutine is for the sub multigraph graphs created via the multigraph plugin #### -## +=head2 print_submulti_output + + This block of code prints out the return values for our root graphs. It takes + three parameters $slabid, $plugin and @subgraphs. Returns when completed, this + is the multigraph output subroutine for our subgraphs + + $slabid; slab id that we will use to grab info from and print out + $plugin; main(root) being called, used for multigraph output and slab id + @subgraphs; graphs we are actually trying to print data values for + + Example: print_submulti_output($slabid,$plugin,@subgraphs); + +=cut sub print_submulti_output { + # Lets get our slabid, plugin, and subgraphs my ($slabid,$plugin,@subgraphs) = (@_); my $currslab = undef; - + # Time to loop over our subgraphs array foreach my $sgraph (@subgraphs) { - + # Lets set our graph reference for quick calling, and print some info for munin my $graph = $graphs{$sgraph}; - print "multigraph memcached_$plugin.$sgraph\_$slabid\n"; - + # Lets figure out what slab info we are trying to call up if ($plugin eq 'evictions') { $currslab = $items{$slabid}; } elsif ($plugin eq 'memory') { @@ -520,11 +573,12 @@ sub print_submulti_output { } else { return; } - + # Lets print our data values with their appropriate name foreach my $dsrc (@{$graph->{datasrc}}) { my %datasrc = %$dsrc; while ( my ($key, $value) = each(%datasrc)) { next if ($key ne 'name'); + next if (($sgraph eq 'slabevics') && ($value eq 'reclaimed') && ($stats{version} =~ /1\.4\.[0-2]/)); my $output = $currslab->{$value}; if (($sgraph eq 'slabevictime') || ($sgraph eq 'slabitemtime')) { $output = time_scale('data',$output); ; @@ -533,17 +587,23 @@ sub print_submulti_output { } } } - return; } -## -#### Subroutines for printing out config information for graphs #### -## +=head1 Config Subroutines -## -#### This subroutine does the bulk printing the config info per graph #### -## + These subroutines handle the config portion of munin calls. + +=head2 do_config + + This is the main call issued assuming we call up config and plugin specified exists + The subroutine takes one parameter $plugin, and returns when completed. + + $plugin; main(root) graph being called + + Example: do_config($plugin); + +=cut sub do_config { my ($plugin) = (@_); @@ -579,22 +639,59 @@ sub do_config { return; } -## -#### This subroutine is for the config info for sub multigraph graphs created via the multigraph plugin #### -## +=head2 get_conn + + This subroutine returns a socket connection + +=cut + +sub get_conn { + my $s = undef; + + # check if we want to use sockets instead of tcp + my ($sock) = ($host =~ /unix:\/\/(.+)*$/); + + if ($sock) { + $s = IO::Socket::UNIX->new( + Peer => $sock + ); + } else { + $s = IO::Socket::INET->new( + Proto => "tcp", + PeerAddr => $host, + PeerPort => $port, + Timeout => 10, + ); + } + + return $s; +} + +=head2 print_submulti_config + + This subroutine prints out the config information for all of the subgraphs. + It takes three parameters, $slabid, $plugin and @subgraphs, returns when + completed, this is the mutligraph config output for our subgraphs + + $slabid; slab id that we will use to grab info from and print out + $plugin; main(root) being called, used for multigraph output and slab id + @subgraphs; graphs we are actually trying to print data values for + + Example: print_submulti_config($slabid,$plugin,@subgraphs); + +=cut sub print_submulti_config { + # Lets get our slabid, plugin, and subgraphs my ($slabid,$plugin,@subgraphs) = (@_); my ($slabitems,$slabchnks) = undef; - + # Time to loop over our subgraphs array foreach my $sgraph (@subgraphs) { - + # Lets set our graph reference, and main graph config for easy handling my $graph = $graphs{$sgraph}; - my %graphconf = %{$graph->{config}}; - + # Lets tell munin which graph we are graphing, and what our main graph config info is print "multigraph memcached_$plugin.$sgraph\_$slabid\n"; - while ( my ($key, $value) = each(%graphconf)) { if ($key eq 'title') { print "graph_$key $value" . "$slabid" . " ($chnks{$slabid}->{chunk_size} Bytes)\n"; @@ -605,7 +702,7 @@ sub print_submulti_config { print "graph_$key $value\n"; } } - + # Lets tell munin about our data values and how to treat them foreach my $dsrc (@{$graph->{datasrc}}) { my %datasrc = %$dsrc; while ( my ($key, $value) = each(%datasrc)) { @@ -618,25 +715,28 @@ sub print_submulti_config { return; } -## -#### This subroutine is for the config info for root multigraph graphs which render on the main node page #### -## +=head2 print_rootmulti_config + + This subroutine prints out the config information for all of the main(root) graphs. + It takes one parameter, $plugin, returns when completed. + + $plugin; main(root) graph used for multigraph call + + Example: print_rootmulti_config($plugin); + +=cut sub print_rootmulti_config { + # Lets get out plugin, set our graph reference and our graph config info my ($plugin) = (@_); - - die 'Unknown Plugin Specified: ' . ($plugin ? $plugin : '') unless $graphs{$plugin}; - my $graph = $graphs{$plugin}; - my %graphconf = %{$graph->{config}}; - + # Lets tell munin about the graph we are referencing and print the main config print "multigraph memcached_$plugin\n"; - while ( my ($key, $value) = each(%graphconf)) { print "graph_$key $value\n"; } - + # Lets tell munin about our data values and how to treat them foreach my $dsrc (@{$graph->{datasrc}}) { my %datasrc = %$dsrc; while ( my ($key, $value) = each(%datasrc)) { @@ -644,29 +744,31 @@ sub print_rootmulti_config { print "$dsrc->{name}.$key $value\n"; } } - return; } -## -#### This subroutine is for the config info for non multigraph graphs which render on the main node page #### -## +=head2 print_root_config + + This subroutine prints out the config information for all of the main(root) graphs. + It takes one parameter, $plugin, returns when completed. + + $plugin; main(root) graph used for multigraph call + + Example: print_root_config($plugin); + +=cut sub print_root_config { + # Lets get our plugin, set our graph reference and our graph config info my ($plugin) = (@_); - - die 'Unknown Plugin Specified: ' . ($plugin ? $plugin : '') unless $graphs{$plugin}; - my $graph = $graphs{$plugin}; - my %graphconf = %{$graph->{config}}; - + # Lets tell munin about the graph we are referencing and print the main config print "graph memcached_$plugin\n"; - while ( my ($key, $value) = each(%graphconf)) { print "graph_$key $value\n"; } - + # Lets tell munin about our data values and how to treat them foreach my $dsrc (@{$graph->{datasrc}}) { my %datasrc = %$dsrc; while ( my ($key, $value) = each(%datasrc)) { @@ -674,46 +776,49 @@ sub print_root_config { print "$dsrc->{name}.$key $value\n"; } } - return; } -## -#### This subroutine actually performs the data fetch for us #### -#### These commands do not lock up Memcache at all #### -## +=head1 Misc Subroutines + + These subroutines are misc ones, and are referenced inside of the code. They + should never be called up by Munin. + +=head2 fetch_stats + + This subroutine fetches the information from memcached and stores it into our + hashes for later referencing throughout the graph. Returns when completed + +=cut sub fetch_stats { - my $s = IO::Socket::INET->new( - Proto => "tcp", - PeerAddr => $host, - PeerPort => $port, - ); - + # Lets try and connect to memcached + my $s = get_conn(); + # Die if we can't establish a connection to memcached die "Error: Unable to Connect to $host\[:$port\]\n" unless $s; - + # Lets print the stats command and store the info from the output print $s "stats\r\n"; - while (my $line = <$s>) { - if ($line =~ /STAT\s(.+?)\s(\d+)/) { + if ($line =~ /STAT\s(.+?)\s(.*)/) { my ($skey,$svalue) = ($1,$2); $stats{$skey} = $svalue; } last if $line =~ /^END/; } - + # Lets print the stats settings command and store the info from the output print $s "stats settings\r\n"; - while (my $line = <$s>) { - if ($line =~ /STAT\s(.+?)\s(\d+)/) { + if ($line =~ /STAT\s(.+?)\s(.*)/) { my ($skey,$svalue) = ($1,$2); + if ($skey eq 'evictions') { + $skey = 'evictions_active'; + } $stats{$skey} = $svalue; } last if $line =~ /^END/; } - + # Lets print the stats slabs command and store the info from the output print $s "stats slabs\r\n"; - while (my $line = <$s>) { if ($line =~ /STAT\s(\d+):(.+)\s(\d+)/) { my ($slabid,$slabkey,$slabvalue) = ($1,$2,$3); @@ -721,9 +826,8 @@ sub fetch_stats { } last if $line =~ /^END/; } - + # Lets print the stats items command and store the info from the output print $s "stats items\r\n"; - while (my $line = <$s>) { if ($line =~ /STAT\sitems:(\d+):(.+?)\s(\d+)/) { my ($itemid,$itemkey,$itemvalue) = ($1,$2,$3); @@ -733,14 +837,23 @@ sub fetch_stats { } } -## -#### This subroutine is to help manage the time_scale settings for the graph -## +=head2 time_scale + + This subroutine is here for me to adjust the timescale of the time graphs + for last evicted item and age of eldest item in cache. + + Please note, after long usage I have noticed these counters may not + be accurate, I believe the developers are aware and have submitted + a patch upstream. + +=cut sub time_scale { + # Lets get our config option and value to adjust my ($configopt,$origvalue) = (@_); my $value; - + # If config is defined, it returns the config info for time scale + # If data is defined, it returns the original value after its been adjusted if ($configopt eq 'config') { if ($timescale == 1) { $value = "Seconds" . $origvalue; diff --git a/plugins/other/memcached_requests_ b/plugins/memcached/memcached_requests_ similarity index 91% rename from plugins/other/memcached_requests_ rename to plugins/memcached/memcached_requests_ index 284617fd..b414b674 100755 --- a/plugins/other/memcached_requests_ +++ b/plugins/memcached/memcached_requests_ @@ -6,9 +6,16 @@ use warnings; use Cache::Memcached; +$0 =~ /memcached_requests_(\d+_\d+_\d+_\d+)_(\d+)$/; +my ($ip, $port) = ($1, $2); +$ip =~ s/_/./g; +my $address = "$ip:$port"; + +my $title = $ENV{title} || $address; + my $cmd = shift || ''; if ($cmd eq 'config') { - print "graph_title Memcached requests\n"; + print "graph_title Memcached requests -- $title\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel requests\n"; print "graph_category memcached\n"; @@ -26,11 +33,6 @@ if ($cmd eq 'config') { exit 0; } -$0 =~ /memcached_requests_(\d+_\d+_\d+_\d+)_(\d+)$/; -my ($ip, $port) = ($1, $2); -$ip =~ s/_/./g; -my $address = "$ip:$port"; - my $memd = new Cache::Memcached { 'servers' => [$address] }; my $memstats = $memd->stats(['misc']); diff --git a/plugins/other/memcached_traffic_ b/plugins/memcached/memcached_traffic_ similarity index 100% rename from plugins/other/memcached_traffic_ rename to plugins/memcached/memcached_traffic_ diff --git a/plugins/other/mongrel_memory b/plugins/memory/mongrel_memory similarity index 100% rename from plugins/other/mongrel_memory rename to plugins/memory/mongrel_memory diff --git a/plugins/other/mongrel_process_memory b/plugins/memory/mongrel_process_memory similarity index 100% rename from plugins/other/mongrel_process_memory rename to plugins/memory/mongrel_process_memory diff --git a/plugins/minecraft/minecraft-users b/plugins/minecraft/minecraft-users new file mode 100755 index 00000000..7e4861f3 --- /dev/null +++ b/plugins/minecraft/minecraft-users @@ -0,0 +1,36 @@ +#!/usr/local/bin/ruby +# Config: +# [minecraft_users] +# env.host awesomeserver.com +# env.port 25566 +# + +require 'socket' + +if ARGV[0] == 'config' + puts "graph_title Connected players" + puts "graph_vlabel players" + puts "players.label players" + puts "graph_info Number of players connected to Minecraft" + puts "graph_category Minecraft" + exit +end + +host = ENV['host'] +host = 'localhost' unless host + +port = ENV['port'] +port = '25566' unless port + +socket = TCPSocket.new(host, port) +socket.puts "QUERY" + +response = socket.read +response = response.split("\n") + +server_port = response[0].split(" ", 2)[1].to_i +player_count = response[1].split(" ", 2)[1].to_i +max_players = response[2].split(" ", 2)[1].to_i +player_list = response[3].split(" ", 2)[1].chomp[1..-2] + +puts "players.value #{player_count}" \ No newline at end of file diff --git a/plugins/other/moblock_connections b/plugins/moblock/moblock_connections similarity index 100% rename from plugins/other/moblock_connections rename to plugins/moblock/moblock_connections diff --git a/plugins/other/mod_jk b/plugins/mod_jk/mod_jk similarity index 100% rename from plugins/other/mod_jk rename to plugins/mod_jk/mod_jk diff --git a/plugins/other/mogilefsd_activity b/plugins/mogilefs/mogilefsd_activity similarity index 100% rename from plugins/other/mogilefsd_activity rename to plugins/mogilefs/mogilefsd_activity diff --git a/plugins/other/mogilefsd_queries b/plugins/mogilefs/mogilefsd_queries similarity index 100% rename from plugins/other/mogilefsd_queries rename to plugins/mogilefs/mogilefsd_queries diff --git a/plugins/other/mongo_btree b/plugins/mongodb/mongo_btree similarity index 100% rename from plugins/other/mongo_btree rename to plugins/mongodb/mongo_btree diff --git a/plugins/other/mongo_conn b/plugins/mongodb/mongo_conn similarity index 100% rename from plugins/other/mongo_conn rename to plugins/mongodb/mongo_conn diff --git a/plugins/other/mongo_lock b/plugins/mongodb/mongo_lock similarity index 100% rename from plugins/other/mongo_lock rename to plugins/mongodb/mongo_lock diff --git a/plugins/other/mongo_mem b/plugins/mongodb/mongo_mem similarity index 100% rename from plugins/other/mongo_mem rename to plugins/mongodb/mongo_mem diff --git a/plugins/other/mongo_ops b/plugins/mongodb/mongo_ops similarity index 100% rename from plugins/other/mongo_ops rename to plugins/mongodb/mongo_ops diff --git a/plugins/other/monit_parser b/plugins/monit/monit_parser similarity index 100% rename from plugins/other/monit_parser rename to plugins/monit/monit_parser diff --git a/plugins/other/microsoft-sql b/plugins/mssql/microsoft-sql similarity index 100% rename from plugins/other/microsoft-sql rename to plugins/mssql/microsoft-sql diff --git a/plugins/other/microsoft-sql-buffer-cache-hit-ratio b/plugins/mssql/microsoft-sql-buffer-cache-hit-ratio similarity index 100% rename from plugins/other/microsoft-sql-buffer-cache-hit-ratio rename to plugins/mssql/microsoft-sql-buffer-cache-hit-ratio diff --git a/plugins/other/microsoft-sql-data-file-sizes b/plugins/mssql/microsoft-sql-data-file-sizes similarity index 100% rename from plugins/other/microsoft-sql-data-file-sizes rename to plugins/mssql/microsoft-sql-data-file-sizes diff --git a/plugins/other/microsoft-sql-log-file-size b/plugins/mssql/microsoft-sql-log-file-size similarity index 100% rename from plugins/other/microsoft-sql-log-file-size rename to plugins/mssql/microsoft-sql-log-file-size diff --git a/plugins/other/munin_stats_plugins b/plugins/munin/munin_stats_plugins similarity index 100% rename from plugins/other/munin_stats_plugins rename to plugins/munin/munin_stats_plugins diff --git a/plugins/other/mysql_connections b/plugins/mysql/mysql_connections similarity index 100% rename from plugins/other/mysql_connections rename to plugins/mysql/mysql_connections diff --git a/plugins/other/mysql_innodb b/plugins/mysql/mysql_innodb similarity index 100% rename from plugins/other/mysql_innodb rename to plugins/mysql/mysql_innodb diff --git a/plugins/other/mysql_qcache b/plugins/mysql/mysql_qcache similarity index 100% rename from plugins/other/mysql_qcache rename to plugins/mysql/mysql_qcache diff --git a/plugins/other/mysql_qcache_mem b/plugins/mysql/mysql_qcache_mem similarity index 100% rename from plugins/other/mysql_qcache_mem rename to plugins/mysql/mysql_qcache_mem diff --git a/plugins/other/mysql_report b/plugins/mysql/mysql_report similarity index 100% rename from plugins/other/mysql_report rename to plugins/mysql/mysql_report diff --git a/plugins/other/mysql_size_ b/plugins/mysql/mysql_size_ similarity index 100% rename from plugins/other/mysql_size_ rename to plugins/mysql/mysql_size_ diff --git a/plugins/other/mysql_size_all b/plugins/mysql/mysql_size_all similarity index 100% rename from plugins/other/mysql_size_all rename to plugins/mysql/mysql_size_all diff --git a/plugins/other/mysql_slave b/plugins/mysql/mysql_slave similarity index 100% rename from plugins/other/mysql_slave rename to plugins/mysql/mysql_slave diff --git a/plugins/other/mysql_slave_threads b/plugins/mysql/mysql_slave_threads similarity index 100% rename from plugins/other/mysql_slave_threads rename to plugins/mysql/mysql_slave_threads diff --git a/plugins/other/mysql-schema-size b/plugins/mysql_schema_size_/mysql-schema-size similarity index 100% rename from plugins/other/mysql-schema-size rename to plugins/mysql_schema_size_/mysql-schema-size diff --git a/plugins/other/mysql-table-size b/plugins/mysql_table_size_/mysql-table-size similarity index 100% rename from plugins/other/mysql-table-size rename to plugins/mysql_table_size_/mysql-table-size diff --git a/plugins/other/dvb-signal b/plugins/mythtv/dvb-signal similarity index 100% rename from plugins/other/dvb-signal rename to plugins/mythtv/dvb-signal diff --git a/plugins/other/mythtv_programs b/plugins/mythtv/mythtv_programs similarity index 100% rename from plugins/other/mythtv_programs rename to plugins/mythtv/mythtv_programs diff --git a/plugins/other/mythtv_status_ b/plugins/mythtv/mythtv_status_ similarity index 100% rename from plugins/other/mythtv_status_ rename to plugins/mythtv/mythtv_status_ diff --git a/plugins/nagios/nagios_multi_ b/plugins/nagios/nagios_multi_ new file mode 100755 index 00000000..926428bc --- /dev/null +++ b/plugins/nagios/nagios_multi_ @@ -0,0 +1,701 @@ +#!/usr/bin/perl +# +=head1 NAGIOS MULTIGRAPH + +A Plugin to monitor Nagios Servers and their Performance (Multigraph) + +=head1 MUNIN CONFIGURATION + +[nagios_multi_*] + user root + env.binary /usr/local/nagios/bin/nagiostats *default* + env.passive off *default* + +=head2 MUNIN ENVIRONMENT CONFIGURATION EXPLANATION + + binary = location of your nagiostats binary including binary + passive = tell the plugin to graph passive results + +=head1 NODE CONFIGURATION + + Make sure the nagiostats binary exists and is executable by root + or by the user specified that the plugin will run as. + + Available root graphs and subgraphs contained in this Plugin. + + services => I This graphs the current service problems. + svcchkdetail => This graph shows current services warning,critical,unknown,checked,scheduled,flapping,down + svcchkext => This graph shows the service check execution times + svcchklat => This graph shows the service check latency times + svcchksc => This graph shows the serivce check state change % + + hosts => I This graphs the current host problems. + hostchkdetail => This graph shows current hosts down,unreachable,checked,scheduled,flapping,down + hostchkext => This graph shows the host check execution times + hostchklat => This graph shows the host check latency times + hostchksc => This graph shows the host check state change % + + checks => I This graphs the current host problems. + extcmdcount => This graph shows external command buffer availability / usage + hostchkactcount => This graph shows the active host checks for the last 1,5,15,60M + hostchkpsvcount => This graph shows the passive host checks for the last 1,5,15,60M + * depends on passive flag, which defaults to off, and forces these graphs to not be drawn + svcchkactcount => This graph shows the active service checks for the last 1,5,15,60M + svcchkpsvcount => This graph shows the passive service checks for the last 1,5,15,60M + * depends on passive flag, which defaults to off, and forces these graphs to not be drawn + +=head1 MUNIN PLUGIN DOCUMENTATION + + This is just some helpful links for plugin troubleshooting. + + http://munin-monitoring.org/wiki/Documentation#Plugins + http://munin-monitoring.org/wiki/protocol-config + +=head1 AUTHOR + +Matt West < https://github.com/mhwest13/Nagios-Munin-Plugin > + +=head1 LICENSE + +GPLv2 + +=head1 MAGIC MARKERS + +#%# family=auto +#%# capabilities=autoconf suggest + +=cut + +use strict; +use warnings; +use Munin::Plugin; +use File::Basename; + +if (basename($0) !~ /^nagios_multi_/) { + print "This script needs to be named nagios_multi_ and have symlinks which start the same.\n"; + exit 1; +} + +# tell munin about our multigraph capabilties +need_multigraph(); + +# import binary information or use default setting +my $binary = $ENV{binary} || '/usr/local/nagios/bin/nagiostats'; +unless ((-e $binary) && (-x $binary)) { + # err, I'm unable to run the binary specified + print "no: Unable to execute $binary\n"; + exit 1; +} + +# import passive flag or use default setting +my $passive = $ENV{passive} || 'off'; + +=head1 Graph Declarations + + This block of code builds up all of the graph info for all root / sub graphs. + + %graphs: is a container for all of the graph definition information. In here is where you'll + find the configuration information for munin's graphing procedure. + Format: + + $graph{graph_name} => { + config => { + You'll find the main graph config stored here + { key => value }, + { ... }, + }, + keys => [ 'Name', 'Name', 'Name', ... ], + datasrc => [ + Name: name given to data value + Attr: Attribute and value, attribute must be valid plugin argument + { name => 'Name', info => 'info about graph' }, + { ... }, + ], + results => { + You'll find the results info from a stats call stored here + { key => value }, + { ... }, + }, + } + +=cut + +my %graphs; + +# main graph for service checks +$graphs{services} = { + config => { + args => '--lower-limit 0', + vlabel => 'Service Problems', + category => 'nagios', + title => 'Service Problems', + info => 'Current Service Problems by Alert Status', + }, + keys => [ 'NUMSVCOK', 'NUMSVCWARN', 'NUMSVCUNKN', 'NUMSVCCRIT' ], + datasrc => [ + { name => 'NUMSVCOK', label => 'Up', min => '0', type => 'GAUGE', info => 'number of services which are Ok.', graph => 'no', draw => 'LINE2' }, + { name => 'NUMSVCWARN', label => 'Warning', min => '0', type => 'GAUGE', info => 'number of services which are Warning.', draw => 'LINE2' }, + { name => 'NUMSVCUNKN', label => 'Unknown', min => '0', type => 'GAUGE', info => 'number of services which are Unknown.', draw => 'LINE2' }, + { name => 'NUMSVCCRIT', label => 'Critical', min => '0', type => 'GAUGE', info => 'number of services which are Critical.', draw => 'LINE2' }, + ], +}; +# multi-graph for service check detail information ( sub graph of service problems graph ) +$graphs{svcchkdetail} = { + config => { + args => '--lower-limit 0', + vlabel => 'Total # of Service Checks', + category => 'details', + title => 'Detailed Service Info', + info => 'Detailed Service Check Information', + }, + keys => [ 'NUMSVCWARN', 'NUMSVCUNKN', 'NUMSVCCRIT', 'NUMSVCCHECKED', 'NUMSVCSCHEDULED', 'NUMSVCFLAPPING', 'NUMSVCDOWNTIME' ], + datasrc => [ + { name => 'NUMSVCWARN', label => 'Warning', min => '0', type => 'GAUGE', info => 'number of services which are Warning.', draw => 'LINE2' }, + { name => 'NUMSVCUNKN', label => 'Unknown', min => '0', type => 'GAUGE', info => 'number of services which are Unknown.', draw => 'LINE2' }, + { name => 'NUMSVCCRIT', label => 'Critical', min => '0', type => 'GAUGE', info => 'number of services which are Critical.', draw => 'LINE2' }, + { name => 'NUMSVCCHECKED', label => 'Checked', min => '0', type => 'GAUGE', info => 'total number of services that have been checked since start.', draw => 'LINE2' }, + { name => 'NUMSVCSCHEDULED', label => 'Scheduled', min => '0', type => 'GAUGE', info => 'total number of services that are currently scheduled to be checked.', draw => 'LINE2' }, + { name => 'NUMSVCFLAPPING', label => 'Flapping', min => '0', type => 'GAUGE', info => 'total number of services that are currently flapping.', draw => 'LINE2' }, + { name => 'NUMSVCDOWNTIME', label => 'Scheduled Downtime', min => '0', type => 'GAUGE', info => 'total number of services that are currently in scheduled downtime.', draw => 'LINE2' }, + ], +}; +# multi-graph for service check % state change ( sub graph of service problems graph ) +$graphs{svcchksc} = { + config => { + args => '--lower-limit 0 --upper-limit 100', + vlabel => '%', + category => 'statechange', + title => 'Service State Change', + info => 'Total Percent of State Change between checks', + }, + keys => [ 'MINSVCPSC', 'MAXSVCPSC', 'AVGSVCPSC' ], + datasrc => [ + { name => 'MINSVCPSC', label => 'Min', min => '0', type => 'GAUGE', info => 'min service check % state change.', draw => 'AREA' }, + { name => 'MAXSVCPSC', label => 'Max', min => '0', type => 'GAUGE', info => 'max service check % state change.', draw => 'AREA' }, + { name => 'AVGSVCPSC', label => 'Average', min => '0', type => 'GAUGE', info => 'avg service check % state change.', draw => 'LINE2' }, + ], +}; +# multi-graph for service check latency and execution times ( sub graph of service problems graph ) +$graphs{svcchklat} = { + config => { + args => '--lower-limit 0', + vlabel => 'time (ms)', + category => 'latency', + title => 'Service Check Latency Times', + info => 'Service Check Latency Times', + }, + keys => [ 'MINACTSVCLAT', 'MAXACTSVCLAT', 'AVGACTSVCLAT' ], + datasrc => [ + { name => 'MINACTSVCLAT', label => 'Min Latency', min => '0', type => 'GAUGE', info => 'min active service check latency (ms).', draw => 'LINE2' }, + { name => 'MAXACTSVCLAT', label => 'Max Latency', min => '0', type => 'GAUGE', info => 'max active service check latency (ms).', draw => 'LINE2' }, + { name => 'AVGACTSVCLAT', label => 'Average Latency', min => '0', type => 'GAUGE', info => 'avg active service check latency (ms).', draw => 'LINE2' }, + ], +}; +# multi-graph for service check execution time ( sub graph of service problems graph ) +$graphs{svcchkext} = { + config => { + args => '--lower-limit 0', + vlabel => 'time (ms)', + category => 'execution', + title => 'Service Check Execution Times', + info => 'Service Check Execution Times', + }, + keys => [ 'MINACTSVCEXT', 'MAXACTSVCEXT', 'AVGACTSVCEXT' ], + datasrc => [ + { name => 'MINACTSVCEXT', label => 'Min Execution', min => '0', type => 'GAUGE', info => 'min active service check execution time (ms).', draw => 'LINE2' }, + { name => 'MAXACTSVCEXT', label => 'Max Execution', min => '0', type => 'GAUGE', info => 'max active service check execution time (ms).', draw => 'LINE2' }, + { name => 'AVGACTSVCEXT', label => 'Average Execution', min => '0', type => 'GAUGE', info => 'avg active service check execution time (ms).', draw => 'LINE2' }, + ], +}; +# main graph for host problems +$graphs{hosts} = { + config => { + args => '--lower-limit 0', + vlabel => 'Host Problems', + category => 'nagios', + title => 'Host Problems', + info => 'Current Host Problems by Alert Status', + }, + keys => [ 'NUMHSTUP', 'NUMHSTDOWN', 'NUMHSTUNR' ], + datasrc => [ + { name => 'NUMHSTUP', label => 'Up', min => '0', type => 'GAUGE', info => 'number of hosts up.', graph => 'no', draw => 'LINE2' }, + { name => 'NUMHSTDOWN', label => 'Down', min => '0', type => 'GAUGE', info => 'number of hosts which are down.', draw => 'LINE2' }, + { name => 'NUMHSTUNR', label => 'Unknown', min => '0', type => 'GAUGE', info => 'number of hosts which are Unreachable.', draw => 'LINE2' }, + ], +}; +# multi-graph for host check detail information ( sub graph of host problems graph ) +$graphs{hostchkdetail} = { + config => { + args => '--lower-limit 0', + vlabel => 'Total # of Host Checks', + category => 'details', + title => 'Detailed Host Info', + info => 'Detailed Host Check Information', + }, + keys => [ 'NUMHSTDOWN', 'NUMHSTUNR', 'NUMHSTCHECKED', 'NUMHSTSCHEDULED', 'NUMHSTFLAPPING', 'NUMHSTDOWNTIME' ], + datasrc => [ + { name => 'NUMHSTDOWN', label => 'Down', min => '0', type => 'GAUGE', info => 'number of hosts which are down.', draw => 'LINE2' }, + { name => 'NUMHSTUNR', label => 'Unknown', min => '0', type => 'GAUGE', info => 'number of hosts which are Unreachable.', draw => 'LINE2' }, + { name => 'NUMHSTCHECKED', label => 'Checked', min => '0', type => 'GAUGE', info => 'total number of hosts that have been checked since start.', draw => 'LINE2' }, + { name => 'NUMHSTSCHEDULED', label => 'Scheduled', min => '0', type => 'GAUGE', info => 'total number of hosts that are currently scheduled to be checked.', draw => 'LINE2' }, + { name => 'NUMHSTFLAPPING', label => 'Flapping', min => '0', type => 'GAUGE', info => 'total number of hosts that are currently flapping.', draw => 'LINE2' }, + { name => 'NUMHSTDOWNTIME', label => 'Downtime', min => '0', type => 'GAUGE', info => 'total number of hosts that are currently in scheduled downtime.', draw => 'LINE2' }, + ], +}; +# multi-graph for host check % state change ( sub graph of host problems graph ) +$graphs{hostchksc} = { + config => { + args => '--lower-limit 0 --upper-limit 100', + vlabel => '%', + category => 'statechange', + title => 'Host State Change', + info => 'Total Percent of State Change between checks', + }, + keys => [ 'MINHSTPSC', 'MAXHSTPSC', 'AVGHSTPSC' ], + datasrc => [ + { name => 'MINHSTPSC', label => 'Min', min => '0', type => 'GAUGE', info => 'min host check % state change.', draw => 'AREA' }, + { name => 'MAXHSTPSC', label => 'Max', min => '0', type => 'GAUGE', info => 'max host check % state change.', draw => 'AREA' }, + { name => 'AVGHSTPSC', label => 'Average', min => '0', type => 'GAUGE', info => 'avg host check % state change.', draw => 'LINE2' }, + ], +}; +# multi-graph for host check latency times ( sub graph of host problems graph ) +$graphs{hostchklat} = { + config => { + args => '--lower-limit 0', + vlabel => 'time (ms)', + category => 'latency', + title => 'Host Check Latency Times', + info => 'Host Check Latency Times', + }, + keys => [ 'MINACTHSTLAT', 'MAXACTHSTLAT', 'AVGACTHSTLAT' ], + datasrc => [ + { name => 'MINACTHSTLAT', label => 'Min Latency', min => '0', type => 'GAUGE', info => 'min active host check latency (ms).', draw => 'LINE2' }, + { name => 'MAXACTHSTLAT', label => 'Max Latency', min => '0', type => 'GAUGE', info => 'max active host check latency (ms).', draw => 'LINE2' }, + { name => 'AVGACTHSTLAT', label => 'Average Latency', min => '0', type => 'GAUGE', info => 'avg active host check latency (ms).', draw => 'LINE2' }, + ], +}; +# multi-graph for host check execution times ( sub graph of host problems graph ) +$graphs{hostchkext} = { + config => { + args => '--lower-limit 0', + vlabel => 'time (ms)', + category => 'execution', + title => 'Host Check Execution Times', + info => 'Host Check Execution Times', + }, + keys => [ 'MINACTHSTEXT', 'MAXACTHSTEXT', 'AVGACTHSTEXT' ], + datasrc => [ + { name => 'MINACTHSTEXT', label => 'Min Execution', min => '0', type => 'GAUGE', info => 'min active host check execution time (ms).', draw => 'LINE2' }, + { name => 'MAXACTHSTEXT', label => 'Max Execution', min => '0', type => 'GAUGE', info => 'max active host check execution time (ms).', draw => 'LINE2' }, + { name => 'AVGACTHSTEXT', label => 'Average Execution', min => '0', type => 'GAUGE', info => 'avg active host check execution time (ms).', draw => 'LINE2' }, + ], +}; +# main graph for host / service check counts +$graphs{checks} = { + config => { + args => '--lower-limit 0', + vlabel => 'Total # of Checks', + category => 'nagios', + title => 'Totals', + info => 'Total Number of Service and Host Checks', + }, + keys => [ 'NUMSERVICES', 'NUMHOSTS' ], + datasrc => [ + { name => 'NUMSERVICES', label => '# of Services', min => '0', type => 'GAUGE', info => 'total number of services.', draw => 'LINE2' }, + { name => 'NUMHOSTS', label => '# of Hosts', min => '0', type => 'GAUGE', info => 'total number of hosts.', draw => 'LINE2' }, + ], +}; +# multi-graph for number of host checks in x mins ( sub graph of checks graph ) +$graphs{hostchkactcount} = { + config => { + args => '--lower-limit 0', + vlabel => '# Host Checks', + category => 'active', + title => 'Host Checks', + info => 'Total Number of Active Host Checks', + order => 'NUMHSTACTCHK60M NUMHSTACTCHK15M NUMHSTACTCHK5M NUMHSTACTCHK1M', + }, + keys => [ 'NUMHSTACTCHK1M', 'NUMHSTACTCHK5M', 'NUMHSTACTCHK15M', 'NUMHSTACTCHK60M' ], + datasrc => [ + { name => 'NUMHSTACTCHK1M', label => 'Active Checks 1m', min => '0', type => 'GAUGE', info => 'number of hosts actively checked in last 1 minutes.', draw => 'AREA' }, + { name => 'NUMHSTACTCHK5M', label => 'Active Checks 5m', min => '0', type => 'GAUGE', info => 'number of hosts actively checked in last 5 minutes.', draw => 'AREA' }, + { name => 'NUMHSTACTCHK15M', label => 'Active Checks 15m', min => '0', type => 'GAUGE', info => 'number of hosts actively checked in last 15 minutes.', draw => 'AREA' }, + { name => 'NUMHSTACTCHK60M', label => 'Active Checks 60m', min => '0', type => 'GAUGE', info => 'number of hosts actively checked in last 60 minutes.', draw => 'AREA' }, + ], +}; +# multi-graph for number of host checks in x mins ( sub graph of checks graph ) +$graphs{hostchkpsvcount} = { + config => { + args => '--lower-limit 0', + vlabel => '# Host Checks', + category => 'passive', + title => 'Host Checks', + info => 'Total Number of Passive Host Checks', + order => 'NUMHSTPSVCHK60M NUMHSTPSVCHK15M NUMHSTPSVCHK5M NUMHSTPSVCHK1M', + }, + keys => [ 'NUMHSTPSVCHK1M', 'NUMHSTPSVCHK5M', 'NUMHSTPSVCHK15M', 'NUMHSTPSVCHK60M' ], + datasrc => [ + { name => 'NUMHSTPSVCHK1M', label => 'Passive Checks 1m', min => '0', type => 'GAUGE', info => 'number of hosts passively checked in last 1 minutes.', draw => 'AREA' }, + { name => 'NUMHSTPSVCHK5M', label => 'Passive Checks 5m', min => '0', type => 'GAUGE', info => 'number of hosts passively checked in last 5 minutes.', draw => 'AREA' }, + { name => 'NUMHSTPSVCHK15M', label => 'Passive Checks 15m', min => '0', type => 'GAUGE', info => 'number of hosts passively checked in last 15 minutes.', draw => 'AREA' }, + { name => 'NUMHSTPSVCHK60M', label => 'Passive Checks 60m', min => '0', type => 'GAUGE', info => 'number of hosts passively checked in last 60 minutes.', draw => 'AREA' }, + ], +}; +# multi-graph for number of service checks in x mins ( sub graph of checks graph ) +$graphs{svcchkactcount} = { + config => { + args => '--lower-limit 0', + vlabel => '# of Service Checks', + category => 'active', + title => 'Service Checks', + info => 'Total Number of Active Service Checks', + order => 'NUMSVCACTCHK60M NUMSVCACTCHK15M NUMSVCACTCHK5M NUMSVCACTCHK1M', + }, + keys => [ 'NUMSVCACTCHK1M', 'NUMSVCACTCHK5M', 'NUMSVCACTCHK15M', 'NUMSVCACTCHK60M' ], + datasrc => [ + { name => 'NUMSVCACTCHK1M', label => 'Active Checks 1m', min => '0', type => 'GAUGE', info => 'number of services actively checked in last 1 minutes.', draw => 'AREA' }, + { name => 'NUMSVCACTCHK5M', label => 'Active Checks 5m', min => '0', type => 'GAUGE', info => 'number of services actively checked in last 5 minutes.', draw => 'AREA' }, + { name => 'NUMSVCACTCHK15M', label => 'Active Checks 15m', min => '0', type => 'GAUGE', info => 'number of services actively checked in last 15 minutes.', draw => 'AREA' }, + { name => 'NUMSVCACTCHK60M', label => 'Active Checks 60m', min => '0', type => 'GAUGE', info => 'number of services actively checked in last 60 minutes.', draw => 'AREA' }, + ], +}; +# multi-graph for number of service checks in x mins ( sub graph of checks graph ) +$graphs{svcchkpsvcount} = { + config => { + args => '--lower-limit 0', + vlabel => '# of Service Checks', + category => 'passive', + title => 'Service Checks', + info => 'Total Number of Passive Service Checks', + order => 'NUMSVCPSVCHK60M NUMSVCPSVCHK15M NUMSVCPSVCHK5M NUMSVCPSVCHK1M', + }, + keys => [ 'NUMSVCPSVCHK1M', 'NUMSVCPSVCHK5M', 'NUMSVCPSVCHK15M', 'NUMSVCPSVCHK60M' ], + datasrc => [ + { name => 'NUMSVCPSVCHK1M', label => 'Passive Checks 1m', min => '0', type => 'GAUGE', info => 'number of services passively checked in last 1 minutes.', draw => 'AREA' }, + { name => 'NUMSVCPSVCHK5M', label => 'Passive Checks 5m', min => '0', type => 'GAUGE', info => 'number of services passively checked in last 5 minutes.', draw => 'AREA' }, + { name => 'NUMSVCPSVCHK15M', label => 'Passive Checks 15m', min => '0', type => 'GAUGE', info => 'number of services passively checked in last 15 minutes.', draw => 'AREA' }, + { name => 'NUMSVCPSVCHK60M', label => 'Passive Checks 60m', min => '0', type => 'GAUGE', info => 'number of services passively checked in last 60 minutes.', draw => 'AREA' }, + ], +}; +# multi-graph for external command count ( sub graph of checks graph ) +$graphs{extcmdcount} = { + config => { + args => '--lower-limit 0', + vlabel => '# of Ext Command Slots', + category => 'externalcmds', + title => 'External Commands', + info => 'External Command Buffer Slot Information', + }, + keys => [ 'TOTCMDBUF', 'USEDCMDBUF', 'HIGHCMDBUF', 'NUMEXTCMDS1M', 'NUMEXTCMDS5M', 'NUMEXTCMDS15M' ], + datasrc => [ + { name => 'TOTCMDBUF', label => 'Total', min => '0', type => 'GAUGE', info => 'total number of external command buffer slots available.', draw => 'AREA' }, + { name => 'USEDCMDBUF', label => 'Current Used', min => '0', type => 'GAUGE', info => 'number of external command buffer slots currently in use.', draw => 'LINE2' }, + { name => 'HIGHCMDBUF', label => 'Peak Used', min => '0', type => 'GAUGE', info => 'highest number of external command buffer slots ever in use.', draw => 'LINE2' }, + { name => 'NUMEXTCMDS1M', label => 'Used last 1m', min => '0', type => 'GAUGE', info => 'number of external commands processed in last 1 minutes.', draw => 'LINE2' }, + { name => 'NUMEXTCMDS5M', label => 'Used last 5m', min => '0', type => 'GAUGE', info => 'number of external commands processed in last 5 minutes.', draw => 'LINE2' }, + { name => 'NUMEXTCMDS15M', label => 'Used last 15m', min => '0', type => 'GAUGE', info => 'number of external commands processed in last 15 minutes.', draw => 'LINE2' }, + ], +}; + +=head1 Munin Checks + + These checks look for config / autoconf / suggest params + +=head2 Config Check + + This block of code looks at the argument that is possibly supplied, + should it be config, it then checks to make sure the plugin + specified exists, assuming it does, it will run the do_config + subroutine for the plugin specified, otherwise it dies complaining + about an unknown plugin. + +=cut + +if (defined $ARGV[0] && $ARGV[0] eq 'config') { + # Lets take the plugin from the execution name. + $0 =~ /nagios_multi_(.+)*/; + my $plugin = $1; + # And lets make sure we have a plugin called that. + die 'Unknown Plugin Specified: ' . ($plugin ? $plugin : '') unless $graphs{$plugin}; + # Now lets go ahead and print out our config. + do_config($plugin); + exit 0; +} + +=head2 Autoconf Check + + This block of code looks at the argument that is possibly supplied, + should it be autoconf, we are going to print yes at this point since + we've already tested for our binary to exist and be executable, the + process will then exit. + +=cut + +if (defined $ARGV[0] && $ARGV[0] eq 'autoconf') { + # well we can execute the binary, so plugin should be good from here... + print "yes\n"; + exit 0; +} + +=head2 Suggest Check + + This block of code looks at the argument that is possibly supplied, + should it be suggest, we are going to print the possible plugins + which can be specified. Note we only specify the root graphs for the + multigraphs, since the rest of the subgraphs will appear "behind" the + root graphs. + +=cut + +if (defined $ARGV[0] && $ARGV[0] eq 'suggest') { + # well we can execute the binary, so print possible root multigraph plugin names + my @rootplugins = ('services','hosts','checks'); + foreach my $plugin (@rootplugins) { + print "$plugin\n"; + } + exit 0; +} + +=head1 Subroutines + + Begin Subroutine calls to output data / config information + +=head2 fetch_output + + This subroutine is the main call for printing data for the plugin. + No parameters are taken as this is the default call if no arguments + are supplied from the command line. + +=cut + +fetch_output(); + +sub fetch_output { + # Lets figure out what plugin they want to run, and check that it exists + $0 =~ /nagios_multi_(.+)*/; + my $plugin = $1; + die 'Unknown Plugin Specified: ' . ($plugin ? $plugin : '') unless $graphs{$plugin}; + # Lets set up our subgraphs array with all of the graphs which are extensions + # of the root graph / plugin + my @subgraphs; + if ($plugin eq 'services') { + @subgraphs = ('svcchkdetail','svcchksc','svcchklat','svcchkext'); + } elsif ($plugin eq 'hosts') { + @subgraphs = ('hostchkdetail','hostchksc','hostchklat','hostchkext'); + } elsif ($plugin eq 'checks') { + @subgraphs = ('svcchkactcount','hostchkactcount','extcmdcount'); + if ($passive =~ /on/i) { + push(@subgraphs,'svcchkpsvcount'); + push(@subgraphs,'hostchkpsvcount'); + } + } + # Lets just double check the plugin you specified is a root graph / plugin + if (grep $_ eq $plugin, @subgraphs) { + die "Error: $plugin is not a valid root graph, valid graphs are: @subgraphs\n"; + } + # Lets print out the data for our sub graphs / plugins + foreach my $subgraph (@subgraphs) { + print_sub_output($plugin,$subgraph); + } + # Lets print out the data for our main graph / plugin + print_root_output($plugin); + return; +} + +=head2 print_root_output + + This block of code prints out the return values for our root graphs. It takes + one parameter $plugin. Returns when completed + + $plugin; main(root) graph we are calling up to print data values for + + Example: print_root_output($plugin); + +=cut + +sub print_root_output { + # Lets get our plugin, set our graph information, and print for Munin to process + my ($plugin) = (@_); + my $graph = $graphs{$plugin}; + print "multigraph nagios_$plugin\n"; + # Getting keys to pass to nagiostats for data retrieval + # call up fetch_nagios_stats with the keys we just got. + my @keys = @{$graph->{keys}}; + fetch_nagios_stats($plugin,@keys); + # print the results for the keys with the name for Munin to process + foreach my $dsrc (@{$graph->{datasrc}}) { + my $output = 0; + my %datasrc = %$dsrc; + while ( my ($key, $value) = each(%datasrc)) { + next if ($key ne 'name'); + print "$dsrc->{name}.value $graph->{results}->{$value}\n"; + } + } + return; +} + +=head2 print_sub_output + + This block of code prints out the return values for our root graphs. It takes + one parameter $plugin. Returns when completed + + $plugin; main(root) being called, used for multigraph output + $subgraph; graph we are actually trying to print data values for + + Example: print_sub_output($plugin,$subgraph); + +=cut + +sub print_sub_output { + # Lets get our plugin, set our graph information, and print for Munin to process + my ($plugin,$subgraph) = (@_); + my $graph = $graphs{$subgraph}; + print "multigraph nagios_$plugin\.$subgraph\n"; + # Getting keys to pass to nagiostats for data retrieval + # call up fetch_nagios_stats with the keys we just got. + my @keys = @{$graph->{keys}}; + fetch_nagios_stats($subgraph,@keys); + # print the results for the keys with the name for Munin to process + foreach my $dsrc (@{$graph->{datasrc}}) { + my $output = 0; + my %datasrc = %$dsrc; + while ( my ($key, $value) = each(%datasrc)) { + next if ($key ne 'name'); + print "$dsrc->{name}.value $graph->{results}->{$value}\n"; + } + } + return; +} + +=head2 do_config + + This is the main call issued assuming we call up config and plugin specified exists + The subroutine takes one parameter $plugin, and returns when completed. + + $plugin; main(root) graph being called + + Example: do_config($plugin); + +=cut + +sub do_config { + # Lets get our plugin and set subgraphs to undef + my ($plugin) = (@_); + my @subgraphs; + if ($plugin eq 'services') { + # update subgraphs since our plugin is services + @subgraphs = ('svcchkdetail','svcchksc','svcchklat','svcchkext'); + } elsif ($plugin eq 'hosts') { + # update subgraphs since our plugin is hosts + @subgraphs = ('hostchkdetail','hostchksc','hostchklat','hostchkext'); + } elsif ($plugin eq 'checks') { + # update subgraphs since our plugin is checks + @subgraphs = ('svcchkactcount','hostchkactcount','extcmdcount'); + if ($passive =~ /on/i) { + push(@subgraphs,'svcchkpsvcount'); + push(@subgraphs,'hostchkpsvcount'); + } + } + # Now that we know what graphs to reference, lets print out their config info + foreach my $subgraph (@subgraphs) { + print_sub_config($plugin,$subgraph); + } + # Now lets print out the config information for our root graph + print_root_config($plugin); + return; +} + +=head2 print_sub_config + + This subroutine prints out the config information for all of the subgraphs. + It takes two parameters, $plugin and $subgraph + + $plugin; main(root) graph used for multigraph call + $subgraph; subgraph being called up. + + Example: print_sub_config($plugin,$subgraph); + +=cut + +sub print_sub_config { + # Lets get our plugin and subgraph, after that print for Munin to process it. + my ($plugin,$subgraph) = (@_); + my $graph = $graphs{$subgraph}; + print "multigraph nagios_$plugin.$subgraph\n"; + # Lets print our subgraph's main config info. + my %graphconf = %{$graph->{config}}; + while ( my ($key, $value) = each(%graphconf)) { + print "graph_$key $value\n"; + } + # Lets print our subgraph's per graph config info. + foreach my $dsrc (@{$graph->{datasrc}}) { + my %datasrc = %$dsrc; + while ( my ($key, $value) = each(%datasrc)) { + next if ($key eq 'name'); + print "$dsrc->{name}.$key $value\n"; + } + } + return; +} + +=head2 print_root_config + + This subroutine prints out the config information for all of the main(root) graphs. + It takes one parameters, $plugin + + $plugin; main(root) graph used for multigraph call + + Example: print_root_config($plugin); + +=cut + +sub print_root_config { + # Lets get our plugin and graph, after that print for Munin to process it. + my ($plugin) = (@_); + my $graph = $graphs{$plugin}; + print "multigraph nagios_$plugin\n"; + # Lets print out graph's main config info. + my %graphconf = %{$graph->{config}}; + while ( my ($key, $value) = each(%graphconf)) { + print "graph_$key $value\n"; + } + # Lets print our graphs per graph config info. + foreach my $dsrc (@{$graph->{datasrc}}) { + my %datasrc = %$dsrc; + while ( my ($key, $value) = each(%datasrc)) { + next if ($key eq 'name'); + print "$dsrc->{name}.$key $value\n"; + } + } + return; +} + +=head2 fetch_nagios_stats + + This subroutine actually runs the nagiostats binary with the keys specified in an array + Two parameters are passed, $plugin and @keys, and it will return when complete. + + $plugin; graph we are calling up, we use this to store the results in the hash + for easy recall later. + @keys; keys we want the values for from nagiostats binary. + + Example: fetch_nagios_stats($plugin,@keys); + +=cut + +sub fetch_nagios_stats { + # Lets get our current plugin and list of keys we want info for, as well as reference our graph + my ($plugin,@keys) = (@_); + my $graph = $graphs{$plugin}; + # Lets set our command to include our binary plus options, as well as join our array with ,'s + my $command = $binary . " -m -d " . join(",",@keys); + # Lets open the command and pipe it out for easy reading by line + open(CMD, "$command |") or die "Unable to execute command: $command\n"; + # While a return exists from the command, store the value in the key specified + while (my $line = ) { + chomp($line); + my $key = shift(@keys); + $graph->{results}->{$key} = $line; + } + return; +} diff --git a/plugins/other/nagiosstatus b/plugins/nagios/nagiosstatus similarity index 100% rename from plugins/other/nagiosstatus rename to plugins/nagios/nagiosstatus diff --git a/plugins/other/netapp_cifs b/plugins/netapp/netapp_cifs similarity index 100% rename from plugins/other/netapp_cifs rename to plugins/netapp/netapp_cifs diff --git a/plugins/other/netapp_cpu_ b/plugins/netapp/netapp_cpu_ similarity index 100% rename from plugins/other/netapp_cpu_ rename to plugins/netapp/netapp_cpu_ diff --git a/plugins/other/netapp_if_ b/plugins/netapp/netapp_if_ similarity index 100% rename from plugins/other/netapp_if_ rename to plugins/netapp/netapp_if_ diff --git a/plugins/other/cirix-netscaler-connections b/plugins/netscaler/cirix-netscaler-connections similarity index 100% rename from plugins/other/cirix-netscaler-connections rename to plugins/netscaler/cirix-netscaler-connections diff --git a/plugins/other/cirix-netscaler-cpu-usage b/plugins/netscaler/cirix-netscaler-cpu-usage similarity index 100% rename from plugins/other/cirix-netscaler-cpu-usage rename to plugins/netscaler/cirix-netscaler-cpu-usage diff --git a/plugins/other/ag241-adsl b/plugins/network/ag241-adsl similarity index 100% rename from plugins/other/ag241-adsl rename to plugins/network/ag241-adsl diff --git a/plugins/other/arp b/plugins/network/arp similarity index 100% rename from plugins/other/arp rename to plugins/network/arp diff --git a/plugins/other/avm-fritzbox-wan-traffic b/plugins/network/avm-fritzbox-wan-traffic similarity index 100% rename from plugins/other/avm-fritzbox-wan-traffic rename to plugins/network/avm-fritzbox-wan-traffic diff --git a/plugins/network/bandwidth_ b/plugins/network/bandwidth_ new file mode 100755 index 00000000..99a4fc39 --- /dev/null +++ b/plugins/network/bandwidth_ @@ -0,0 +1,284 @@ +#!/usr/bin/perl +# -*- perl -*- + +=head1 NAME + +bandwidth_ - Wildcard-plugin to monitor total network traffic and + predict 30 day bandwidth usage + +=head1 CONFIGURATION + +This is a wildcard plugin. To monitor an interface, link +bandwidth_ to this file. E.g. + + ln -s /usr/share/munin/node/plugins-auto/bandwidth_ \ + /etc/munin/node.d/bandwidth_eth0 + +...will monitor eth0 + +Most likely usage is to monitor an interface connected to your ISP. + +The suggest option will try and determine if you have any interfaces with a +public IP and if so it will suggest monitoring those interfaces. If all +IP addresses are private the setup will have to be done manually. Suggest +does not handle IPv6 addresses. + +=head1 USAGE + +Any device found in /proc/net/dev can be monitored. Examples include ipsec*, +eth*, irda* and lo. + +Please note that aliases cannot be monitored with this plugin. + +=head1 VERSION + +$Id: bandwidth_,v 1.35 2012/01/23 20:04:33 root Exp $ + +=head1 AUTHOR + +Sean Whitney + +=head1 LICENSE + +GPLv2 + +=head1 MAGIC MARKERS + + #%# family=contrib + #%# capabilities=autoconf suggest + +=head1 BUGS + +I know that bandwidth is bits and base10 as opposed to bytes and base2. +However the purpose of this plugin it to monitor your monthly bandwidth +consumption to make sure you don't go over your ISP's peak. ISP's seem +to be interested in expressing peaks in bytes.... + +=cut + +use strict; +use Storable qw(store retrieve); +use Switch; + +my $interface; +my $history; +my $counter_input; +my $counter_output; +my $input; +my $output; +my $uptime; +my $oldest_ts; +my $input_30days; +my $output_30days; +my $perf_ref = {}; +my $count30 = 2592000; # The number of seconds in 30 days +my $unix_ts = time; +my $rollover = 4294967295; + +eval { init(); }; + +sub autoconf { + $0 =~ /bandwidth_(.+)*$/; + $interface = $1; + exit 2 unless defined $interface; + $history = "/var/lib/munin/plugin-state/bandwidth_$interface.state"; +} + +sub bit32or64 { + if ( $input > $rollover || $output > $rollover ) { + $rollover = 18446744073709551615; + } +} + +sub retrieve_history { + return (undef) unless ( -r $history ); + my $store = retrieve($history); + while ( my ( $key, $value ) = each(%$store) ) { + if ( $unix_ts - $key < $count30 ) { + $perf_ref->{$key} = $value; + } + if ( $key =~ /last/ ) { + $perf_ref->{$key} = $value; + } + + } +} + +sub suggest { + + # This only works if one of your interfaces has a public IP, + # Otherwise it will fail and you will have to set it manually + # Multiple public IP addresses can be detected. It won't + # Detect IPv6 addresses. + my $locate = readpipe("locate -b '\\ifconfig'"); + my @ifconfig = readpipe($locate); + my @old; + my $net = "/proc/net/dev"; + my @interfaces; + -f $net || die "Unable to read $net: $!"; + open( DEV, "<", $net ) || die "Unable to read $net: $!"; + + while () { + chomp; + split; + /Inter|face/ and next; + split /:/; + push( @interfaces, $_[0] ); + } + close(DEV); + + foreach (@ifconfig) { + if (/inet addr:([\d.]+)/) { + $1 + =~ /^(127\.\d+|10\.\d+|172\.(1[6-9]|2\d|3[0-1])|192\.168)(\.\d+){2}$/ + and next; + exists $interfaces[ $old[0] ] and print "$old[0]\n"; + } + @old = split; + chomp @old; + } + exit 0; +} + +sub store_history { + + # Store the current values to the new old times + $perf_ref->{$unix_ts} = { + input => $input, + output => $output, + }; + $perf_ref->{last} = { + counter_input => $counter_input, + counter_output => $counter_output, + uptime => $uptime, + }; + store( $perf_ref, $history ) || die "Unable to store $history: $!"; +} + +sub arg { + defined( $ARGV[0] ) or return; + switch ( $ARGV[0] ) { + case 'autoconf' { print "yes\n"; exit 0; } + case 'config' { print_config(); } + case 'suggest' { suggest(); } + } +} + +sub print_config { + print <; + chomp $counter_input; + close($rx); + open(my $tx , "<", "/sys/class/net/$interface/statistics/tx_bytes" ) + || die "Unable to read: $!"; + $counter_output = <$tx>; + chomp $counter_output; + close(DEV); +} + +sub uptime { + my $puptime = "/proc/uptime"; + open( TIME, "<", $puptime ) || die "Unable to read $puptime: $!"; + while (