current value
[upsgraph] / upsgraph.pl
1 #!/usr/bin/perl -w
2
3 if ((@ARGV != 1) && (@ARGV != 2)) {
4 print STDERR "Syntax: ${0} configfile [uid]\n";
5 exit(1);
6 }
7
8 use Net::SNMP;
9 use IO::Socket::INET;
10 use RRDs;
11 use File::Copy;
12 use Data::Dumper;
13
14 $UPSGRAPH::outdir = "";
15 $UPSGRAPH::step = 60;
16 $UPSGRAPH::keep = (370*24*60*60)/$UPSGRAPH::step;
17 $UPSGRAPH::hosts = ();
18
19 do $ARGV[0] or die "can't read config: $!";
20
21 my $outdir = $UPSGRAPH::outdir;
22 my $step = $UPSGRAPH::step;
23 my $keep = $UPSGRAPH::keep;
24 my $hosts = $UPSGRAPH::hosts;
25
26 sub rrdcreate(@) {
27 my $newrrd = shift;
28 my $field = shift;
29 my $vars = shift;
30 my $start = shift;
31
32 my @cmd = ("${newrrd}", "--step=${step}");
33
34 if (defined($start)) {
35 push @cmd, "--start=${start}";
36 }
37
38 push @cmd, "DS:${field}:GAUGE:600:" .
39 $vars->{$field}->{'min'} . ":" .
40 $vars->{$field}->{'max'} . " ";
41
42 push @cmd, "RRA:AVERAGE:0.5:1:${keep}";
43
44 RRDs::create(@cmd);
45 if (RRDs::error) {
46 print "Error while creating: " . RRDs::error . "\n";
47 exit 1;
48 }
49 }
50
51 sub fetch_snmp(@) {
52 my $address = shift;
53 my $community = shift;
54 my $oid = shift;
55
56 (my $session, my $error) = Net::SNMP->session(Hostname => $address,
57 Community => $community);
58
59 if (!$session) {
60 print STDERR "session error: $error";
61 }
62
63 $session->translate(0);
64
65 my $result = $session->get_request($oid);
66
67 $session->close;
68
69 return undef if (!defined($result));
70
71 $result->{$oid};
72 }
73
74 sub fetch_tcp(@) {
75 my $address = shift;
76 my $port = shift;
77
78 my $sock = IO::Socket::INET->new(PeerAddr => $address,
79 PeerPort => $port,
80 Proto => 'tcp',
81 Timeout => 1);
82
83 return undef if (!$sock);
84
85 chomp(my $value = <$sock>);
86
87 close($sock);
88
89 if (!$value) {
90 return undef;
91 }
92
93 $value=~ s/\s//g;
94
95 $value;
96 }
97
98 if ($> == 0) {
99 if (@ARGV != 2) {
100 print STDERR "Running as root, please provide UID as 2th argument!\n";
101 exit(1);
102 }
103
104 print "Running as root, switching to ".$ARGV[1]."\n";
105 $< = $> = $ARGV[1];
106 }
107
108 foreach my $host (@$hosts) {
109 my $rrdfile = $host->{'rrdfile'};
110
111 foreach my $var (keys(%{$host->{'vars'}})) {
112 $host->{'vars'}->{$var}->{'min'} = 'U' if (!defined($host->{'vars'}->{$var}->{'min'}));
113 $host->{'vars'}->{$var}->{'max'} = 'U' if (!defined($host->{'vars'}->{$var}->{'max'}));
114 }
115
116 if (-e "${rrdfile}") {
117 print "Reading old ${rrdfile} to preserve data...\n";
118
119 my $rrdinfo = RRDs::info("${rrdfile}");
120 if (RRDs::error) {
121 print "Error while getting info: " . RRDs::error . "\n";
122 exit 1;
123 }
124
125 (my $start, my $ostep, my $names, my $data) =
126 RRDs::fetch("${rrdfile}",
127 "-s " . (time() - ($rrdinfo->{'rra[0].rows'} * $rrdinfo->{'step'})),
128 "AVERAGE");
129
130 if (RRDs::error) {
131 print "Error while fetching data: " . RRDs::error . "\n";
132 exit 1;
133 }
134
135 foreach my $field (@$names) {
136 if (! -e "${rrdfile}.${field}") {
137 rrdcreate("${rrdfile}.${field}",
138 "${field}",
139 $host->{'vars'},
140 (${start}-${ostep}));
141 }
142 }
143
144 my $pos = $start;
145 foreach my $line (@$data) {
146 foreach my $field (@$names) {
147 my $val = shift (@$line);
148 $val = 'U' if (!defined($val));
149
150 RRDs::update("${rrdfile}.${field}", "${pos}:${val}");
151 if (RRDs::error) {
152 print "Can't insert data: " . RRDs::error . "\n";
153 exit 1;
154 }
155
156 }
157
158 $pos += $ostep;
159
160 if ((($pos-$start)/$ostep) == $#$data) {
161 last;
162 }
163 }
164
165 rename("${rrdfile}", "${rrdfile}.old") or die "Can't rename old file: $!\n";
166 }
167
168 foreach my $field (@{$host->{'fields'}}) {
169 if (! -e "${rrdfile}.${field}") {
170 print "Creating ${rrdfile}.${field}...\n";
171 rrdcreate("${rrdfile}.${field}",
172 "${field}",
173 $host->{'vars'});
174 }
175
176 my $rrdinfo = RRDs::info("${rrdfile}.${field}");
177 if (RRDs::error) {
178 print "Error while getting info: " . RRDs::error . "\n";
179 exit 1;
180 }
181
182 if (defined($rrdinfo->{"ds[${field}].min"})) {
183 if ($rrdinfo->{"ds[${field}].min"} ne $host->{'vars'}->{$field}->{'min'}) {
184 RRDs::tune("${rrdfile}.${field}","-i",$field.":".$host->{'vars'}->{$field}->{'min'});
185 }
186 } else {
187 if ($host->{'vars'}->{$field}->{'min'} ne 'U') {
188 RRDs::tune("${rrdfile}.${field}","-i",$field.":".$host->{'vars'}->{$field}->{'min'});
189 }
190 }
191
192 if (RRDs::error) {
193 print "Error while setting min: " . RRDs::error . "\n";
194 exit 1;
195 }
196
197 if (defined($rrdinfo->{"ds[${field}].max"})) {
198 if ($rrdinfo->{"ds[${field}].max"} ne $host->{'vars'}->{$field}->{'max'}) {
199 RRDs::tune("${rrdfile}.${field}","-a",$field.":".$host->{'vars'}->{$field}->{'max'});
200 }
201 } else {
202 if ($host->{'vars'}->{$field}->{'max'} ne 'U') {
203 RRDs::tune("${rrdfile}.${field}","-a",$field.":".$host->{'vars'}->{$field}->{'max'});
204 }
205 }
206
207 if (RRDs::error) {
208 print "Error while setting max: " . RRDs::error . "\n";
209 exit 1;
210 }
211
212 if ($rrdinfo->{'rra[0].rows'} != $keep) {
213 print "Resizing ${rrdfile}.${field} from " . $rrdinfo->{'rra[0].rows'} .
214 " to ${keep} samples.\n";
215
216 (my $start, my $ostep, my $names, my $data) =
217 RRDs::fetch("${rrdfile}.${field}",
218 "-s " . (time() - ($rrdinfo->{'rra[0].rows'} * $rrdinfo->{'step'})),
219 "AVERAGE");
220
221 if (RRDs::error) {
222 print "Error while fetching data: " . RRDs::error . "\n";
223 exit 1;
224 }
225
226 rrdcreate("${rrdfile}.${field}.new",
227 "${field}",
228 $host->{'vars'},
229 (${start}-${ostep}));
230
231 print "Preserving data since " . localtime($start) . "\n";
232
233 my $pos = $start;
234 foreach my $line (@$data) {
235 my $vline = "${pos}";
236
237 foreach my $val (@$line) {
238 $val = 'U' if (!defined($val));
239 $vline .= ":${val}";
240 }
241 RRDs::update("${rrdfile}.${field}.new", $vline) or die "Can't insert data\n";
242
243 if (RRDs::error) {
244 print "Error while updating: " . RRDs::error . "\n";
245 exit 1;
246 }
247 $pos += $ostep;
248
249 if ((($pos-$start)/$ostep) == $#$data) {
250 last;
251 }
252 }
253
254 rename("${rrdfile}.${field}", "${rrdfile}.${field}.old") or die "Can't rename old file: $!\n";
255 rename("${rrdfile}.${field}.new", "${rrdfile}.${field}") or die "Can't rename new file: $!\n";
256
257 $rrdinfo = RRDs::info("${rrdfile}.${field}");
258 if (RRDs::error) {
259 print "Error while getting info: " . RRDs::error . "\n";
260 exit 1;
261 }
262
263 if ($rrdinfo->{'rra[0].rows'} != $keep) {
264 print "Failed!\n";
265 exit 1;
266 }
267 }
268 }
269 }
270
271 my $child = fork();
272
273 die "fork failed!" if (!defined($child));
274
275 exit 0 if ($child != 0);
276
277 while(1) {
278 open(HTML, ">${outdir}/index.html.new");
279
280 print HTML '<html><head><meta http-equiv="refresh" content="60"/><meta http-equiv="cache-control" content="no-cache"/><meta http-equiv="pragma" content="no-cache"/><meta http_equiv="expires" content="Sat, 26 Jul 1997 05:00:00 GMT"/><title>Status</title></head>';
281 print HTML '<body bgcolor="#ffffff">';
282
283 foreach my $host (@$hosts) {
284 print HTML "[<a href=\"#".${host}->{'name'}."\">".${host}->{'name'}."</a>]&nbsp;";
285 }
286 print HTML "<br>\n";
287
288 foreach my $host (@$hosts) {
289 print HTML "<br>\n";
290 print HTML "<a name=\"".${host}->{'name'}."\"></a>\n";
291 my $vars = $host->{'vars'};
292 my $rrdfile = $host->{'rrdfile'};
293 my $hostname = $host->{'name'};
294
295 foreach my $var (@{$host->{'fields'}}) {
296 delete $vars->{$var}->{'value'};
297
298 my $result;
299
300 if ((!defined($vars->{$var}->{'proto'})) ||
301 ($vars->{$var}->{'proto'} eq '') ||
302 ($vars->{$var}->{'proto'} eq 'snmp')) {
303 $result = fetch_snmp($host->{'address'}, $host->{'community'}, $vars->{$var}->{'oid'});
304 } elsif ($vars->{$var}->{'proto'} eq 'tcp') {
305 $result = fetch_tcp($host->{'address'}, $vars->{$var}->{'port'});
306 }
307
308 next unless (defined $result);
309
310 $vars->{$var}->{'value'} = $result;
311 if (defined($vars->{$var}->{'factor'})) {
312 $vars->{$var}->{'value'} *= $vars->{$var}->{'factor'};
313 }
314 }
315
316 foreach my $var (@{$host->{'fields'}}) {
317 if (!(defined($vars->{$var}->{'value'}))) {
318 $vars->{$var}->{'value'} = 'U';
319 }
320 RRDs::update("${rrdfile}.${var}", "N:" . $vars->{$var}->{'value'});
321 }
322 if (RRDs::error) {
323 print "Error while updating: " . RRDs::error . "\n";
324 }
325
326 foreach my $var (@{$host->{'fields'}}) {
327 my @graphdef = ('-P', "--lazy", "-t", $hostname." - ".$vars->{$var}->{'name'}, "DEF:${var}=${rrdfile}.${var}:${var}:AVERAGE", "LINE1:${var}#FF0000");
328
329 push @graphdef, "VDEF:cur=${var},LAST";
330 push @graphdef, 'GPRINT:cur:Current\\: <span foreground="#FF0000">%.2lf</span>\\r';
331
332 my $mtime;
333 $mtime=(stat("${outdir}/${hostname}.${var}.png.work"))[9];
334
335 (my $averages, my $width, my $height) =
336 RRDs::graph("${outdir}/${hostname}.${var}.png.work",
337 "-w", "720", @graphdef);
338
339 pop @graphdef;
340 pop @graphdef;
341
342 if (RRDs::error) {
343 print "Error while graphing: " . RRDs::error . "\n";
344 } else {
345 my $newmtime=(stat("${outdir}/${hostname}.${var}.png.work"))[9];
346 if ((!defined($mtime)) || ($newmtime != $mtime)) {
347 copy("${outdir}/${hostname}.${var}.png.work", "${outdir}/${hostname}.${var}.png.new");
348 rename("${outdir}/${hostname}.${var}.png.new", "${outdir}/${hostname}.${var}.png");
349 }
350 }
351
352 print HTML "<a href=\"${hostname}.${var}.html\"><img src=\"${hostname}.${var}.png\" width=\"${width}\" height=\"${height}\" border=\"0\"></a><br>\n";
353
354 open (HTML2, ">${outdir}/${hostname}.${var}.html.new");
355 print HTML2 '<html><head><meta http-equiv="refresh" content="60"/><meta http-equiv="cache-control" content="no-cache"/><meta http-equiv="pragma" content="no-cache"/><meta http_equiv="expires" content="Sat, 26 Jul 1997 05:00:00 GMT"/><title>' . $vars->{$var}->{'name'} . '</title></head>';
356 print HTML2 '<body bgcolor="#ffffff">';
357
358 push @graphdef, "VDEF:min=${var},MINIMUM";
359 push @graphdef, "GPRINT:min:Minimum\\: %.2lf";
360
361 push @graphdef, "VDEF:avg=${var},AVERAGE";
362 push @graphdef, "GPRINT:avg:Average\\: %.2lf";
363
364 push @graphdef, "VDEF:max=${var},MAXIMUM";
365 push @graphdef, "GPRINT:max:Maximum\\: %.2lf";
366
367 push @graphdef, "VDEF:cur=${var},LAST";
368 push @graphdef, "GPRINT:cur:Current\\: %.2lf";
369
370 $mtime=(stat("${outdir}/${hostname}.${var}.long.png.work"))[9];
371 ($averages, $width, $height) =
372 RRDs::graph("${outdir}/${hostname}.${var}.long.png.work",
373 "-w", "1008", @graphdef);
374
375 if (RRDs::error) {
376 print "Error while graphing: " . RRDs::error . "\n";
377 } else {
378 my $newmtime=(stat("${outdir}/${hostname}.${var}.long.png.work"))[9];
379 if ((!defined($mtime)) || ($newmtime != $mtime)) {
380 copy("${outdir}/${hostname}.${var}.long.png.work", "${outdir}/${hostname}.${var}.long.png.new");
381 rename("${outdir}/${hostname}.${var}.long.png.new", "${outdir}/${hostname}.${var}.long.png");
382 }
383 }
384
385 print HTML2 "<img src=\"${hostname}.${var}.long.png\" width=\"${width}\" height=\"${height}\"><br>";
386
387 $mtime=(stat("${outdir}/${hostname}.${var}.week.png.work"))[9];
388 ($averages, $width, $height) =
389 RRDs::graph("${outdir}/${hostname}.${var}.week.png.work",
390 "-w", "1008", "-e", "now", "-s", "end-1w", @graphdef);
391
392 if (RRDs::error) {
393 print "Error while graphing: " . RRDs::error . "\n";
394 } else {
395 my $newmtime=(stat("${outdir}/${hostname}.${var}.week.png.work"))[9];
396 if ((!defined($mtime)) || ($newmtime != $mtime)) {
397 copy("${outdir}/${hostname}.${var}.week.png.work", "${outdir}/${hostname}.${var}.week.png.new");
398 rename("${outdir}/${hostname}.${var}.week.png.new", "${outdir}/${hostname}.${var}.week.png");
399 }
400 }
401
402 print HTML2 "<img src=\"${hostname}.${var}.week.png\" width=\"${width}\" height=\"${height}\"><br>";
403
404 $mtime=(stat("${outdir}/${hostname}.${var}.year.png.work"))[9];
405 ($averages, $width, $height) =
406 RRDs::graph("${outdir}/${hostname}.${var}.year.png.work",
407 "-w", "1008", "-e", "now", "-s", "end-1y", @graphdef);
408
409 if (RRDs::error) {
410 print "Error while graphing: " . RRDs::error . "\n";
411 } else {
412 my $newmtime=(stat("${outdir}/${hostname}.${var}.year.png.work"))[9];
413 if ((!defined($mtime)) || ($newmtime != $mtime)) {
414 copy("${outdir}/${hostname}.${var}.year.png.work", "${outdir}/${hostname}.${var}.year.png.new");
415 rename("${outdir}/${hostname}.${var}.year.png.new", "${outdir}/${hostname}.${var}.year.png");
416 }
417 }
418
419 print HTML2 "<img src=\"${hostname}.${var}.year.png\" width=\"${width}\" height=\"${height}\"><br>";
420
421 print HTML2 "</body></html>\n";
422 close(HTML2);
423 rename("${outdir}/${hostname}.${var}.html.new", "${outdir}/${hostname}.${var}.html");
424 }
425 }
426
427 print HTML "</body></html>\n";
428 print HTML "<br>Generated on: " . localtime(time());
429 print HTML ' by <a href="http://git.zerfleddert.de/cgi-bin/gitweb.cgi/upsgraph">upsgraph</a>.';
430
431 close(HTML);
432
433 rename("${outdir}/index.html.new", "${outdir}/index.html");
434
435 sleep(${step}/2);
436 }
Impressum, Datenschutz