#!/usr/bin/perl -w
# Assumes wstart has already been run.
use strict;
use RRDs;

my $rrd = '/var/lib/rrd';
my $img = '/var/www/localhost/htdocs/rrdtool/adsl';

# process data for each interface (add/delete as required)
&ProcessADSL("adsl", "Westell 2100");

sub ShowErr
{
	if (my $err = RRDs::error) {
		print "$0: failed to create rrd: $err\n";
	}
}

sub NormalizeDB
{
	my ($db) = @_;
	if ($db > 1000) {
		$db = 0;
	}
	$db /= 10;
}

sub ProcessADSL
{
	my ($name, $desc) = @_;

	my $in = `westell`;
	my ($uptime) = ($in =~ /Uptime Counter:\s+(\d+)/);
	my ($up_sync) = ($in =~ /Upstream Sync.*:\s+(\d+)/);
	my ($down_snr) = ($in =~ /Downstream SNR.*:\s+(\d+)/);
	my ($down_att) = ($in =~ /Downstream Att.*:\s+(\d+)/);
	my ($down_sync) = ($in =~ /Downstream Sync.*:\s+(\d+)/);
	my ($fec_err) = ($in =~ /FEC.*:\s+(\d+)/);
	my ($crc_err) = ($in =~ /CRC.*:\s+(\d+)/);
	my ($hec_err) = ($in =~ /HEC.*:\s+(\d+)/);
	my ($signal_err) = ($in =~ /Signal.*:\s+(\d+)/);
	my ($frame_err) = ($in =~ /Frame.*:\s+(\d+)/);
	my ($tx_cell) = ($in =~ /Tx Cell.*:\s+(\d+)/);
	my ($rx_cell) = ($in =~ /Rx Cell.*:\s+(\d+)/);
	my ($drop_cell) = ($in =~ /Dropped.*:\s+(\d+)/);
	my ($tx_eth) = ($in =~ /Tx Eth.*:\s+(\d+)/);
	my ($rx_eth) = ($in =~ /Rx Eth.*:\s+(\d+)/);
	my ($drop_eth) = ($in =~ /Discard.*:\s+(\d+)/);

	$down_snr = &NormalizeDB($down_snr);
	$down_att = &NormalizeDB($down_att);

	print "$name uptime: $uptime\n";
	print "$name frames in, out: $tx_eth, $rx_eth\n";
	print "$name cells in, out: $tx_cell, $rx_cell\n";
	print "$name sync down, up: $down_sync, $up_sync\n";
	print "$name snr down: $down_snr\n";
	print "$name attenuation down: $down_att\n";
	print "$name errors fec, crc, hec, signal, frame: $fec_err, $crc_err, $hec_err, $signal_err, $frame_err\n";
	print "$name dropped frames, cells: $drop_eth, $drop_cell\n";

	if (! -e "$rrd/$name.rrd")
	{
	print "creating rrd database for $name interface...\n";
	RRDs::create "$rrd/$name.rrd",
		"-s 300",
		"DS:uptime:GAUGE:600:U:U",
		"DS:upsync:GAUGE:600:U:U",
		"DS:downsync:GAUGE:600:U:U",
		"DS:downsnr:GAUGE:600:U:U",
		"DS:downatt:GAUGE:600:U:U",
		"DS:fecerr:GAUGE:600:U:U",
		"DS:crcerr:GAUGE:600:U:U",
		"DS:hecerr:GAUGE:600:U:U",
		"DS:frameerr:GAUGE:600:U:U",
		"DS:signalerr:GAUGE:600:U:U",
		"DS:txcell:DERIVE:600:U:U",
		"DS:rxcell:DERIVE:600:U:U",
		"DS:dropcell:DERIVE:600:U:U",
		"DS:txeth:DERIVE:600:U:U",
		"DS:rxeth:DERIVE:600:U:U",
		"DS:dropeth:DERIVE:600:U:U",
		"RRA:AVERAGE:0.5:1:576",
		"RRA:AVERAGE:0.5:6:672",
		"RRA:AVERAGE:0.5:24:732",
		"RRA:AVERAGE:0.5:144:1460";
	&ShowErr();
	}

	RRDs::update "$rrd/$name.rrd",
		"-t", "uptime:upsync:downsync:downsnr:downatt:fecerr:crcerr:hecerr:frameerr:signalerr:txcell:rxcell:dropcell:txeth:rxeth:dropeth",
		"N:$uptime:$up_sync:$down_sync:$down_snr:$down_att:$fec_err:$crc_err:$hec_err:$frame_err:$signal_err:$tx_cell:$rx_cell:$drop_cell:$tx_eth:$rx_eth:$drop_eth";
	&ShowErr();

	my @intervals = ('day', 'week', 'month', 'year');
	foreach my $time (@intervals) {
		&CreateGaugeGraph($name, $time, $desc, "uptime", "Uptime", "seconds");
		&CreateGaugeGraph($name, $time, $desc, "downsnr", "SNR Down", "dB");
		&CreateGaugeGraph($name, $time, $desc, "downatt", "Attenuation Down", "dBm");
		&CreateErrorGraph($name, $time, $desc);
		&CreateSyncGraph($name, $time, $desc);
	&CreateTrafficGraph($name, $time, $desc, "rxeth", "txeth", "frames");
		&CreateTrafficGraph($name, $time, $desc, "rxcell", "txcell", "cells");
	}
}

sub CreateErrorGraph
{
	my ($name, $interval, $desc) = @_;

	RRDs::graph "$img/$interval/errors.png",
		"-s -1$interval",
		"-t Errors on $name :: $desc",
		"--lazy",
		"-h", "180", "-w", "600",
		"-l 0",
		"-a", "PNG",
		"-v errors",
		"DEF:fecerr=$rrd/$name.rrd:fecerr:AVERAGE",
		"DEF:crcerr=$rrd/$name.rrd:crcerr:AVERAGE",
		"DEF:hecerr=$rrd/$name.rrd:hecerr:AVERAGE",
		"DEF:frameerr=$rrd/$name.rrd:frameerr:AVERAGE",
		"DEF:signalerr=$rrd/$name.rrd:signalerr:AVERAGE",
		"DEF:dropcell=$rrd/$name.rrd:dropcell:AVERAGE",
		"DEF:dropeth=$rrd/$name.rrd:dropeth:AVERAGE",
		"LINE1:fecerr#FF0000:FEC Errors",
		"GPRINT:fecerr:MAX:  Max\\: %4.0lf %s",
		"GPRINT:fecerr:AVERAGE: Avg\\: %4.0lf %S",
		"GPRINT:fecerr:LAST: Current\\: %4.0lf %Serrors\\n",
		"LINE1:crcerr#0000FF:CRC Errors",
		"GPRINT:crcerr:MAX:  Max\\: %4.0lf %s",
		"GPRINT:crcerr:AVERAGE: Avg\\: %4.0lf %S",
		"GPRINT:crcerr:LAST: Current\\: %4.0lf %Serrors\\n",
		"LINE1:hecerr#00FF00:HEC Errors",
		"GPRINT:hecerr:MAX:  Max\\: %4.0lf %s",
		"GPRINT:hecerr:AVERAGE: Avg\\: %4.0lf %S",
		"GPRINT:hecerr:LAST: Current\\: %4.0lf %Serrors\\n",
		"LINE1:frameerr#00FFFF:Frames Lost",
		"GPRINT:frameerr:MAX:  Max\\: %4.0lf %s",
		"GPRINT:frameerr:AVERAGE: Avg\\: %4.0lf %S",
		"GPRINT:frameerr:LAST: Current\\: %4.0lf %Serrors\\n",
		"LINE1:signalerr#000000:Signal Lost",
		"GPRINT:signalerr:MAX:  Max\\: %4.0lf %s",
		"GPRINT:signalerr:AVERAGE: Avg\\: %4.0lf %S",
		"GPRINT:signalerr:LAST: Current\\: %4.0lf %Serrors\\n",
		"LINE1:dropcell#FF00FF:Cells Dropped",
		"GPRINT:dropcell:MAX:  Max\\: %4.0lf %s",
		"GPRINT:dropcell:AVERAGE: Avg\\: %4.0lf %S",
		"GPRINT:dropcell:LAST: Current\\: %4.0lf %Serrors\\n",
		"LINE1:dropeth#FFFF00:Frames Dropped",
		"GPRINT:dropeth:MAX:  Max\\: %4.0lf %s",
		"GPRINT:dropeth:AVERAGE: Avg\\: %4.0lf %S",
		"GPRINT:dropeth:LAST: Current\\: %4.0lf %Serrors";
	&ShowErr();
}

sub CreateGaugeGraph
{
	my ($name, $interval, $desc, $data, $text, $unit) = @_;

	RRDs::graph "$img/$interval/$data.png",
		"-s -1$interval",
		"-t $text on $name :: $desc",
		"--lazy",
		"-h", "80", "-w", "600",
		"-l 0",
		"-a", "PNG",
		"-v $unit",
		"DEF:in=$rrd/$name.rrd:$data:AVERAGE",
		"AREA:in#32CD32:$text",
		"LINE1:in#336600",
		"GPRINT:in:MAX:  Max\\: %4.1lf %s",
		"GPRINT:in:AVERAGE: Avg\\: %4.1lf %S",
		"GPRINT:in:LAST: Current\\: %4.1lf %S$unit\\n",
		"HRULE:0#000000";
	&ShowErr();
}

sub CreateSyncGraph
{
	my ($name, $interval, $desc) = @_;

	RRDs::graph "$img/$interval/sync.png",
		"-s -1$interval",
		"-t sync on $name :: $desc",
		"--lazy",
		"-h", "80", "-w", "600",
		"-l 0",
		"-a", "PNG",
		"-v bps",
		"DEF:in=$rrd/$name.rrd:downsync:AVERAGE",
		"DEF:out=$rrd/$name.rrd:upsync:AVERAGE",
		"CDEF:out_neg=out,-1,*",
		"AREA:in#32CD32:Downstream",
		"LINE1:in#336600",
		"GPRINT:in:LAST: Current\\: %5.1lf %Sbps\\n",
		"AREA:out_neg#4169E1:Upstream",
		"LINE1:out_neg#0033CC",
		"GPRINT:out:LAST: Current\\: %4.3lf %Sbps",
		"HRULE:0#000000";
	&ShowErr();
}

sub CreateTrafficGraph
{
	my ($name, $interval, $desc, $dat_in, $dat_out, $unit) = @_;

	RRDs::graph "$img/$interval/$unit.png",
		"-s -1$interval",
		"-t traffic $unit on $name :: $desc",
		"--lazy",
		"-h", "80", "-w", "600",
		"-l 0",
		"-a", "PNG",
		"-v $unit/sec",
		"DEF:in=$rrd/$name.rrd:$dat_in:AVERAGE",
		"DEF:out=$rrd/$name.rrd:$dat_out:AVERAGE",
		"CDEF:out_neg=out,-1,*",
		"AREA:in#32CD32:Incoming",
		"LINE1:in#336600",
		"GPRINT:in:MAX:  Max\\: %6.3lf %s",
		"GPRINT:in:AVERAGE: Avg\\: %6.3lf %S",
		"GPRINT:in:LAST: Current\\: %6.3lf %S$unit/sec\\n",
		"AREA:out_neg#4169E1:Outgoing",
		"LINE1:out_neg#0033CC",
		"GPRINT:out:MAX:  Max\\: %6.3lf %S",
		"GPRINT:out:AVERAGE: Avg\\: %6.3lf %S",
		"GPRINT:out:LAST: Current\\: %6.3lf %S$unit/sec",
		"HRULE:0#000000";
	&ShowErr();
}

