only copy files when they have changed
[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 = ("--lazy", "-t", $hostname." - ".$vars->{$var}->{'name'}, "DEF:${var}=${rrdfile}.${var}:${var}:AVERAGE", "LINE1:${var}#FF0000");
328
329 my $mtime;
330 $mtime=(stat("${outdir}/${hostname}.${var}.png.work"))[9];
331
332 (my $averages, my $width, my $height) =
333 RRDs::graph("${outdir}/${hostname}.${var}.png.work",
334 "-w", "720", @graphdef);
335
336 if (RRDs::error) {
337 print "Error while graphing: " . RRDs::error . "\n";
338 } else {
339 my $newmtime=(stat("${outdir}/${hostname}.${var}.png.work"))[9];
340 if ($newmtime != $mtime) {
341 copy("${outdir}/${hostname}.${var}.png.work", "${outdir}/${hostname}.${var}.png.new");
342 rename("${outdir}/${hostname}.${var}.png.new", "${outdir}/${hostname}.${var}.png");
343 }
344 }
345
346 print HTML "<a href=\"${hostname}.${var}.html\"><img src=\"${hostname}.${var}.png\" width=\"${width}\" height=\"${height}\" border=\"0\"></a><br>\n";
347
348 open (HTML2, ">${outdir}/${hostname}.${var}.html.new");
349 print HTML2 "<html><head><title>" . $vars->{$var}->{'name'} . "</title></head>";
350 print HTML2 '<body bgcolor="#ffffff">';
351
352
353 push @graphdef, "VDEF:min=${var},MINIMUM";
354 push @graphdef, "GPRINT:min:Minimum\\: %.2lf";
355
356 push @graphdef, "VDEF:avg=${var},AVERAGE";
357 push @graphdef, "GPRINT:avg:Average\\: %.2lf";
358
359 push @graphdef, "VDEF:max=${var},MAXIMUM";
360 push @graphdef, "GPRINT:max:Maximum\\: %.2lf";
361
362 push @graphdef, "VDEF:cur=${var},LAST";
363 push @graphdef, "GPRINT:cur:Current\\: %.2lf";
364
365 $mtime=(stat("${outdir}/${hostname}.${var}.long.png.work"))[9];
366 ($averages, $width, $height) =
367 RRDs::graph("${outdir}/${hostname}.${var}.long.png.work",
368 "-w", "1008", @graphdef);
369
370 if (RRDs::error) {
371 print "Error while graphing: " . RRDs::error . "\n";
372 } else {
373 my $newmtime=(stat("${outdir}/${hostname}.${var}.long.png.work"))[9];
374 if ($newmtime != $mtime) {
375 copy("${outdir}/${hostname}.${var}.long.png.work", "${outdir}/${hostname}.${var}.long.png.new");
376 rename("${outdir}/${hostname}.${var}.long.png.new", "${outdir}/${hostname}.${var}.long.png");
377 }
378 }
379
380 print HTML2 "<img src=\"${hostname}.${var}.long.png\" width=\"${width}\" height=\"${height}\"><br>";
381
382 $mtime=(stat("${outdir}/${hostname}.${var}.week.png.work"))[9];
383 ($averages, $width, $height) =
384 RRDs::graph("${outdir}/${hostname}.${var}.week.png.work",
385 "-w", "1008", "-e", "now", "-s", "end-1w", @graphdef);
386
387 if (RRDs::error) {
388 print "Error while graphing: " . RRDs::error . "\n";
389 } else {
390 my $newmtime=(stat("${outdir}/${hostname}.${var}.week.png.work"))[9];
391 if ($newmtime != $mtime) {
392 copy("${outdir}/${hostname}.${var}.week.png.work", "${outdir}/${hostname}.${var}.week.png.new");
393 rename("${outdir}/${hostname}.${var}.week.png.new", "${outdir}/${hostname}.${var}.week.png");
394 }
395 }
396
397 print HTML2 "<img src=\"${hostname}.${var}.week.png\" width=\"${width}\" height=\"${height}\"><br>";
398
399 $mtime=(stat("${outdir}/${hostname}.${var}.year.png.work"))[9];
400 ($averages, $width, $height) =
401 RRDs::graph("${outdir}/${hostname}.${var}.year.png.work",
402 "-w", "1008", "-e", "now", "-s", "end-1y", @graphdef);
403
404 if (RRDs::error) {
405 print "Error while graphing: " . RRDs::error . "\n";
406 } else {
407 my $newmtime=(stat("${outdir}/${hostname}.${var}.year.png.work"))[9];
408 if ($newmtime != $mtime) {
409 copy("${outdir}/${hostname}.${var}.year.png.work", "${outdir}/${hostname}.${var}.year.png.new");
410 rename("${outdir}/${hostname}.${var}.year.png.new", "${outdir}/${hostname}.${var}.year.png");
411 }
412 }
413
414 print HTML2 "<img src=\"${hostname}.${var}.year.png\" width=\"${width}\" height=\"${height}\"><br>";
415
416 print HTML2 "</body></html>\n";
417 close(HTML2);
418 rename("${outdir}/${hostname}.${var}.html.new", "${outdir}/${hostname}.${var}.html");
419 }
420 }
421
422 print HTML "</body></html>\n";
423 print HTML "<br>Generated on: " . localtime(time());
424 print HTML ' by <a href="http://git.zerfleddert.de/cgi-bin/gitweb.cgi/upsgraph">upsgraph</a>.';
425
426 close(HTML);
427
428 rename("${outdir}/index.html.new", "${outdir}/index.html");
429
430 sleep(${step}/2);
431 }
Impressum, Datenschutz