From 23f3138c8ee249ceee09c711d27718c8826352a7 Mon Sep 17 00:00:00 2001 From: Michael 'PoempelFox' Meier Date: Wed, 4 Mar 2015 15:58:48 +0100 Subject: [PATCH] new plugin for monitoring allnet ip sensoric devices (e.g. ALL4500) --- plugins/sensors/allnet__ | 247 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 247 insertions(+) create mode 100755 plugins/sensors/allnet__ diff --git a/plugins/sensors/allnet__ b/plugins/sensors/allnet__ new file mode 100755 index 00000000..6a25749b --- /dev/null +++ b/plugins/sensors/allnet__ @@ -0,0 +1,247 @@ +#!/usr/bin/perl + +# This script is used to feed sensor information from Allnet-Devices like e.g. +# Allnet 4000 or 4500 into munin. It uses the XML-Interface of the devices to +# fetch data. +# +# written by Michael Meier , License GPLv2 +# +# Usage: +# Put this script onto some server, either on your munin-server or onto some +# other server that can reach the actual device. Then symlink to it from the +# munin-node plugin dir (usually /etc/munin/plugins). The naming of the symlink +# is a critical part of the configuration, it needs to be: +# SOMETHING_HOSTNAME_TYPE, e.g. allnet_monitordevice42.yourdomain.com_temp +# SOMETHING really is just a random name containing no special chars, use +# whatever you like. +# HOSTNAME is the hostname of the Allnet-Device. This is also the hostname +# under which the sensors will show up in munin. +# TYPE is the type of sensors you're interested in, valid values are: +# temp, hum, amps +# If you want to monitor more than one type of sensor, create more than one +# symlink. +# The plugin will automatically feed all sensors of the selected type to munin, +# under the names that you set in the allnets config. Take care with special +# characters, in particular Umlauts and the such - munin will probably not like +# those in names. +# +# Note that the format of the XML output these devices deliver varies vastly +# between different firmware versions. This script currently understands 3 +# completely different outputs, but there is no guarantee that's all the +# variations that exist. If the script doesn't understand the output of your +# device, set the beextratolerant parameter. You might also want to try a +# different firmware version. +# +# The behaviour of the script can be influenced by environment variables. The +# variables are (note this is case sensitive!): +# username Username for basic HTTP auth (if your device is password protected) +# password Password for basic HTTP auth (if your device is password protected) +# beextratolerant This needs to be set to 1 for some extremely old/stripped +# down devices. Note that this relaxes sanity checks and +# will lead to nonsense-sensors showing up on devices that +# do not require this. +# DUMP set to 1 to enable some debug output (only outside of munin!) +# internalsensorname.warning The warning-threshold for the sensor reported +# to munin. You need to replace +# "internalsensorname" with the munin-internal +# name of the sensor, usually sensor+somenumber. +# Can be seen in the webinterface. +# internalsensorname.critical Same as above, but for the critical-threshold. +# +# As an example, you could put the following file into /etc/munin/plugin-conf.d: +# [allnet_monitordevice42.yourdomain.com_*] +# env.username horst +# env.password topsecret +# env.sensor14.warning 10.0 +# env.sensor14.critical 15.0 + +# One tuneable: +# Timeout for requests. +my $timeout = 3; # the LWP default of 180 secs would be way too long + +# ----------------------------------------------------------------------------- + +# No need to change those, can be overriden from commandline. +my $hostname = 'localhost'; +my $valtype = 'temp'; # or humidity or amps +# These are tried one after another. +my @xmlpaths = ('/xml/sensordata.xml', '/xml'); + +use LWP::UserAgent; +use XML::Simple; + +# AuthAgent is simply LWP::UserAgent with a reimplemented version of the +# method basic auth. Theoretically/according to the documentation, the base +# implementation should be able to do everything that is needed as well, but +# it seems to broken and "the internet" suggests it has been for a long time. +{ + package AuthAgent; + use base 'LWP::UserAgent'; + + sub get_basic_credentials { + if (defined($ENV{'username'}) && defined($ENV{'password'})) { + return $ENV{'username'}, $ENV{'password'}; + } else { + return 'admin', 'password'; + } + } +} + +# Par. 0: URL +# Returns: The contents of the website +sub geturl($) { + my $url = shift(); + + my $ua = AuthAgent->new(); + $ua->agent($0); + $ua->timeout($timeout); + # Create a request + my $req = HTTP::Request->new(GET => $url); + # Pass request to the user agent and get a response back + my $res = $ua->request($req); + # Check the outcome of the response + unless ($res->is_success()) { + #print("ERROR getting data from $url\n"); + return undef; + } + return $res->content(); +} + +if ((@ARGV > 0) && ($ARGV[0] eq "autoconf")) { + print("No\n"); + exit(0); +} +my $progname = $0; +if ($progname =~ m/[-_]temp$/) { + $valtype = 'temp'; +} elsif ($progname =~ m/[-_]temperature$/) { + $valtype = 'temp'; +} elsif ($progname =~ m/[-_]hum$/) { + $valtype = 'humidity'; +} elsif ($progname =~ m/[-_]humidity$/) { + $valtype = 'humidity'; +} elsif ($progname =~ m/[-_]amps$/) { + $valtype = 'amps'; +} elsif ($progname =~ m/[-_]ampere$/) { + $valtype = 'amps'; +} +if ($progname =~ m/.+_(.+?)_.+/) { + $hostname = $1; +} +my $sensorxml; +foreach $xp (@xmlpaths) { + $sensorxml = geturl('http://' . $hostname . $xp); + if (defined($sensorxml)) { last; } +} +unless (defined($sensorxml)) { + print("# Sorry, Failed to fetch data."); + exit(1); +} +# VERY old firmware versions have HTML crap around the actual XML. +$sensorxml =~ s!.*(.*).*!$1!sg; +if (defined($ENV{'DUMP'}) && ($ENV{'DUMP'} eq '1')) { + print($sensorxml . "\n"); +} +if ((@ARGV > 0) && ($ARGV[0] eq "config")) { + if ($valtype eq 'humidity') { + print("graph_title Humidity sensors\n"); + print("graph_args --lower-limit 0 --upper-limit 100.0\n"); + print("graph_vlabel percent\n"); + } elsif ($valtype eq 'temp') { + print("graph_title Temperature sensors\n"); + print("graph_vlabel degC\n"); + } elsif ($valtype eq 'amps') { + print("graph_title Electric current sensors\n"); + print("graph_args --lower-limit 0\n"); + print("graph_vlabel Ampere\n"); + } + print("graph_category envsensors\n"); + print("host_name $hostname\n"); +} +my $sensordata = XMLin($sensorxml, KeyAttr => { }, ForceArray => [ 'data' ] ); +my $beextratolerant = 0; +if (defined($ENV{'beextratolerant'})) { + if (($ENV{'beextratolerant'} =~ m/^y/) || ($ENV{'beextratolerant'} eq '1')) { + $beextratolerant = 1; + } +} +#print($sensordata->[0]); +foreach $k (keys($sensordata)) { + if ($k =~ m/^n(\d+)$/) { # Special handling: Could be output from the OLD XML interface. + my $nr = $1; + if (defined($sensordata->{'s'.$nr}) + && defined($sensordata->{'t'.$nr}) + && defined($sensordata->{'min'.$nr}) + && defined($sensordata->{'max'.$nr})) { + # OK, all values available, really is from the old XML interface. + if ($sensordata->{'s'.$nr} eq '0') { next; } # 0 means no sensor. + # OK, lets map the sensortype. + my $st = 'temp'; + if ($sensordata->{'s'.$nr} eq '65') { $st = 'humidity'; } + if ($sensordata->{'s'.$nr} eq '101') { + # potential FIXME: 101 is actually "water/contact sensor", but since it + # returns 0.0 and 100.0 as values, we can treat it just like humidity + # for simplicity. + $st = 'humidity'; + } + if ($valtype ne $st) { next; } # these aren't the droids you're looking for + if ((@ARGV > 0) && ($ARGV[0] eq "config")) { + print("sensor${nr}.label " . $sensordata->{'n'.$nr} . "\n"); + print("sensor${nr}.type GAUGE\n"); + if (defined($ENV{"sensor${nr}.warning"})) { printf("sensor%s.warning %s\n", $nr, $ENV{"sensor${nr}.warning"}); } + if (defined($ENV{"sensor${nr}.critical"})) { printf("sensor%s.critical %s\n", $nr, $ENV{"sensor${nr}.critical"}); } + } else { + print("sensor${nr}.value " . $sensordata->{'t'.$nr}. "\n"); + } + } + # Fall through - probably wasn't the old XML anyways. + } + my $onesens = $sensordata->{$k}; + unless (defined($onesens->{'value_float'}) + && defined($onesens->{'name'}) + && defined($onesens->{'min_abs_float'}) + && defined($onesens->{'max_abs_float'}) + && defined($onesens->{'unit'})) { + # Not all values available -> no sane sensor data (or the "system" block in the XML) + unless ($beextratolerant) { next; } + # Maybe yet another firmware version that does not deliver all of these values, + # so lets check for the absolute MINIMUM set. + unless (defined($onesens->{'value_float'}) && defined($onesens->{'name'})) { + next; # Minimum set not available either. + } + if ($onesens->{'value_float'} < -20000.0) { next; } # Invalid value + # OK, so fill in the blanks + unless (defined($onesens->{'unit'})) { $onesens->{'unit'} = 'C'; } + unless (defined($onesens->{'min_abs_float'})) { + $onesens->{'min_abs_float'} = $onesens->{'value_float'} - 0.01; + } + unless (defined($onesens->{'max_abs_float'})) { + $onesens->{'max_abs_float'} = $onesens->{'value_float'} + 0.01; + } + } + if (($onesens->{'min_abs_float'} == 0.0) + && ($onesens->{'max_abs_float'} == 0.0) + && ($onesens->{'value_float'} == 0.0)) { + next; # If the sensor never showed anything but 0.0, we do not care about it. + } + if ($onesens->{'unit'} eq 'A AC') { $onesens->{'unit'} = 'A'; } + if ($onesens->{'unit'} =~ m/C$/) { + $onesens->{'unit'} = 'C'; # Remove WTF8-Fuckup + } + if (($valtype eq 'temp') && ($onesens->{'unit'} ne 'C')) { next; } + if (($valtype eq 'humidity') && ($onesens->{'unit'} ne '%')) { next; } + if (($valtype eq 'amps') && ($onesens->{'unit'} ne 'A')) { next; } + if ((@ARGV > 0) && ($ARGV[0] eq "config")) { + print("${k}.label " . $onesens->{'name'} . "\n"); + #print("${k}.info " . $onesens->{'name'} . "\n"); + print("${k}.type GAUGE\n"); + if (defined($ENV{"${k}.warning"})) { printf("%s.warning %s\n", $k, $ENV{"${k}.warning"}); } + if (defined($ENV{"${k}.critical"})) { printf("%s.critical %s\n", $k, $ENV{"${k}.critical"}); } + } else { + if ($onesens->{'value_float'} < -20000.0) { # Invalid readings return -20480.0 + print("${k}.value U\n"); + } else { + print("${k}.value " . $onesens->{'value_float'} . "\n"); + } + } +}