]> git.zerfleddert.de Git - upsgraph/blame - upsgraph.pl
copy is not atomic...
[upsgraph] / upsgraph.pl
CommitLineData
e18c93b3
MG
1#!/usr/bin/perl -w
2
1c9fb9df
MG
3if ((@ARGV != 1) && (@ARGV != 2)) {
4 print STDERR "Syntax: ${0} configfile [uid]\n";
e18c93b3
MG
5 exit(1);
6}
7
1c9fb9df 8use Net::SNMP;
0e572cdc 9use IO::Socket::INET;
e5da3876 10use RRDs;
e26b065f 11use File::Copy;
e5da3876 12use Data::Dumper;
e18c93b3 13
1c9fb9df 14$UPSGRAPH::outdir = "";
1c9fb9df 15$UPSGRAPH::step = 60;
e5da3876 16$UPSGRAPH::keep = (370*24*60*60)/$UPSGRAPH::step;
6c1071ee 17$UPSGRAPH::hosts = ();
e18c93b3 18
1c9fb9df 19do $ARGV[0] or die "can't read config: $!";
e18c93b3 20
1c9fb9df 21my $outdir = $UPSGRAPH::outdir;
1c9fb9df 22my $step = $UPSGRAPH::step;
e5da3876 23my $keep = $UPSGRAPH::keep;
6c1071ee 24my $hosts = $UPSGRAPH::hosts;
e18c93b3 25
e5da3876 26sub rrdcreate(@) {
21377f43
MG
27 my $newrrd = shift;
28 my $field = shift;
6c1071ee 29 my $vars = shift;
e5da3876
MG
30 my $start = shift;
31
21377f43 32 my @cmd = ("${newrrd}", "--step=${step}");
e5da3876
MG
33
34 if (defined($start)) {
35 push @cmd, "--start=${start}";
36 }
37
21377f43
MG
38 push @cmd, "DS:${field}:GAUGE:600:" .
39 $vars->{$field}->{'min'} . ":" .
40 $vars->{$field}->{'max'} . " ";
41
e5da3876
MG
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
6c1071ee
MG
51sub fetch_snmp(@) {
52 my $address = shift;
53 my $community = shift;
54 my $oid = shift;
e5da3876 55
6c1071ee
MG
56 (my $session, my $error) = Net::SNMP->session(Hostname => $address,
57 Community => $community);
e18c93b3 58
0e572cdc
MG
59 if (!$session) {
60 print STDERR "session error: $error";
61 }
e5da3876 62
6c1071ee 63 $session->translate(0);
e5da3876 64
0e572cdc 65 my $result = $session->get_request($oid);
e5da3876 66
6c1071ee 67 $session->close;
21377f43 68
0e572cdc
MG
69 return undef if (!defined($result));
70
71 $result->{$oid};
72}
73
74sub 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;
6c1071ee 96}
e5da3876 97
6c1071ee
MG
98if ($> == 0) {
99 if (@ARGV != 2) {
100 print STDERR "Running as root, please provide UID as 2th argument!\n";
101 exit(1);
e5da3876
MG
102 }
103
6c1071ee
MG
104 print "Running as root, switching to ".$ARGV[1]."\n";
105 $< = $> = $ARGV[1];
21377f43 106}
e5da3876 107
6c1071ee
MG
108foreach my $host (@$hosts) {
109 my $rrdfile = $host->{'rrdfile'};
21377f43 110
46ead44b
MG
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
6c1071ee
MG
116 if (-e "${rrdfile}") {
117 print "Reading old ${rrdfile} to preserve data...\n";
e5da3876 118
6c1071ee
MG
119 my $rrdinfo = RRDs::info("${rrdfile}");
120 if (RRDs::error) {
121 print "Error while getting info: " . RRDs::error . "\n";
122 exit 1;
123 }
21377f43
MG
124
125 (my $start, my $ostep, my $names, my $data) =
6c1071ee 126 RRDs::fetch("${rrdfile}",
21377f43
MG
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
6c1071ee
MG
135 foreach my $field (@$names) {
136 if (! -e "${rrdfile}.${field}") {
137 rrdcreate("${rrdfile}.${field}",
138 "${field}",
139 $host->{'vars'},
140 (${start}-${ostep}));
141 }
142 }
21377f43
MG
143
144 my $pos = $start;
145 foreach my $line (@$data) {
6c1071ee
MG
146 foreach my $field (@$names) {
147 my $val = shift (@$line);
21377f43 148 $val = 'U' if (!defined($val));
21377f43 149
6c1071ee
MG
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
21377f43 156 }
6c1071ee 157
21377f43
MG
158 $pos += $ostep;
159
160 if ((($pos-$start)/$ostep) == $#$data) {
161 last;
162 }
163 }
164
6c1071ee
MG
165 rename("${rrdfile}", "${rrdfile}.old") or die "Can't rename old file: $!\n";
166 }
21377f43 167
6c1071ee
MG
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}");
21377f43
MG
177 if (RRDs::error) {
178 print "Error while getting info: " . RRDs::error . "\n";
179 exit 1;
180 }
181
178e4778
MG
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'}) {
178e4778
MG
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
21377f43 212 if ($rrdinfo->{'rra[0].rows'} != $keep) {
6c1071ee
MG
213 print "Resizing ${rrdfile}.${field} from " . $rrdinfo->{'rra[0].rows'} .
214 " to ${keep} samples.\n";
e18c93b3 215
6c1071ee
MG
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");
6aa53fc5 220
6c1071ee
MG
221 if (RRDs::error) {
222 print "Error while fetching data: " . RRDs::error . "\n";
223 exit 1;
224 }
6aa53fc5 225
6c1071ee
MG
226 rrdcreate("${rrdfile}.${field}.new",
227 "${field}",
228 $host->{'vars'},
229 (${start}-${ostep}));
6aa53fc5 230
6c1071ee 231 print "Preserving data since " . localtime($start) . "\n";
e18c93b3 232
6c1071ee
MG
233 my $pos = $start;
234 foreach my $line (@$data) {
235 my $vline = "${pos}";
e18c93b3 236
6c1071ee
MG
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";
e18c93b3 242
6c1071ee
MG
243 if (RRDs::error) {
244 print "Error while updating: " . RRDs::error . "\n";
245 exit 1;
246 }
247 $pos += $ostep;
e18c93b3 248
6c1071ee
MG
249 if ((($pos-$start)/$ostep) == $#$data) {
250 last;
251 }
252 }
e18c93b3 253
6c1071ee
MG
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";
e18c93b3 256
6c1071ee
MG
257 $rrdinfo = RRDs::info("${rrdfile}.${field}");
258 if (RRDs::error) {
259 print "Error while getting info: " . RRDs::error . "\n";
260 exit 1;
261 }
e18c93b3 262
6c1071ee
MG
263 if ($rrdinfo->{'rra[0].rows'} != $keep) {
264 print "Failed!\n";
265 exit 1;
266 }
e18c93b3 267 }
e18c93b3 268 }
6c1071ee
MG
269}
270
271my $child = fork();
e18c93b3 272
6c1071ee
MG
273die "fork failed!" if (!defined($child));
274
275exit 0 if ($child != 0);
276
277while(1) {
e18c93b3
MG
278 open(HTML, ">${outdir}/index.html.new");
279
2c148a8e 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>';
e18c93b3
MG
281 print HTML '<body bgcolor="#ffffff">';
282
6c1071ee 283 foreach my $host (@$hosts) {
2c148a8e
MG
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";
6c1071ee
MG
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'};
e5da3876 297
0e572cdc
MG
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
6c1071ee
MG
308 next unless (defined $result);
309
0e572cdc 310 $vars->{$var}->{'value'} = $result;
6c1071ee
MG
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 }
e5da3876 322 if (RRDs::error) {
6c1071ee 323 print "Error while updating: " . RRDs::error . "\n";
e5da3876
MG
324 }
325
6c1071ee 326 foreach my $var (@{$host->{'fields'}}) {
0d418146 327 my @graphdef = ("--lazy", "-t", $hostname." - ".$vars->{$var}->{'name'}, "DEF:${var}=${rrdfile}.${var}:${var}:AVERAGE", "LINE1:${var}#FF0000");
6c1071ee 328 (my $averages, my $width, my $height) =
75dbe166 329 RRDs::graph("${outdir}/${hostname}.${var}.png.work",
6c1071ee 330 "-w", "720", @graphdef);
e5da3876 331
6c1071ee
MG
332 if (RRDs::error) {
333 print "Error while graphing: " . RRDs::error . "\n";
334 } else {
75dbe166
MG
335 copy("${outdir}/${hostname}.${var}.png.work", "${outdir}/${hostname}.${var}.png.new");
336 rename("${outdir}/${hostname}.${var}.png.new", "${outdir}/${hostname}.${var}.png");
6c1071ee 337 }
e5da3876 338
2c148a8e 339 print HTML "<a href=\"${hostname}.${var}.html\"><img src=\"${hostname}.${var}.png\" width=\"${width}\" height=\"${height}\" border=\"0\"></a><br>\n";
58c56c83 340
6c1071ee
MG
341 open (HTML2, ">${outdir}/${hostname}.${var}.html.new");
342 print HTML2 "<html><head><title>" . $vars->{$var}->{'name'} . "</title></head>";
343 print HTML2 '<body bgcolor="#ffffff">';
58c56c83 344
58c56c83 345
6c1071ee
MG
346 push @graphdef, "VDEF:min=${var},MINIMUM";
347 push @graphdef, "GPRINT:min:Minimum\\: %.2lf";
58c56c83 348
6c1071ee
MG
349 push @graphdef, "VDEF:avg=${var},AVERAGE";
350 push @graphdef, "GPRINT:avg:Average\\: %.2lf";
58c56c83 351
6c1071ee
MG
352 push @graphdef, "VDEF:max=${var},MAXIMUM";
353 push @graphdef, "GPRINT:max:Maximum\\: %.2lf";
e5da3876 354
6c1071ee
MG
355 push @graphdef, "VDEF:cur=${var},LAST";
356 push @graphdef, "GPRINT:cur:Current\\: %.2lf";
e5da3876 357
6c1071ee 358 ($averages, $width, $height) =
75dbe166 359 RRDs::graph("${outdir}/${hostname}.${var}.long.png.work",
6c1071ee 360 "-w", "1008", @graphdef);
e5da3876 361
6c1071ee
MG
362 if (RRDs::error) {
363 print "Error while graphing: " . RRDs::error . "\n";
364 } else {
75dbe166
MG
365 copy("${outdir}/${hostname}.${var}.long.png.work", "${outdir}/${hostname}.${var}.long.png.new");
366 rename("${outdir}/${hostname}.${var}.long.png.new", "${outdir}/${hostname}.${var}.long.png");
6c1071ee 367 }
e5da3876 368
6c1071ee
MG
369 print HTML2 "<img src=\"${hostname}.${var}.long.png\" width=\"${width}\" height=\"${height}\"><br>";
370
371 ($averages, $width, $height) =
75dbe166 372 RRDs::graph("${outdir}/${hostname}.${var}.week.png.work",
6c1071ee
MG
373 "-w", "1008", "-e", "now", "-s", "end-1w", @graphdef);
374
375 if (RRDs::error) {
376 print "Error while graphing: " . RRDs::error . "\n";
377 } else {
75dbe166
MG
378 copy("${outdir}/${hostname}.${var}.week.png.work", "${outdir}/${hostname}.${var}.week.png.new");
379 rename("${outdir}/${hostname}.${var}.week.png.new", "${outdir}/${hostname}.${var}.week.png");
6c1071ee 380 }
e5da3876 381
6c1071ee 382 print HTML2 "<img src=\"${hostname}.${var}.week.png\" width=\"${width}\" height=\"${height}\"><br>";
e5da3876 383
6c1071ee 384 ($averages, $width, $height) =
75dbe166 385 RRDs::graph("${outdir}/${hostname}.${var}.year.png.work",
6c1071ee 386 "-w", "1008", "-e", "now", "-s", "end-1y", @graphdef);
e5da3876 387
6c1071ee
MG
388 if (RRDs::error) {
389 print "Error while graphing: " . RRDs::error . "\n";
390 } else {
75dbe166
MG
391 copy("${outdir}/${hostname}.${var}.year.png.work", "${outdir}/${hostname}.${var}.year.png.new");
392 rename("${outdir}/${hostname}.${var}.year.png.new", "${outdir}/${hostname}.${var}.year.png");
6c1071ee 393 }
e18c93b3 394
6c1071ee 395 print HTML2 "<img src=\"${hostname}.${var}.year.png\" width=\"${width}\" height=\"${height}\"><br>";
e18c93b3 396
6c1071ee
MG
397 print HTML2 "</body></html>\n";
398 close(HTML2);
399 rename("${outdir}/${hostname}.${var}.html.new", "${outdir}/${hostname}.${var}.html");
400 }
e18c93b3
MG
401 }
402
403 print HTML "</body></html>\n";
404 print HTML "<br>Generated on: " . localtime(time());
863e62dc 405 print HTML ' by <a href="http://git.zerfleddert.de/cgi-bin/gitweb.cgi/upsgraph">upsgraph</a>.';
e18c93b3
MG
406
407 close(HTML);
408
409 rename("${outdir}/index.html.new", "${outdir}/index.html");
410
411 sleep(${step}/2);
412}
Impressum, Datenschutz