contrib-munin/plugins/power/apcupsd_ww

344 lines
8.8 KiB
Perl
Executable File

#!/usr/bin/perl -w
#
# Plugin to monitor apcupsd via apcaccess
#
# Version 1.3
#
# Copyright (C) 2005-2008 Behan Webster <behanw AT websterwood DOT com>
# Licenced under GPL 2.0
#
# Written by: Behan Webster <behanw AT websterwood DOT com>
# German translation by: Bianco Veigel <bianco.veigel AT zivillian DOT de>
#
#%# family=auto
#%# capabilities=autoconf
use strict;
use warnings;
use vars qw(%attrs %data %num);
my $apcaccess='/sbin/apcaccess';
#$apcaccess='/home/behanw/bin/apcaccess';
my $config='/etc/munin/plugin-conf.d/apcupsd_ww';
my $language = $ENV{LANG} || 'en';
# Example apcaccess output
# KEY : VALUE
#
# UPSNAME : Elfhild
# MODEL : SMART-UPS 1400 RM XL
# STATUS : ONLINE
# LINEV : 123.5 Volts
# LOADPCT : 24.9 Percent Load Capacity
# BCHARGE : 100.0 Percent
# TIMELEFT : 63.0 Minutes
# OUTPUTV : 123.5 Volts
# ITEMP : 39.1 C Internal
# BATTV : 54.5 Volts
# NOMOUTV : 115 Volts
# NOMBATTV : 48.0 Volts
# Possible values to graph in munin
# Only the ones which are available will be graphed
%attrs = (
# APCACCESS_KEY => {
# name => 'attribute_name',
# label => {
# en => 'English title',
# de => 'Titel in Deutsch',
# fr => 'titre en Francais',
# },
# type => 'name_of_functio_for_type', # Default is 'num'
# # Can use one value, or list of values. If the first value can't be used, try the next.
# # KEY Key from apcaccess
# # num,num,num Specify a list of possible nominal values to guess from
# # num Specify fixed nominal value
# nominal => [ 'KEY', '100' ],
# # KEY Key from apcaccess
# # +-num% Calculate percentage min:max from nominal value
# # +-num Calculate min:max from nominal value
# # -num:+num Calculate min:max from nominal value
# # num:num Specify fixed min:max values
# # num or :num Specify fixed max value
# # num: Specify fixed min value
# warning => [ 'KEY:KEY', '+-10%', '-10:+15' ],
# critical => [ 'KEY:KEY', '+-10%', '-10:+15' ],
# },
BCHARGE => { # BCHARGE : 100.0 Percent
name => 'battery',
label => {
en => 'Percent battery charge',
de => 'Batterieladung in Prozent',
},
warning => '33:', # %
critical => '5:', # %
},
LOADPCT => { # LOADPCT : 28.6 Percent Load Capacity
name => 'upsload',
label => {
en => 'Percent load capacity',
de => 'Auslastung in Prozent',
},
warning => '75', # %
critical => '90', # %
},
TIMELEFT => { # TIMELEFT : 17.0 Minutes
name => 'timeleft',
label => {
en => 'Minutes of run time',
de => 'Akkulaufzeit in Minuten',
},
warning => '5:', # mins
critical => [ 'DLOWBATT:', '2:' ], # DLOWBATT : 02 Minutes
},
LINEV => { # LINEV : 121.5 Volts
name => 'linevolts',
label => {
en => 'Line voltage',
de => 'Eingangsspannung',
},
nominal => [ 'NOMINV', 'NOMOUTV', '115,230' ], # NA=115V, Europe=230V
warning => [ '+-10%', '108:128' ],
critical => [ 'LOTRANS:HITRANS', '+-15%', '104:132' ],
},
BATTV => { # BATTV : 27.7 Volts
name => 'batteryvolts',
label => {
en => 'Battery voltage',
de => 'Batteriespannung',
},
nominal => [ 'NOMBATTV', '12,24,48' ], # NOMBATTV : 48.0 Volts
warning => '-5%:+15%',
critical => '-10%:+25%',
},
OUTPUTV => { # OUTPUTV : 122.2 Volts
name => 'outputvolts',
label => {
en => 'Output voltage',
de => 'Ausgangsspannung',
},
nominal => [ 'NOMOUTV', '115,230' ], # NOMOUTV : 115 Volts
warning => [ '+-10%', '108:128' ],
critical => [ 'LOTRANS:HITRANS', '+-15%', '104:132' ],
},
ITEMP => { # ITEMP : 44.1 C Internal
name => 'temperature',
label => {
en => 'UPS temperature',
de => 'USV Temperatur',
},
warning => 50, # C
critical => 60, # C
},
LINEFAIL => { # LINEFAIL : OK
name => 'linefail',
label => {
en => 'Line voltage status',
de => 'Status Eingangsspannung',
},
type => 'bool',
critical => '0:', # Failed
},
BATTSTAT => { # BATTSTAT : OK
name => 'battstat',
label => {
en => 'Battery status',
de => 'Batteriestatus',
},
type => 'bool',
critical => '0:', # Failed
},
MAINS => { # MAINS : OK
name => 'mains',
label => {
en => 'Mains status',
de => 'Status Eingangsspannung',
},
type => 'bool',
critical => '0:', # Failed
},
#STATUS => { # STATUS : ONLINE
# name => 'status',
# label => {
# en => 'Status',
# },
# type => 'status',
# critical => 0,
#},
);
# Read config file
# Can be used to override settings in %attrs
if (-f $config) {
require $config;
}
# Determine plugin capabilities
if (defined $ARGV[0] && $ARGV[0] =~ /autoconf|detect/) {
if (-x $apcaccess) {
print "yes\n";
exit 0;
} else {
print "no (apcaccess not found)\n";
exit 1;
}
}
# Read info from apcupsd using apcaccess
die "$apcaccess: not found\n" unless -x $apcaccess;
open (APCACCESS, "$apcaccess 2>&1 |") || die "$apcaccess: $!\n";
while (<APCACCESS>) {
chomp;
die "$apcaccess: $_\n" if /Error contacting apcupsd/;
$data{$1} = $2 if /^(\S+?)\s*:\s+(.+?)$/;
$num{$1} = $2 if /^(\S+?)\s*:\s+([\d.x]+)/;
}
close APCACCESS;
# Auto-configure plugin
if (defined $ARGV[0] && $ARGV[0] eq 'config') {
if (defined $data{UPSNAME}) {
print "graph_title $data{UPSNAME} ($data{MODEL})\n";
} else {
print "graph_title $data{MODEL}\n";
}
#print "graph_vlabel Units\n";
print "graph_category sensors\n";
print "graph_info This graph shows information about your APC uninterruptible power supply.\n";
foreach my $what (sort keys %attrs) {
&label("$what");
}
# Print current values
} else {
foreach my $what (sort keys %attrs) {
next unless defined $data{$what};
my $func = $attrs{$what}{type} || 'num';
my $value = eval "\&$func('$what')";
print "$attrs{$what}{name}.value $value\n";
}
}
exit 0;
##############################################################################
# Print label/title for value
sub label {
my $what = shift;
return unless defined $data{$what};
my $attr = $attrs{$what};
# Determine language to use for labels
my $lang = $language;
$lang =~ s/_.*$// unless defined $attr->{label}{$lang};
# Failback to english if translation isn't available
$lang = 'en' unless defined $attr->{label}{$lang};
print "$attr->{name}.label $attr->{label}{$lang}\n";
&info($what, 'warning');
&info($what, 'critical');
}
##############################################################################
# Makes a scalar or array into an array (used in &info)
sub list {
return (ref($_[0]) eq 'ARRAY') ? @{$_[0]} : @_;
}
##############################################################################
# Used to setup warning or critical levels for munin
sub info {
my $what = shift;
my $level = shift; # 'warning' or 'critical'
my $attr = $attrs{$what};
return unless defined $attr->{$level};
# Determine nominal value for info calculation
my $nom = undef;
if (defined $attr->{nominal}) {
for my $n (&list($attr->{nominal})) {
# Guess list: compare guesses to value of $num{$what}
if ($n =~ /,/) {
my $fitness = ~0;
next unless $num{$what};
foreach my $possibility (split /[,\s]+/, $n) {
my $diff = abs($num{$what} - $possibility);
($nom, $fitness) = ($possibility, $diff) if $fitness >= $diff;
}
# Absolute nominal value
} elsif ($n =~ /^[\d.]+$/) {
$nom = $n;
last;
# Lookup nominal value as an APCUPSD key
} elsif (defined $num{$n}) {
$nom = $num{$n};
last;
}
}
}
# Calculate info value for $level
foreach my $value (&list($attr->{$level})) {
$value =~ s/([^:]+)/&calc($1,$nom)/eg;
if ($value =~ /^[\d.:]+$/) {
print "$attr->{name}.$level $value\n";
return;
}
}
}
##############################################################################
# Change warning/critical ranges into numbers for munin
sub calc {
my $v = shift;
my $nom = shift;
return $v if $v =~ /^[\d.]+$/;
return $num{$v} if defined $num{$v};
return '' unless defined $nom;
if ($v =~ /^\+-([\d.]+)%$/) {
return sprintf "%.0f:%.0f", (100 - $1) * $nom / 100, (100 + $1) * $nom / 100;
} elsif ($v =~ /^([-+][\d.]+)%$/) {
return sprintf "%.0f", (100 + $1) * $nom / 100;
} elsif ($v =~ /^\+-([\d.]+)$/) {
return sprintf "%d:%d", $nom - $1, $nom + $1;
} elsif ($v =~ /^([-+][\d.]+)$/) {
return $nom + $1;
} elsif ($v =~ /^\*([\d.]+)$/) {
return $nom * $1;
} elsif ($v =~ /^\/([\d.]+)$/) {
return sprintf "%.0f", $nom / $1;
}
return '';
}
##############################################################################
# Default "type" routine to display values
sub num {
my $what = shift;
return $num{$what};
}
##############################################################################
# "type" routine to change Ok/Not Ok into 1/0
sub bool {
my $what = shift;
return $num{$what} eq "OK" ? "1" : "0";
}
#sub status {
# my $what = shift;
# return unless defined $data{$what};
# print "$attrs{$what}{name}.value ";
# print $num{$what} eq "ONLINE" ? "1" : "0";
# print "\n";
#}
# vim: sw=4 ts=4