#!/usr/bin/perl -w use strict; # Quick'n'dirty Rackshack bandwidth stats aggregator using rrdtool # Copyright (c) 2003 Eike Frost (rrdtool@kefro.st) use LWP::Simple; use RRDs; use Storable; my $maxsources = 30; my $graphdir = 'graphs/'; my $rrdfile = 'rs_interfaces.rrd'; my @colors_in = qw(00e000 00d800 00d000 00c800 00c000 00b800 00b000 00a800 00a000 009800 009000 008800 008000 007800 007000 006800 006000); my @colors_out = qw(0000ff 0000f8 0000f0 0000e8 0000e0 0000d8 0000d0 0000c8 0000c0 0000b8 0000b0 0000a8 0000a0 000098 000090 000088 000080); my %stored = (-e 'rs_interfaces.storable' ? %{retrieve('rs_interfaces.storable')} : ()); my @ifs = (defined $stored{list} ? @{$stored{list}} : ()); my %ifs = (defined $stored{hash} ? %{$stored{hash}} : ()); my $networks = get("http://www.rackshack.net/english/aboutus/networks.asp"); my @ifsadr = ($networks =~ m/a href="(http:\/\/www.ev1.net\/stats\/.*?)"/gis); if (!-e 'rs_interfaces.rrd') { my @datasources = (); for (my $i = 0; $i < $maxsources; $i++) { push @datasources, 'DS:ifin_' .$i.':GAUGE:600:0:125000000', 'DS:ifout_'.$i.':GAUGE:600:0:125000000'; } RRDs::create ('rs_interfaces.rrd', '--start', time(), @datasources, '--step', 300, 'RRA:AVERAGE:0.5:1:2000', 'RRA:AVERAGE:0.5:6:2000', 'RRA:AVERAGE:0.5:24:2000', 'RRA:AVERAGE:0.5:288:2000', 'RRA:MAX:0.5:1:2000', 'RRA:MAX:0.5:6:2000', 'RRA:MAX:0.5:24:2000', 'RRA:MAX:0.5:288:2000'); } foreach my $if (@ifsadr) { my $ifadr = get($if); my $ifname = ($ifadr =~ m/<TITLE>.*to (.*)<\/TITLE>/gis)[0]; unless (exists $ifs{$ifname}) { push @ifs, $ifname; } $ifs{$ifname}{cuin} = ($ifadr =~ m/<!-- cuin d ([0-9]+) -->/gis)[0]; $ifs{$ifname}{cuout} = ($ifadr =~ m/<!-- cuout d ([0-9]+) -->/gis)[0]; } my $output = 'N:'; foreach my $if (@ifs) { $output .= int($ifs{$if}{cuin}).':'.int($ifs{$if}{cuout}).':'; } if (scalar @ifs < $maxsources) { for (my $i = 0; $i < $maxsources - scalar @ifs; $i++) { $output .= 'U:U:' } } $output = substr $output, 0, -1; RRDs::update ($rrdfile, $output); my $ERR = RRDs::error; die "ERROR while updating rs_interfaces.rrd: $ERR\n" if $ERR; my @defs = (); my $lastbytes = 'CDEF:totalbytes=0,'; my $lastinbytes = 'CDEF:totalinbytes=0,'; my $lastoutbytes = 'CDEF:totaloutbytes=0,'; my $lastin = 'CDEF:totalinbits=0,'; my $lastout = 'CDEF:totaloutbits=0,'; my $timespan = 259200; for (my $i = 0; $i < scalar @ifs; $i++) { push @defs, 'DEF:ifinbytes_'.$i.'='.$rrdfile.':ifin_'.$i.':AVERAGE', 'DEF:ifoutbytes_'.$i.'='.$rrdfile.':ifout_'.$i.':AVERAGE', 'CDEF:ifinbits_'.$i.'=ifinbytes_'.$i.',8,*', 'CDEF:ifoutbits_'.$i.'=ifoutbytes_'.$i.',8,*', 'CDEF:ifoutbitsneg_'.$i.'=ifoutbits_'.$i.',-1,*', 'CDEF:iftotalinbytes_'.$i.'=ifinbytes_'.$i. ',0,125000000,LIMIT,UN,0,ifinbytes_'.$i.',IF,' .$timespan.',*', 'CDEF:iftotaloutbytes_'.$i.'=ifoutbytes_'.$i. ',0,125000000,LIMIT,UN,0,ifoutbytes_'.$i.',IF,' .$timespan.',*', 'CDEF:iftotalbytes_'.$i.'=iftotalinbytes_'.$i. ',iftotaloutbytes_'.$i.',+'; $lastbytes .= 'iftotalbytes_'.$i.',+,'; $lastinbytes .= 'iftotalinbytes_'.$i.',+,'; $lastoutbytes .= 'iftotaloutbytes_'.$i.',+,'; $lastin .= 'ifinbits_'.$i.',+,'; $lastout .= 'ifoutbits_'.$i.',+,'; } push @defs, map { substr $_, 0, -1 } ($lastbytes, $lastin, $lastout, $lastinbytes, $lastoutbytes); push @defs, 'CDEF:totalbits=totalinbits,totaloutbits,+'; sub trim { my $s = shift; $s = substr $s, 0, 11; if (length ($s) < 11) { $s .= ' 'x(11-length($s)); } return $s; } my @areas = (); my $count = 0; push @areas, 'AREA:ifinbits_0#'.$colors_in[0].':'.trim($ifs[0]); push @areas, 'GPRINT:ifinbits_0:AVERAGE:%3.0lf %sb/s'; push @areas, 'GPRINT:iftotalinbytes_0:AVERAGE:%5.1lf %sB'; for (my $i = 1; $i < scalar @ifs; $i++) { push @areas, 'STACK:ifinbits_'.$i.'#'.$colors_in[$i].':'.trim($ifs[$i]); push @areas, 'GPRINT:ifinbits_'.$i.':AVERAGE:%3.0lf %sb/s'; push @areas, 'GPRINT:iftotalinbytes_'.$i.':AVERAGE:%5.1lf %sB'; if ($i%4 == 3) { $count++; if ($count == 1) { push @areas, 'GPRINT:totalinbits:AVERAGE:%6.2lf %sb/s'; } elsif ($count == 2) { push @areas, 'GPRINT:totalinbits:LAST:%6.2lf %sb/s'; } elsif ($count == 3) { push @areas, 'GPRINT:totalinbytes:AVERAGE:%6.2lf %sB'; } push @areas, 'COMMENT:\n'; } } push @areas, 'COMMENT:\n'; $count = 0; push @areas, 'AREA:ifoutbitsneg_0#'.$colors_out[0].':'.trim($ifs[0]); push @areas, 'GPRINT:ifoutbits_0:AVERAGE:%3.0lf %sb/s'; push @areas, 'GPRINT:iftotaloutbytes_0:AVERAGE:%5.1lf %sB'; for (my $i = 1; $i < scalar @ifs; $i++) { push @areas, 'STACK:ifoutbitsneg_'.$i.'#'.$colors_out[$i].':'. trim($ifs[$i]); push @areas, 'GPRINT:ifoutbits_'.$i.':AVERAGE:%3.0lf %sb/s'; push @areas, 'GPRINT:iftotaloutbytes_'.$i.':AVERAGE:%5.1lf %sB'; if ($i%4 ==3) { $count++; if ($count == 1) { push @areas, 'GPRINT:totaloutbits:AVERAGE:%6.2lf %sb/s'; } elsif ($count == 2) { push @areas, 'GPRINT:totaloutbits:LAST:%6.2lf %sb/s'; } elsif ($count == 3) { push @areas, 'GPRINT:totaloutbytes:AVERAGE:%6.2lf %sB'; } push @areas, 'COMMENT:\n'; } } push @areas, 'COMMENT:\n'; RRDs::graph ($graphdir.'rs_day.png', '--start', $timespan*-1, '-e', '-300', @defs, 'COMMENT: avg xfer avg xfer avg xfer avg xfer Sums(av/mx/to)\n', @areas, 'COMMENT:Totals | Average Maximum Current xfer '. '| Generated on '.localtime().'\n', 'COMMENT:in+out |', 'GPRINT:totalbits:AVERAGE: %7.2lf %sb/s', 'GPRINT:totalbits:MAX: %7.2lf %sb/s', 'GPRINT:totalbits:LAST: %7.2lf %sb/s', 'GPRINT:totalbytes:AVERAGE: %7.2lf %sB', 'COMMENT:| If you have any questions about this graph, contact rrdtool@kefro.st', '-v', 'bits/sec', '-t', 'Rackshack Internet Backbone Bandwidth Utilization (3 days)', '-h', '350', '-w', '864', '-x', 'HOUR:1:HOUR:6:HOUR:2:0:%H'); my $ERR2 = RRDs::error; die "ERROR while graphing: $ERR2\n" if $ERR2; $stored{list} = \@ifs; $stored{hash} = \%ifs; store (\%stored, 'rs_interfaces.storable');