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 | } |
