Projet

Général

Profil

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

root / plugins / power / nutups2_ @ 50cb1d89

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

1
#! /usr/bin/perl -w
2

    
3
=head1 NAME
4

    
5
nutups2_ - Plugin to monitor UPSes managed by NUT
6

    
7
=head1 CONFIGURATION
8

    
9
Generally none needed.
10

    
11
If you have installed NUT at a non-standard location, then you can specify its
12
location like:
13

    
14
  [nutups2_*]
15
    env.upsc /some/location/bin/upsc
16

    
17
=head1 WARNING AND CRITICAL SETTINGS
18

    
19
If upsc reports 'high' and 'low' values for some attribute, those will used
20
as the critical range. Otherwise the following environment variables can be
21
used to set the defaults for all fields:
22

    
23
    env.warning
24
    env.critical
25

    
26
You can also control individual fields like:
27

    
28
    env.input_L1.warning
29
    env.output.critical
30

    
31
=head1 MAGIC MARKERS
32

    
33
 #%# family=auto
34
 #%# capabilities=autoconf suggest
35

    
36
=head1 FEATURES
37

    
38
The plugin supports reporting battery charge, UPS load, input/output
39
frequencies/currents/voltages, apparent and real power output, humidity and
40
temperature readings. Note however that different UPS models report different
41
levels of detail; the plugin reports whatever information the NUT UPS driver
42
(and in turn the UPS itself) provides.
43

    
44
Although the 'suggest' command will only offer UPSes for which the local host
45
is the master, you can also monitor remote UPSes if you include the host name
46
in the symlink, like:
47

    
48
	nutups2_<upsname>@<hostname or address>_frequency
49

    
50
etc.
51

    
52
=head1 AUTHOR
53

    
54
Gábor Gombás <gombasg@sztaki.hu>
55

    
56
=head1 LICENSE
57

    
58
GPLv2 or later
59

    
60
=cut
61

    
62
use strict;
63
use Munin::Plugin;
64
use Carp;
65

    
66
my $UPSC = $ENV{'upsc'} || 'upsc';
67

    
68
# For the 'filter' field, the first sub-match should contain the name to
69
# display, and the second sub-match should indicate if it is a nominal
70
# value instead of a sensor reading.
71
my %config = (
72
	charge => {
73
		filter => qr/^(.*)\.(?:charge|load)$/,
74
		title => 'UPS load and battery charge',
75
		args => '--base 1000 -l 0 -u 100',
76
		vlabel => '%',
77
		config => \&common_config,
78
		fetch => \&common_fetch,
79
	},
80
	current => {
81
		filter => qr/^(.*)\.current(\.nominal)?$/,
82
		title => 'UPS current',
83
		args => '--base 1000 -l 0',
84
		vlabel => 'Amper',
85
		config => \&common_config,
86
		fetch => \&common_fetch,
87
	},
88
	frequency => {
89
		filter => qr/^(.*)\.frequency(\.nominal)?$/,
90
		title => 'UPS frequency',
91
		args => '--base 1000 -l 0',
92
		vlabel => 'Hz',
93
		config => \&common_config,
94
		fetch => \&common_fetch,
95
	},
96
	humidity => {
97
		filter => qr/^(.*)\.humidity$/,
98
		title => 'UPS humidity',
99
		args => '--base 1000 -l 0',
100
		vlabel => '%',
101
		config => \&common_config,
102
		fetch => \&common_fetch,
103
	},
104
	power => {
105
		filter => qr/^(.*)\.power(\.nominal)?$/,
106
		title => 'UPS apparent power',
107
		args => '--base 1000 -l 0',
108
		vlabel => 'VA',
109
		config => \&common_config,
110
		fetch => \&common_fetch,
111
	},
112
	realpower => {
113
		filter => qr/^(.*)\.realpower(\.nominal)?$/,
114
		title => 'UPS real power',
115
		args => '--base 1000 -l 0',
116
		vlabel => 'Watt',
117
		config => \&common_config,
118
		fetch => \&common_fetch,
119
	},
120
	temperature => {
121
		filter => qr/^(.*)\.temperature$/,
122
		title => 'UPS temperature',
123
		args => '--base 1000 -l 0',
124
		vlabel => 'Celsius',
125
		config => \&common_config,
126
		fetch => \&common_fetch,
127
	},
128
	voltage => {
129
		filter => qr/^(.*)\.voltage(\.nominal)?$/,
130
		title => 'UPS voltage',
131
		args => '--base 1000 -l 0',
132
		vlabel => 'Volt',
133
		config => \&common_config,
134
		fetch => \&common_fetch,
135
	},
136
	runtime => {
137
		filter => qr/^(.*)\.runtime$/,
138
		title => 'UPS runtime',
139
		args => '--base 1000 -l 0',
140
		vlabel => 'Seconds',
141
		config => \&common_config,
142
		fetch => \&common_fetch,
143
	},
144
);
145

    
146
sub read_ups_values {
147
	my $ups = shift;
148

    
149
	my @lines = `$UPSC $ups 2>/dev/null`;
150
	my $values = {};
151
	for my $line (@lines) {
152
		chomp $line;
153

    
154
		my ($key, $value) = $line =~ m/^([^:]+):\s+(\S.*)$/;
155
		$values->{$key} = $value;
156
	}
157
	return $values;
158
}
159

    
160
sub graph_config {
161
	my ($func, $ups, $values) = @_;
162

    
163
	print "graph_title " . $config{$func}->{'title'} . " ($ups)\n";
164
	print "graph_vlabel " . $config{$func}->{'vlabel'} . "\n";
165
	print "graph_args " . $config{$func}->{'args'} . "\n";
166
	print "graph_category sensors\n";
167

    
168
	my @info;
169
	push @info, 'Manufacturer: "' . $values->{'ups.mfr'} . '"'
170
		if exists $values->{'ups.mfr'} and $values->{'ups.mfr'} ne 'unknown';
171
	push @info, 'Model: "' . $values->{'ups.model'} . '"'
172
		if exists $values->{'ups.model'};
173
	push @info, 'Serial: "' . $values->{'ups.serial'} . '"'
174
		if exists $values->{'ups.serial'};
175
	map { s/\s+/ /g } @info;
176
	print "graph_info " . join(', ', @info) . "\n"
177
		if @info;
178
}
179

    
180
sub print_range_warning {
181
	my ($id, $key, $values) = @_;
182

    
183
	if (exists $values->{$key . '.minimum'}) {
184
		print $id . ".min " . $values->{$key . '.minimum'} . "\n";
185
	}
186
	if (exists $values->{$key . '.maximum'}) {
187
		print $id . ".max " . $values->{$key . '.maximum'} . "\n";
188
	}
189

    
190
	my $range = '';
191
	if (exists $values->{$key . '.high'}) {
192
		$range = $values->{$key . '.high'};
193
	}
194
	if (exists $values->{$key . '.low'}) {
195
		$range = $values->{$key . '.low'} . ':' . $range;
196
	}
197
	# print_thresholds() needs 'undef' for no range
198
	undef $range unless $range;
199
	print_thresholds($id, undef, undef, undef, $range);
200
}
201

    
202
# Example keys for voltages:
203
#	battery.voltage
204
#	battery.voltage.minimum
205
#	battery.voltage.maximum
206
#	battery.voltage.nominal
207
#	input.voltage
208
#	input.voltage.minimum
209
#	input.voltage.maximum
210
#	input.bypass.L1-N.voltage
211
#	input.L1-N.voltage
212
#	output.voltage
213
#	output.voltage.nominal
214
#	output.L1-N.voltage
215
#
216
# Replace 'voltage' with 'current' in the above list to get an example
217
# for current keys.
218
#
219
# Frequency keys:
220
#	input.frequency
221
#	input.frequency.nominal
222
#	input.bypass.frequency
223
#	input.bypass.frequency.nominal
224
#	output.frequency
225
#	output.frequency.nominal
226
#	output.frequency.minimum
227
#	output.frequency.maximum
228
sub common_config {
229
	my ($func, $ups) = @_;
230

    
231
	my $values = read_ups_values($ups);
232
	graph_config($func, $ups, $values);
233
	for my $key (sort keys %$values) {
234
		my ($field, $nominal) = $key =~ $config{$func}->{'filter'};
235
		next unless $field;
236

    
237
		$field .= $nominal if $nominal;
238
		my $id = clean_fieldname($field);
239

    
240
		# These labels look better this way and are still short enough
241
		$field = $key if $func =~ m/(charge|temperature|humidity)/;
242

    
243
		# Beautification
244
		$field = ucfirst($field);
245
		$field =~ s/\./ /g;
246

    
247
		print $id . ".label " . $field . "\n";
248
		print $id . ".type GAUGE\n";
249

    
250
		# Draw nominal values a little thinner
251
		print $id . ".draw LINE1\n" if $nominal;
252

    
253
		print_range_warning($id, $key, $values);
254
	}
255
}
256

    
257
sub common_fetch {
258
	my ($func, $ups) = @_;
259

    
260
	my $values = read_ups_values($ups);
261
	for my $key (sort keys %$values) {
262
		my ($field, $nominal) = $key =~ $config{$func}->{'filter'};
263
		next unless $field;
264

    
265
		$field .= $nominal if $nominal;
266
		my $id = clean_fieldname($field);
267

    
268
		print $id . ".value " . $values->{$key} . "\n";
269
	}
270
}
271

    
272
if ($ARGV[0] and $ARGV[0] eq 'autoconf') {
273
	# The former nutups_ plugin parsed upsmon.conf. But for a large UPS
274
	# that powers dozens or hundreds of machines, that would mean
275
	# monitoring the same UPS from every host it powers, which does not
276
	# make sense. Using upsc and defaulting to localhost means that
277
	# 'autoconf' will only enable the plugin on the UPS master node, where
278
	# upsd is running.
279
	my @upses = `$UPSC -l 2>/dev/null`;
280
	if ($?) {
281
		if ($? == -1) {
282
			print "no (program '$UPSC' was not found)\n";
283
		}
284
		else {
285
			print "no (program '$UPSC -l' returned error)\n";
286
		}
287
		exit 0;
288
	}
289

    
290
	map { chomp $_ } @upses;
291
	unless (@upses and $upses[0]) {
292
		print "no (program '$UPSC' listed no units)\n";
293
	}
294
	else {
295
		print "yes\n";
296
	}
297
	exit 0;
298
}
299

    
300
if ($ARGV[0] and $ARGV[0] eq 'suggest') {
301
	my @upses = `$UPSC -l 2>/dev/null`;
302
	for my $ups (@upses) {
303
		chomp $ups;
304
		for my $metric (keys %config) {
305
			my $values = read_ups_values($ups);
306
			my @keys = grep { +$_ =~ $config{$metric}->{'filter'} } keys(%$values);
307
			print $ups . '_' . $metric . "\n" if @keys;
308
		}
309
	}
310
	exit 0;
311
}
312

    
313
croak("Unknown command line arguments") if $ARGV[0] and $ARGV[0] ne 'config';
314

    
315
# The UPS name may contain underscores
316
my $fn_re = join('|', keys %config);
317
my ($ups, $func) = $0 =~ m/nutups2_(.*)_($fn_re)$/;
318

    
319
if ($ARGV[0] and $ARGV[0] eq 'config') {
320
	$config{$func}->{'config'}($func, $ups);
321
}
322
else {
323
	$config{$func}->{'fetch'}($func, $ups);
324
}
325

    
326
exit 0;