Projet

Général

Profil

Paste
Télécharger au format
Statistiques
| Branche: | Révision:

root / plugins / sensors / allnet__ @ 8589c6df

Historique | Voir | Annoter | Télécharger (9,9 ko)

1 23f3138c Michael 'PoempelFox' Meier
#!/usr/bin/perl
2
3
# This script is used to feed sensor information from Allnet-Devices like e.g.
4
# Allnet 4000 or 4500 into munin. It uses the XML-Interface of the devices to
5
# fetch data.
6
#
7
# written by Michael Meier <michael.meier@fau.de>, License GPLv2
8
#
9
# Usage:
10
# Put this script onto some server, either on your munin-server or onto some
11
# other server that can reach the actual device. Then symlink to it from the
12
# munin-node plugin dir (usually /etc/munin/plugins). The naming of the symlink
13
# is a critical part of the configuration, it needs to be:
14
#  SOMETHING_HOSTNAME_TYPE,  e.g. allnet_monitordevice42.yourdomain.com_temp
15
# SOMETHING really is just a random name containing no special chars, use
16
# whatever you like.
17
# HOSTNAME is the hostname of the Allnet-Device. This is also the hostname
18
# under which the sensors will show up in munin.
19
# TYPE is the type of sensors you're interested in, valid values are:
20
#                                                   temp, hum, amps
21
# If you want to monitor more than one type of sensor, create more than one
22
# symlink.
23
# The plugin will automatically feed all sensors of the selected type to munin,
24
# under the names that you set in the allnets config. Take care with special
25
# characters, in particular Umlauts and the such - munin will probably not like
26
# those in names.
27
#
28
# Note that the format of the XML output these devices deliver varies vastly
29
# between different firmware versions. This script currently understands 3
30
# completely different outputs, but there is no guarantee that's all the
31
# variations that exist. If the script doesn't understand the output of your
32
# device, set the beextratolerant parameter. You might also want to try a
33
# different firmware version.
34
#
35
# The behaviour of the script can be influenced by environment variables. The
36
# variables are (note this is case sensitive!):
37
#  username  Username for basic HTTP auth (if your device is password protected)
38
#  password  Password for basic HTTP auth (if your device is password protected)
39
#  beextratolerant   This needs to be set to 1 for some extremely old/stripped
40
#                    down devices. Note that this relaxes sanity checks and
41
#                    will lead to nonsense-sensors showing up on devices that
42
#                    do not require this.
43
#  DUMP      set to 1 to enable some debug output (only outside of munin!)
44
#  internalsensorname.warning   The warning-threshold for the sensor reported
45
#                               to munin. You need to replace
46
#                               "internalsensorname" with the munin-internal
47
#                               name of the sensor, usually sensor+somenumber.
48
#                               Can be seen in the webinterface.
49
#  internalsensorname.critical  Same as above, but for the critical-threshold.
50
#
51
# As an example, you could put the following file into /etc/munin/plugin-conf.d:
52
# [allnet_monitordevice42.yourdomain.com_*]
53
#       env.username horst
54
#       env.password topsecret
55
#       env.sensor14.warning 10.0
56
#       env.sensor14.critical 15.0
57
58
# One tuneable:
59
# Timeout for requests.
60
my $timeout = 3; # the LWP default of 180 secs would be way too long
61
62
# -----------------------------------------------------------------------------
63
64 8589c6df klemens
# No need to change those, can be overridden from commandline.
65 23f3138c Michael 'PoempelFox' Meier
my $hostname = 'localhost';
66
my $valtype = 'temp'; # or humidity or amps
67
# These are tried one after another.
68
my @xmlpaths = ('/xml/sensordata.xml', '/xml');
69
70
use LWP::UserAgent;
71
use XML::Simple;
72
73
# AuthAgent is simply LWP::UserAgent with a reimplemented version of the
74
# method basic auth. Theoretically/according to the documentation, the base
75
# implementation should be able to do everything that is needed as well, but
76
# it seems to broken and "the internet" suggests it has been for a long time.
77
{
78
  package AuthAgent;
79
  use base 'LWP::UserAgent';
80
    
81
  sub get_basic_credentials {
82
    if (defined($ENV{'username'}) && defined($ENV{'password'})) {
83
      return $ENV{'username'}, $ENV{'password'};
84
    } else {
85
      return 'admin', 'password';
86
    }
87
  }
88
}
89
90
# Par. 0: URL
91
# Returns: The contents of the website
92
sub geturl($) {
93
  my $url = shift();
94
95
  my $ua = AuthAgent->new();
96
  $ua->agent($0);
97
  $ua->timeout($timeout);
98
  # Create a request
99
  my $req = HTTP::Request->new(GET => $url);
100
  # Pass request to the user agent and get a response back
101
  my $res = $ua->request($req);
102
  # Check the outcome of the response
103
  unless ($res->is_success()) {
104
    #print("ERROR getting data from $url\n");
105
    return undef;
106
  }
107
  return $res->content();
108
}
109
110
if ((@ARGV > 0) && ($ARGV[0] eq "autoconf")) {
111
  print("No\n");
112
  exit(0);
113
}
114
my $progname = $0;
115
if      ($progname =~ m/[-_]temp$/) {
116
  $valtype = 'temp';
117
} elsif ($progname =~ m/[-_]temperature$/) {
118
  $valtype = 'temp';
119
} elsif ($progname =~ m/[-_]hum$/) {
120
  $valtype = 'humidity';
121
} elsif ($progname =~ m/[-_]humidity$/) {
122
  $valtype = 'humidity';
123
} elsif ($progname =~ m/[-_]amps$/) {
124
  $valtype = 'amps';
125
} elsif ($progname =~ m/[-_]ampere$/) {
126
  $valtype = 'amps';
127
}
128
if ($progname =~ m/.+_(.+?)_.+/) {
129
  $hostname = $1;
130
}
131
my $sensorxml;
132
foreach $xp (@xmlpaths) {
133
  $sensorxml = geturl('http://' . $hostname . $xp);
134
  if (defined($sensorxml)) { last; }
135
}
136
unless (defined($sensorxml)) {
137
  print("# Sorry, Failed to fetch data.");
138
  exit(1);
139
}
140
# VERY old firmware versions have HTML crap around the actual XML.
141
$sensorxml =~ s!.*<xml>(.*)</xml>.*!$1!sg;
142
if (defined($ENV{'DUMP'}) && ($ENV{'DUMP'} eq '1')) {
143
  print($sensorxml . "\n");
144
}
145
if ((@ARGV > 0) && ($ARGV[0] eq "config")) {
146
  if ($valtype eq 'humidity') {
147
    print("graph_title Humidity sensors\n");
148
    print("graph_args --lower-limit 0 --upper-limit 100.0\n");
149
    print("graph_vlabel percent\n");
150
  } elsif ($valtype eq 'temp') {
151
    print("graph_title Temperature sensors\n");
152
    print("graph_vlabel degC\n");
153
  } elsif ($valtype eq 'amps') {
154
    print("graph_title Electric current sensors\n");
155
    print("graph_args --lower-limit 0\n");
156
    print("graph_vlabel Ampere\n");
157
  }
158 e00b066f dipohl
  print("graph_category sensors\n");
159 23f3138c Michael 'PoempelFox' Meier
  print("host_name $hostname\n");
160
}
161
my $sensordata = XMLin($sensorxml, KeyAttr => { }, ForceArray => [ 'data' ] );
162
my $beextratolerant = 0;
163
if (defined($ENV{'beextratolerant'})) {
164
  if (($ENV{'beextratolerant'} =~ m/^y/) || ($ENV{'beextratolerant'} eq '1')) {
165
    $beextratolerant = 1;
166
  }
167
}
168
#print($sensordata->[0]);
169
foreach $k (keys($sensordata)) {
170
  if ($k =~ m/^n(\d+)$/) { # Special handling: Could be output from the OLD XML interface.
171
    my $nr = $1;
172
    if (defined($sensordata->{'s'.$nr})
173
     && defined($sensordata->{'t'.$nr})
174
     && defined($sensordata->{'min'.$nr})
175
     && defined($sensordata->{'max'.$nr})) {
176
      # OK, all values available, really is from the old XML interface.
177
      if ($sensordata->{'s'.$nr} eq '0') { next; } # 0 means no sensor.
178
      # OK, lets map the sensortype.
179
      my $st = 'temp';
180
      if ($sensordata->{'s'.$nr} eq '65') { $st = 'humidity'; }
181
      if ($sensordata->{'s'.$nr} eq '101') {
182
        # potential FIXME: 101 is actually "water/contact sensor", but since it
183
        # returns 0.0 and 100.0 as values, we can treat it just like humidity
184
        # for simplicity.
185
        $st = 'humidity';
186
      }
187
      if ($valtype ne $st) { next; } # these aren't the droids you're looking for
188
      if ((@ARGV > 0) && ($ARGV[0] eq "config")) {
189
        print("sensor${nr}.label " . $sensordata->{'n'.$nr} . "\n");
190
        print("sensor${nr}.type GAUGE\n");
191
        if (defined($ENV{"sensor${nr}.warning"})) { printf("sensor%s.warning %s\n", $nr, $ENV{"sensor${nr}.warning"}); }
192
        if (defined($ENV{"sensor${nr}.critical"})) { printf("sensor%s.critical %s\n", $nr, $ENV{"sensor${nr}.critical"}); }
193
      } else {
194
        print("sensor${nr}.value " . $sensordata->{'t'.$nr}. "\n");
195
      }
196
    }
197
    # Fall through - probably wasn't the old XML anyways.
198
  }
199
  my $onesens = $sensordata->{$k};
200
  unless (defined($onesens->{'value_float'})
201
       && defined($onesens->{'name'})
202
       && defined($onesens->{'min_abs_float'})
203
       && defined($onesens->{'max_abs_float'})
204
       && defined($onesens->{'unit'})) {
205
    # Not all values available -> no sane sensor data (or the "system" block in the XML)
206
    unless ($beextratolerant) { next; }
207
    # Maybe yet another firmware version that does not deliver all of these values,
208
    # so lets check for the absolute MINIMUM set.
209
    unless (defined($onesens->{'value_float'}) && defined($onesens->{'name'})) {
210
      next; # Minimum set not available either.
211
    }
212
    if ($onesens->{'value_float'} < -20000.0) { next; } # Invalid value
213
    # OK, so fill in the blanks
214
    unless (defined($onesens->{'unit'})) { $onesens->{'unit'} = 'C'; }
215
    unless (defined($onesens->{'min_abs_float'})) {
216
      $onesens->{'min_abs_float'} = $onesens->{'value_float'} - 0.01;
217
    }
218
    unless (defined($onesens->{'max_abs_float'})) {
219
      $onesens->{'max_abs_float'} = $onesens->{'value_float'} + 0.01;
220
    }
221
  }
222
  if (($onesens->{'min_abs_float'} == 0.0)
223
   && ($onesens->{'max_abs_float'} == 0.0)
224
   && ($onesens->{'value_float'} == 0.0)) {
225
    next; # If the sensor never showed anything but 0.0, we do not care about it.
226
  }
227
  if ($onesens->{'unit'} eq 'A AC') { $onesens->{'unit'} = 'A'; }
228
  if ($onesens->{'unit'} =~ m/C$/) {
229
    $onesens->{'unit'} = 'C'; # Remove WTF8-Fuckup
230
  }
231
  if (($valtype eq 'temp') && ($onesens->{'unit'} ne 'C')) { next; }
232
  if (($valtype eq 'humidity') && ($onesens->{'unit'} ne '%')) { next; }
233
  if (($valtype eq 'amps') && ($onesens->{'unit'} ne 'A')) { next; }
234
  if ((@ARGV > 0) && ($ARGV[0] eq "config")) {
235
    print("${k}.label " . $onesens->{'name'} . "\n");
236
    #print("${k}.info " . $onesens->{'name'} . "\n");
237
    print("${k}.type GAUGE\n");
238
    if (defined($ENV{"${k}.warning"})) { printf("%s.warning %s\n", $k, $ENV{"${k}.warning"}); }
239
    if (defined($ENV{"${k}.critical"})) { printf("%s.critical %s\n", $k, $ENV{"${k}.critical"}); }
240
  } else {
241
    if ($onesens->{'value_float'} < -20000.0) { # Invalid readings return -20480.0
242
      print("${k}.value U\n");
243
    } else {
244
      print("${k}.value " . $onesens->{'value_float'} . "\n");
245
    }
246
  }
247
}