#!/usr/bin/perl -w
#
# waix.ipac version 0.5
#
# Copyright 2003-2009 Martin Pot
# http://martybugs.net/waix.ipac/
#
# makegraphs.pl

# runs fetchipac, runs ipacsum, parses output, inserts data into rrdtool database, and generates traffic graph

use lib qw(/usr/local/rrdtool-1.2.11/lib/perl);
use RRDs;		# RRDTool shared library for RRDTool data storage and graphing
use Time::Local;	# shared library for basic date/time functionality

# define install location of waix.ipac
my $dir = '/usr/local/waix.ipac';

# define location of rrdtool binary
my $rrdtool = '/usr/bin/rrdtool';
# define location of rrdtool database
my $rrd = $dir;
# define target location of rrdtool images
my $img = '/var/www/html/waix.ipac';

# define location of datafile
my $datafile = "$dir/waix.ipac.data";

# define limits for graph (in kbytes/sec)
#  specify something higher than your actual limit, ie, 30 / 10 is suitable for a 256/64 adsl link
#  or 60 / 20 for a 512/128 adsl link
my $inbound = 600;
my $outbound = 120;


## data retrieval #######################################

# update ipac data
`/usr/local/sbin/fetchipac`;

my ($i, $waix_in, $net_in, $net_out, $peak_in, $offpeak_in);

# get ipac output for the last 5 minutes
my $ipac=`/usr/local/sbin/ipacsum --fixed-quantity K -s 5m`;

# parse the ipacsum output to get the numbers we want
&ParseIpac($ipac, $waix_in, $peak_in, $offpeak_in, $net_in, $net_out);

# print the data
print "peak in:    $peak_in kb\n";
print "waix in:    $waix_in kb\n";
print "offpeak in: $offpeak_in kb\n";
print "total in:   $net_in kb\n";
print "total out:  $net_out kb\n";


## data visualisation ###################################

# if rrdtool database doesn't exist, create it
if (! -e "$rrd/waix.ipac.rrd")
{
	print "creating rrd database...\n";
	system("$rrdtool create $rrd/waix.ipac.rrd -s 300"
		." DS:peak_in:ABSOLUTE:600:0:1200000"
		." DS:offpeak_in:ABSOLUTE:600:0:1200000"
		." DS:waix_in:ABSOLUTE:600:0:1200000"
		." DS:total_in:ABSOLUTE:600:0:1200000"
		." DS:total_out:ABSOLUTE:600:0:1200000"
		." RRA:AVERAGE:0.5:1:8640"
		." RRA:AVERAGE:0.5:6:2880"
		." RRA:AVERAGE:0.5:24:4380"
		." RRA:AVERAGE:0.5:144:3650");

	if ($ERROR = RRDs::error)
	{
		print "$0: unable to create rrd database: $ERROR\n";
		exit(1);
	}
}

# insert values into rrd
RRDs::update "$rrd/waix.ipac.rrd",
	"-t", "waix_in:peak_in:offpeak_in:total_in:total_out",
	"N:$waix_in:$peak_in:$offpeak_in:$net_in:$net_out";

# create graph(s)
&CreateGraph("day");
&CreateGraph("week");
&CreateGraph("month");
&CreateGraph("year");


## graph creation ####################################

sub CreateGraph
{
# creates graph
# inputs: $_[0]: interval (ie, day, week, month, year)

	# get current date/time
	my $date=`date`;
	# escape colons for RRDTool 1.2
	$date =~ s/:/\\:/g;
	# chop of trailing new line char
	chomp($date);

	# generate graph
	RRDs::graph "$img/waix.ipac-$_[0].png",
		"-s -1$_[0]",
		"--lazy",
		"-t", "waix.ipac traffic - last $_[0]",
		"-h", "140", "-w","600",
		"-a", "PNG",
		"-v", "kbytes/sec",
#		"-l", "-$outbound", "-u", "$inbound",
		"--rigid",
		"--slope-mode",
		"DEF:waix_in=$rrd/waix.ipac.rrd:waix_in:AVERAGE",
		"DEF:peak_in=$rrd/waix.ipac.rrd:peak_in:AVERAGE",
		"DEF:total_out=$rrd/waix.ipac.rrd:total_out:AVERAGE",
		"DEF:total_in=$rrd/waix.ipac.rrd:total_in:AVERAGE",
		"DEF:offpeak_in=$rrd/waix.ipac.rrd:offpeak_in:AVERAGE",
		"CDEF:total_out_neg=total_out,-1,*",
		"CDEF:offpeak_line=peak_in,offpeak_in,+",
		"AREA:peak_in#4169E1:In\\: Peak",
		"GPRINT:peak_in:AVERAGE:      Average\\: %5.1lf kb/sec",
		"GPRINT:peak_in:LAST: Current\\: %5.1lf kb/sec\\n",
		"STACK:offpeak_in#FFEE00:In\\: Offpeak",
		"GPRINT:offpeak_in:AVERAGE:   Average\\: %5.1lf kb/sec",
		"GPRINT:offpeak_in:LAST: Current\\: %5.1lf kb/sec\\n",
		"STACK:waix_in#32CD32:In\\: WAIX",
		"GPRINT:waix_in:AVERAGE:      Average\\: %5.1lf kb/sec",
		"GPRINT:waix_in:LAST: Current\\: %5.1lf kb/sec\\n",
		"LINE1:total_in#000000:In\\: Total",
		"GPRINT:total_in:AVERAGE:     Average\\: %5.1lf kb/sec",
		"GPRINT:total_in:LAST: Current\\: %5.1lf kb/sec\\n",
		"AREA:total_out_neg#FF9900:Out\\: Total",
		"GPRINT:total_out:AVERAGE:    Average\\: %5.1lf kb/sec",
		"GPRINT:total_out:LAST: Current\\: %5.1lf kb/sec\\n",
		"LINE1:total_in#336600",
		"LINE1:offpeak_line#827B00",
		"LINE1:peak_in#00008B",
		"LINE1:total_out_neg#FF5500",
		"COMMENT:                                                     Last updated\\: $date\\g",
		"HRULE:0#000000";
	if ($ERROR = RRDs::error) { print "$0: unable to generate graph: $ERROR\n"; }
}


## data manipulation ####################################

sub ParseIpac
{
# parses ipacsum output to determine bandwidth usage for each rule
# inputs:	$_[0]: ipac output for last time interval
# outputs:	$_[1]: waix in
#		$_[2]: peak in
#		$_[3]: off-peak in
#		$_[4]: total in
#		$_[5]: total out

        my $w_in=0; my $t_in=0; my $t_out=0; my $p_in=0; my $op_in=0;
	my $modem_in=0; my $sw_in=0; my $dmz_in=0; my $modem_out=0; my $sw_out=0; my $dmz_out=0;
	my ($s, $n, $h, $d, $m, $y);

	# check for no ipacsum output
	if ($_[0] eq "")
	{
		# return no traffic
		$_[1]=0; $_[2]=0; $_[3]=0; $_[4]=0; $_[5]=0;
		return;
	}

	# split ipacsum output into lines
	my @lines = split(/\n/, $_[0]);
	
	# process each line
	foreach $line (@lines)
	{
		# process each inbound rule
		if ($line =~ m/\s*incoming from ([\w.]+?)\s*:\s*(\d+?)K.*/)
		{
			if ($1 eq "WAIX")		# inbound WAIX traffic
			{ $w_in = $2; }
			elsif ($1 eq "internet")	# total inbound internet traffic
			{ $t_in = $2; }
			elsif ($1 eq "modem")		# inbound traffic from modem
			{ $modem_in = $2; }
			elsif ($1 eq "smoothwall")	# inbound traffic from Smoothwall
			{ $sw_in = $2; }
			elsif ($1 eq "dmz")		# inbound traffic from Smoothwall dmz
			{ $dmz_in = $2; }
		}
		# and each outbound rule
		elsif ($line =~ m/\s*outgoing to ([\w.]+?)\s*:\s*(\d+?)K.*/)
		{
			if ($1 eq "internet")
			{ $t_out = $2; }
			elsif ($1 eq "modem")
			{ $modem_out = $2; }
			elsif ($1 eq "smoothwall")
			{ $sw_out = $2; }
			elsif ($1 eq "dmz")
			{ $dmz_out = $2; }
		}
	}

	# traffic corrections
	# subtract modem, smoothwall & dmz traffic
	$t_in -= ($modem_in + $sw_in + $dmz_in);
	$t_out -= ($modem_out + $sw_out + $dmz_in);

	# get components of local time
	($s,$n,$h,$d,$m,$y) = localtime;

	# check if bliinktime or peak time
	# and calculate peak / off-peak quota traffic
	# note that data for the 1 minute before midnight will be counted as off-peak traffic
	
	# check if off-peak	
	if ($h < 8)
	{
		# off-peak in = total in - waix in
		$op_in = $t_in - $w_in;
		# peak in = 0
		$p_in = 0;
	}
	else # must be peak
	{
		# peak in = total in - waix in
		$p_in = $t_in - $w_in;
		# off-peak in = 0
		$op_in = 0;
	}

	# return data
        $_[1] = $w_in;   # waix in
        $_[2] = $p_in;   # peak in
	$_[3] = $op_in;  # off-peak in
        $_[4] = $t_in;   # total in
        $_[5] = $t_out;  # total out
}
