2
0
mirror of https://github.com/munin-monitoring/contrib.git synced 2018-11-08 00:59:34 +01:00

Merge pull request #61 from ixs/master

New plugins and plugin updates
This commit is contained in:
Steve Schnepp 2012-03-07 03:33:51 -08:00
commit 8f3f79c81c
6 changed files with 738 additions and 10 deletions

View File

@ -1,8 +1,6 @@
#!/bin/sh #!/bin/sh
# #
# $Id: cyrus-imapd 18 2011-07-15 09:14:04Z ixs $ # Copyright (C) 2009 - 2012 Andreas Thienemann <andreas@bawue.net>
#
# Copyright (C) 2009-2011 Andreas Thienemann <andreas@bawue.net>
# #
# This program is free software; you can redistribute it and/or modify # This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU Library General Public License as published by # it under the terms of the GNU Library General Public License as published by
@ -51,7 +49,7 @@ It displays the following three datapoints:
=head1 VERSION =head1 VERSION
$Revision: 18 $ 0.0.20120307
=head1 BUGS =head1 BUGS
@ -71,7 +69,7 @@ GPLv2
CONFIGDIR=$(awk -F : '/^configdirectory:/ { gsub(/ /, "", $2); print $2 }' /etc/imapd.conf 2> /dev/null) CONFIGDIR=$(awk -F : '/^configdirectory:/ { gsub(/ /, "", $2); print $2 }' /etc/imapd.conf 2> /dev/null)
PROCDIR="${CONFIGDIR}/proc" PROCDIR="${CONFIGDIR}/proc"
if [ "$1" = "autoconf" ]; then if [ "$1" == "autoconf" ]; then
if [ "x${CONFIGDIR}x" != "xx" ] && [ -d ${PROCDIR} ]; then if [ "x${CONFIGDIR}x" != "xx" ] && [ -d ${PROCDIR} ]; then
echo yes echo yes
else else
@ -81,14 +79,14 @@ if [ "$1" = "autoconf" ]; then
fi fi
# Check if we actually got some sensible data # Check if we actually got some sensible data
if [ "x${CONFIGDIR}x" = "xx" ]; then if [ "x${CONFIGDIR}x" == "xx" ]; then
exit 1 exit 1
fi fi
# If run with the "config"-parameter, give out information on how the # If run with the "config"-parameter, give out information on how the
# graphs should look. # graphs should look.
if [ "$1" = "config" ]; then if [ "$1" == "config" ]; then
echo 'graph_title Cyrus IMAPd Load' echo 'graph_title Cyrus IMAPd Load'
echo 'graph_args --base 1000 -l 0' echo 'graph_args --base 1000 -l 0'
echo 'graph_vlabel connections' echo 'graph_vlabel connections'

145
plugins/disk/smart_raw__ Executable file
View File

@ -0,0 +1,145 @@
#!/usr/bin/python
#
# Copyright (C) 2011 Andreas Thienemann <andreas@bawue.net>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
"""
=head1 NAME
smart_raw__ - Munin plugin to retreive raw SMART values from a disk.
=head1 APPLICABLE SYSTEMS
All machines with a SMART capable disk and smartmontools installed.
This plugin is very useful if you want to need to monitor a single raw S.M.A.R.T.
value reported by a disk. Load Cycle Counts or Pending Sectors come to mind as
these are a good indicator of problems with a disk.
=head1 CONFIGURATION
The plugin should be installed as smart_raw_sda_193 which means that the smart value
numbered 193 will be read from the disk sda.
Basic configuration for every system is that the plugin needs to be called as root
in order to execute smartmontools.
Add the following to your /etc/munin/plugin-conf.d/smart_raw:
[smart_raw_sda_193]
user root
=head1 INTERPRETATION
Smart RAW values are provided as is. You need to undertand what you are monitoring...
=head1 MAGIC MARKERS
#%# family=contrib
#%# capabilities=
=head1 VERSION
0.0.1
=head1 BUGS
None known.
=head1 AUTHOR
Andreas Thienemann <andreas@bawue.net>
=head1 LICENSE
GPLv3+
=cut
"""
import subprocess
import sys
import os
import re
import pprint
# We are a wildcard plugin, figure out what we are being called for
try:
disk = sys.argv[0].split('_')[2]
value = sys.argv[0].split('_')[3]
except IndexError:
sys.exit(1)
def normalize_name(name):
name = re.sub("[^a-z0-9A-Z]","_", name)
return name
# Code sniplet from Philipp Keller
# http://code.pui.ch/2007/02/19/set-timeout-for-a-shell-command-in-python/
def timeout_command(command, timeout):
"""call shell-command and either return its output or kill it
if it doesn't normally exit within timeout seconds and return None"""
import subprocess, datetime, os, time, signal
start = datetime.datetime.now()
process = subprocess.Popen(command.split(), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
while process.poll() is None:
time.sleep(0.1)
now = datetime.datetime.now()
if (now - start).seconds> timeout:
os.kill(process.pid, signal.SIGKILL)
os.waitpid(-1, os.WNOHANG)
return None
return process.stdout.read()
def read_smart():
"""Read SMART attributes"""
out = timeout_command("/usr/sbin/smartctl -A -d ata /dev/%s" % (disk), 2)
smart_attribs = dict()
for line in out.split('\n')[7:-2]: # Cut away the header and the footer
line = line.split()
smart_attribs[line[0]] = {
'name' : line[1],
'label' : normalize_name(line[1]),
'raw' : line [-1],
}
return smart_attribs
def print_config():
"""Return configuration arguments for munin"""
attribs = read_smart()
print "graph_title S.M.A.R.T raw value %s for drive %s" % (attribs[value]['name'], disk)
print "graph_vlabel Raw value"
print "graph_info RAW smartctl value"
print "graph_category disk"
print "graph_args --base 1000 -l 0"
print "%s.label %s" % (value, attribs[value]['label'])
sys.exit(0)
def fetch():
attribs = read_smart()
print "%s.value %s" % (value, attribs[value]['raw'])
sys.exit(0)
if "config" in sys.argv[1:]:
print_config()
elif "autoconf" in sys.argv[1:]:
pass
elif "suggest" in sys.argv[1:]:
pass
else:
fetch()

View File

@ -1,6 +1,6 @@
#!/usr/bin/python #!/usr/bin/python
# #
# Copyright (C) 2011 Andreas Thienemann <andreas@bawue.net> # Copyright (C) 2011,2012 Andreas Thienemann <andreas@bawue.net>
# #
# This program is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -40,11 +40,13 @@ likely no bmc to query.
In certain cases however bmc-info will just seem to hang for quite some time. In certain cases however bmc-info will just seem to hang for quite some time.
In this case, autodetection does not work because the smbios table has In this case, autodetection does not work because the smbios table has
incorrect information. One system know to experience this problem is the incorrect information. One system known to experience this problem is the
HP Proliant Microserver. HP Proliant Microserver.
Adding env.freeipmi_args "--no-probing --driver-type=KCS --driver-address=0xca2 --register-spacing=1" Adding env.freeipmi_args "--no-probing --driver-type=KCS --driver-address=0xca2 --register-spacing=1"
to the munin plugin configuration will make the plugin work. to the munin plugin configuration will make the plugin work. This is the
specific line for the HP Proliant Microserver mentioned above. Your mileage
may vary.
Basic configuration for every system is that the plugin needs to be called as root. Basic configuration for every system is that the plugin needs to be called as root.

292
plugins/sensors/snmp__areca_ Executable file
View File

@ -0,0 +1,292 @@
#!/usr/bin/python
# Copyright (C) 2009 - 2012 Andreas Thienemann <andreas@bawue.net>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU Library General Public License as published by
# the Free Software Foundation; version 2 only
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Library General Public License for more details.
#
# You should have received a copy of the GNU Library General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
"""
=head1 NAME
snmp__areca_ - Munin plugin to get temperature, voltage or fan speed values
from Areca network enabled RAID controllers via SNMP.
=head1 APPLICABLE SYSTEMS
All machines with a SNMP capable ARECA raid controller.
=head1 CONFIGURATION
Make sure your Areca controller is accessible via SNMP from the munin host:
snmpwalk -v 1 -c snmp_community ip.add.re.ss
The plugin is a wildcard plugin and can thus be used to retrieve different
values depending on the name of the file.
Linking it as snmp_10.8.1.230_areca_fan would retrieve the fan speed values from
the Areca controller at 10.8.1.230.
Valid values are fan, temp and volt.
Add the following to your /etc/munin/plugin-conf.d/snmp__areca file:
[snmp_ip.add.re.ss_*]
community private
version 1
Then test the plugin by calling the following commands:
munin-run snmp_10.8.1.230_areca_temp config
munin-run snmp_10.8.1.230_areca_temp
Both commands should output sensible data without failing.
=head1 INTERPRETATION
The plugin shows the temperature in Celsius or the fanspeed in rotations per minute.
=head1 MAGIC MARKERS
#%# family=contrib
#%# capabilities=
=head1 VERSION
0.0.1
=head1 BUGS
None known. If you know of any, please raise a ticket at https://trac.bawue.org/munin/wiki/areca__snmp_
=head1 AUTHOR
Andreas Thienemann <andreas@bawue.net>
=head1 LICENSE
GPLv2
=cut
"""
import pprint
import time
import sys
import re
import os
from pysnmp import v1, v2c, role, asn1
request_conf = {
"volt" : {
"label" : "Voltages",
"vlabel" : "Volt",
"graph" : "--base 1000 --logarithmic",
"oid" : ".1.3.6.1.4.1.18928.1.2.2.1.8"
},
"fan" : {
"label" : "Fans",
"vlabel" : "RPM",
"graph" : "--base 1000 -l 0",
"oid" : ".1.3.6.1.4.1.18928.1.2.2.1.9"
},
"temp" : {
"label" : "Temperatures",
"vlabel" : "Celsius",
"graph" : "--base 1000 -l 0",
"oid" : ".1.3.6.1.4.1.18928.1.2.2.1.10"
}
}
# Sanity check and parsing of the commandline
host = None
port = 161
request = None
try:
match = re.search("^(?:|.*\/)snmp_([^_]+)_areca_(.+)$", sys.argv[0])
host = match.group(1)
request = match.group(2)
match = re.search("^([^:]+):(\d+)$", host)
if match is not None:
host = match.group(1)
port = match.group(2)
except:
pass
if host is None or request is None:
print "# Error: Incorrect filename. Cannot parse host or request."
sys.exit(1)
# Parse env variables
if os.getenv("community") is not None:
community = os.getenv("community")
else:
community = "public"
if os.getenv("version") is not None:
version = os.getenv("version")
else:
version = "1"
def get_data():
# Fetch the data
results = snmpwalk(request_conf[request]["oid"])
# parse data
vals = []
for i in range(0, len(results)):
idx, res = results[i][0].split(request_conf[request]["oid"])[1].split(".")[1:], results[i][1]
if idx[1] == '1':
vals.append([])
vals[int(idx[2])-1].append(res)
if idx[1] == '2':
vals[int(idx[2])-1].append(res)
if idx[1] == '3':
if request == "volt":
res = float(res)/1000
vals[int(idx[2])-1].append(res)
return vals
def snmpwalk(root):
# Create SNMP manager object
client = role.manager((host, port))
# Create a SNMP request&response objects from protocol version
# specific module.
try:
req = eval('v' + version).GETREQUEST()
nextReq = eval('v' + version).GETNEXTREQUEST()
rsp = eval('v' + version).GETRESPONSE()
except (NameError, AttributeError):
print '# Unsupported SNMP protocol version: %s\n%s' % (version, usage)
sys.exit(-1)
# Store tables headers
head_oids = [root]
encoded_oids = map(asn1.OBJECTID().encode, head_oids)
result = [];
while 1:
# Encode OIDs, encode SNMP request message and try to send
# it to SNMP agent and receive a response
(answer, src) = client.send_and_receive(req.encode(community=community, encoded_oids=encoded_oids))
# Decode SNMP response
rsp.decode(answer)
# Make sure response matches request (request IDs, communities, etc)
if req != rsp:
raise 'Unmatched response: %s vs %s' % (str(req), str(rsp))
# Decode BER encoded Object IDs.
oids = map(lambda x: x[0], map(asn1.OBJECTID().decode, rsp['encoded_oids']))
# Decode BER encoded values associated with Object IDs.
vals = map(lambda x: x[0](), map(asn1.decode, rsp['encoded_vals']))
# Check for remote SNMP agent failure
if rsp['error_status']:
# SNMP agent reports 'no such name' when walk is over
if rsp['error_status'] == 2:
# Switch over to GETNEXT req on error
# XXX what if one of multiple vars fails?
if not (req is nextReq):
req = nextReq
continue
# One of the tables exceeded
for l in oids, vals, head_oids:
del l[rsp['error_index']-1]
else:
raise 'SNMP error #' + str(rsp['error_status']) + ' for OID #' + str(rsp['error_index'])
# Exclude completed OIDs
while 1:
for idx in range(len(head_oids)):
if not asn1.OBJECTID(head_oids[idx]).isaprefix(oids[idx]):
# One of the tables exceeded
for l in oids, vals, head_oids:
del l[idx]
break
else:
break
if not head_oids:
return result
# Print out results
for (oid, val) in map(None, oids, vals):
result.append((oid, str(val)))
# print oid + ' ---> ' + str(val)
# BER encode next SNMP Object IDs to query
encoded_oids = map(asn1.OBJECTID().encode, oids)
# Update request object
req['request_id'] = req['request_id'] + 1
# Switch over GETNEXT PDU for if not done
if not (req is nextReq):
req = nextReq
raise "error"
def print_config():
print "graph_title " + request_conf[request]["label"]
print "graph_vlabel " + request_conf[request]["vlabel"]
print "graph_args " + request_conf[request]["graph"]
print "graph_category sensors"
print "host_name", host
for dataset in get_data():
if request == "volt":
if dataset[1] == "Battery Status":
continue
else:
print request + dataset[0] + ".label", dataset[1]
ref_val = float(dataset[1].split()[-1][:-1])
print request + dataset[0] + ".warning", str(ref_val * 0.95) + ":" + str(ref_val * 1.05)
print request + dataset[0] + ".critical", str(ref_val * 0.80) + ":" + str(ref_val * 1.20)
if request == "temp":
print request + dataset[0] + ".label", dataset[1]
if dataset[1].startswith("CPU"):
print request + dataset[0] + ".warning", 55
print request + dataset[0] + ".critical", 60
if dataset[1].startswith("System"):
print request + dataset[0] + ".warning", 40
print request + dataset[0] + ".critical", 45
if request == "fan":
print request + dataset[0] + ".label", dataset[1]
print request + dataset[0] + ".warning", 2400
print request + dataset[0] + ".critical", 2000
sys.exit(0)
if "config" in sys.argv[1:]:
print_config()
elif "snmpconf" in sys.argv[1:]:
print "require 1.3.6.1.4.1.18928.1.2.2.1.8.1.1"
sys.exit(0)
else:
for dataset in get_data():
# Filter Battery Status (255 == Not installed)
if request == "volt" and dataset[1] == "Battery Status":
continue
print request + dataset[0] + ".value", dataset[2]
sys.exit(0)

104
plugins/sensors/snmp__thecus_fans Executable file
View File

@ -0,0 +1,104 @@
#!/usr/bin/perl -w
# -*- cperl -*-
=head1 NAME
snmp__thecus_fans - Munin plugin to retrive fanspeed readings from a Thecus
NAS device running SNMP.
=head1 APPLICABLE SYSTEMS
All Thecus NAS devices which have the third party NETSNMPD module installed.
This is available at http://www.fajo.de/main/thecus/modules/netsnmpd
=head1 CONFIGURATION
As a rule SNMP plugins need site specific configuration. The default
configuration (shown here) will only work on insecure sites/devices.
[snmp_*]
env.version 2
env.community public
In general SNMP is not very secure at all unless you use SNMP version
3 which supports authentication and privacy (encryption). But in any
case the community string for your devices should not be "public".
Please see 'perldoc Munin::Plugin::SNMP' for further configuration
information.
=head1 INTERPRETATION
The plugin reports the current fan speed readings as reported by the thecusIO
module.
=head1 MIB INFORMATION
Private MIB
=head1 MAGIC MARKERS
#%# family=snmpauto
#%# capabilities=snmpconf
=head1 VERSION
0.0.20120307
=head1 BUGS
None known.
=head1 AUTHOR
Copyright (C) 2011 - 2012 Andreas Thienemann <andreas@bawue.net>
Based on the snmp__uptime plugin as a template.
=head1 LICENSE
GPLv2 or (at your option) any later version.
=cut
use strict;
use Munin::Plugin::SNMP;
use vars qw($DEBUG);
$DEBUG = $ENV{'MUNIN_DEBUG'};
my $response;
if (defined $ARGV[0] and $ARGV[0] eq "snmpconf") {
print "index 1.3.6.1.4.1.14822.101.21.\n";
print "require 1.3.6.1.4.1.14822.101.21.5200.1.1.0 [0-9]\n";
print "require 1.3.6.1.4.1.14822.101.21.5200.1.2.0 [0-9]\n";
exit 0;
}
if (defined $ARGV[0] and $ARGV[0] eq "config") {
my ($host) = Munin::Plugin::SNMP->config_session();
print "host_name $host\n" unless $host eq 'localhost';
print "graph_title Thecus Fans
graph_args --base 1000 -l 0
graph_vlabel RPM
graph_info This graph shows the RPMs of the fans as reported by the ThecusIO module.
graph_category sensors
fan1.label Fan 1
fan1.info Thecus CPU Fan
fan2.label Fan 2
fan2.info Thecus System Fan
";
exit 0;
}
my $session = Munin::Plugin::SNMP->session(-translate =>
[ -timeticks => 0x0 ]);
my $fan1 = $session->get_single ("1.3.6.1.4.1.14822.101.21.5200.1.1.0") || 'U';
my $fan2 = $session->get_single ("1.3.6.1.4.1.14822.101.21.5200.1.2.0") || 'U';
#print "Retrived uptime is '$uptime'\n" if $DEBUG;
print "fan1.value ", $fan1, "\n";
print "fan2.value ", $fan2, "\n";

187
tools/nagios/check_munin Normal file
View File

@ -0,0 +1,187 @@
#!/usr/bin/python
#
# Copyright (C) 2009 Andreas Thienemann <andreas@bawue.net>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU Library General Public License as published by
# the Free Software Foundation; version 2 only
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Library General Public License for more details.
#
# You should have received a copy of the GNU Library General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
#
# Nagios script to query a munin host for plugin values
#
# Can be used as an active check instead of check_dummy
#
import optparse
import socket
import pprint
import sys
import re
parser = optparse.OptionParser("usage: %prog -H <Host> -M <Module> [-P <Port>] -D [<warn>] [<crit>]")
parser.add_option("-H", "--host", dest="host", type="string",
help="specify host to poll")
parser.add_option("-M", "--module", dest="module", type="string",
help="munin module to poll")
parser.add_option("-P", "--port", dest="port", default=4949,
type="int", help="port number to poll")
parser.add_option("-D", "--debug", action="store_true", dest="debug", default=False,
help="Debug output")
(options, args) = parser.parse_args()
HOST = options.host
PORT = options.port
MODULE = options.module
DEBUG = options.debug
if HOST == None or MODULE == None:
parser.error("options -H and -M are required.")
def compare(val, thresh):
# Compare value to warning and critical threshoulds
# Handle different threshold formats: max, :max, min:, min:max
val = float(val)
# max
match = re.match("^[:]?([-+]?[0-9]+)$", str(thresh))
if match:
max = float(match.group(1))
if val > max:
return 3
# min
match = re.match("^([-+]?[0-9]+):$", str(thresh))
if match:
min = float(match.group(1))
if val < min:
return 2
# min:max
match = re.match("^([-+]?[0-9]+):([-+]?[0-9]+)$", str(thresh))
if match:
min, max = float(match.group(1)), float(match.group(2))
if val < min or val > max:
return 1
# okay
return 0
def output(l, cat, desc, ret):
if len(l[cat]) > 0:
print MODULE, desc + ";"
for line in l["critical"]:
print "CRITICAL: " + line + ";"
for line in l["warning"]:
print "WARNING: " + line + ";"
for line in l["ok"]:
print "OK: " + line + ";"
sys.exit(ret)
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
conn = s.makefile('wb', 0)
except:
print "Couldn't connect to requested host"
sys.exit(3)
if conn.readline().startswith("# munin node at"):
conn.writelines("config" + MODULE + "\n")
order = []
data = {}
while True:
line = conn.readline()
if DEBUG:
pprint.pprint(line)
# Last message, bail
if line == ".\n":
break
label = ""
key, val = line.split(" ", 1)
if key.find(".") is not -1:
label = key.split(".")[0]
if label not in data:
data[label] = { "warning" : "", "critical" : "", "value" : "" }
order.append(label)
# No thresholds passed on the command line
if len(args) == 2:
data[label]["warning"] = args[0]
data[label]["critical"] = args[1]
# No thresholds passed on the command line, take the munin supplied ones
if len(args) < 2:
if key.endswith("warning"):
data[label]["warning"] = val[:-1]
if key.endswith("critical"):
data[label]["critical"] = val[:-1]
if data[label]["warning"] == "" or data[label]["critical"] == "":
print "UNKNOWN - Couldn't retrieve thresholds, pass some on the command line"
sys.exit(3)
conn.writelines("fetch " + MODULE + "\n")
while True:
line = conn.readline()
# Last line, bail
if line == ".\n":
if DEBUG:
pprint.pprint(data)
break
key, val = line.split(" ", 1)
label = key.split(".")[0]
if key.endswith("value"):
data[label]["value"] = val[:-1]
conn.writelines("quit\n")
else:
print "UNKNOWN - No munin node detected"
sys.exit(3)
conn.close()
s.close()
l = { "ok" : [], "warning" : [], "critical" : [] }
for entry in order:
# compare actual data: 3 max exceeded, 2 minimum underrun, 1 outside limit, 0 okay
for tresh in ["critical", "warning"]:
val = data[entry]["value"]
tval = data[entry][tresh]
tmp = ""
if compare(val, tval) == 3:
tmp = entry + ": " + val + " has exceeded the maximum threshold of " + tval
break
elif compare(val, tval) == 2:
tmp = entry + ": " + val + " has underrun the minimum threshold of " + tval
break
elif compare(val, tval) == 1:
tmp = entry + ": " + val + " is outside of range " + tval
break
if tmp != "":
l[tresh].append(tmp)
else:
l["ok"].append(entry + ": " + val + " is okay")
output(l, "critical", "CRITICAL", 2)
output(l, "warning", "WARNING", 1)
output(l, "ok", "OK", 0)