#!/usr/bin/perl -w

# linodebw.pl v0.1, (c) 1/24/07 Darxus@ChaosReigns.com, released under GPL v2 or above

# Monitors your linode bandwidth usage vs. your quota, reports actual
# bandwidth used, and estimated bandwidth used, both by month and by time
# period (3 hours by default) if you go over the threshold.  Also reports
# cost if you go over quota.
#
# Usage: ./linode.pl
# Runs continuously.  Run from init if you care about it not being up.
# You must modify the first three variables.
#
# local: total monthly bandwidth usage estimate based on data from /proc/net/dev over the last three hours
# month: total monthly bandwidth usage estimate based on linode xml over the current month
# period: total monthly bandwidth usage estimate based on linode xml over the last three hours
# used: actual total bandwidth used this month, from linode xml
#
# This must be run from an IP belonging to the user which is being queried.
#
# Do not assume anything this says is correct without verifying it yourself.
# Estimate based on the full month is incorrect for the first (incomplete)
# billing period.

$notify = 'darxus@chaosreigns.com'; # comma delimited list with no whitespace, comment out to prevent emailing
$user = 'darxus';
$logdir = "/home/darxus/linodebw"; # comment out this line if you don't want to log
#$threshold = 90;
$threshold = 50; # percentage of quota at which you'd like to be emailed
$price = 0.50; # cost per gigabyte over quota per month

# You are unlikely to need to modify anything below this line.
##############################################################

$url = "http://www.linode.com/members/info/?user=$user";

use LWP::Simple;
use Date::Calc qw( Days_in_Month );
use Time::Local 'timelocal';
use Date::Parse 'str2time';

$delay = $ARGV[0] or $delay = 7200; # please don't set this lower, per linode

while (1)
{
  $text = '';
  $mail = 0;
  print "\n";


#  $linodetime = time;
  $content = get($url);
  for $line (split("\n",$content))
  {
    #print "line:$line\n";
    if ($line =~m#<max_avail>(\d+)</max_avail>#) {
      $quota = $1;
      $quotagb = $quota / 1024 / 1024 / 1024;
    }

    if ($line =~ m#<total_bytes>(\d+)</total_bytes>#) {
      $total = $1;
     # print "total:$total\n";
    }

    if ($line =~ m#<year>(\d+)</year>#) {
      $year = $1;
    }

    if ($line =~ m#<month>(\d+)</month>#) {
      $month = $1;
    }

    if ($line =~ m#<DateTimeStamp>(.*)</DateTimeStamp>#) {
      $linodetime = str2time($1);
    }


  }

  if ( !defined($year) or !defined($month) or !defined($oldmonth) or $month != $oldmonth or !defined($oldyear) or $year != $oldyear ) {
    $days = Days_in_Month($year,$month);
    #print "days:$days\n";
    undef $oldtotal;
  }




  
  $text .= "total bytes used this month:$total, quota: $quotagb gB, time from linode: $linodetime\n";
  $usedgb = $total / 1024 / 1024 / 1024;
  $usedpercent = $total * 100 / $quota;
  if ($usedpercent > 100) {
    $cost = ($usedgb - $quotagb) * $price;
  } else {
    $cost = 0;
  }
  $text .= "actual used $usedgb gB, $usedpercent% of $quotagb, extra bandwidth cost: \$$cost\n";
  $mail = $usedpercent if ($usedpercent >= $threshold and $usedpercent > $mail);
  if (defined($logdir)) {
    open OUT, ">>$logdir/linode.used.log" or print STDERR "Couldn't write to $logdir/linode.used.log: $!\n";
    print OUT "$linodetime $usedgb\n";
    close OUT;
  }

  $monthstart = timelocal(0,0,0,1,$month-1,$year);
  $monthseconds = $linodetime - $monthstart;
  $bytespersecond = $total / $monthseconds;
  $monthbytespermonth = $bytespersecond * $days * 86400;
  $monthgigabytespermonth = $monthbytespermonth / 1024 / 1024 / 1024;
  $monthpercent = $monthbytespermonth * 100 / $quota;
  if ($monthpercent > 100) {
    $cost = ($monthgigabytespermonth - $quotagb) * $price;
  } else {
    $cost = 0;
  }
  $text .= "est. from current month: $monthgigabytespermonth gB/m, $monthpercent% of $quotagb gb, extra bandwidth cost: \$$cost\n";
  $mail = $monthpercent if ($monthpercent >= $threshold and $monthpercent > $mail);
  if (defined($logdir)) {
    open OUT, ">>$logdir/linode.month.log" or print STDERR "Couldn't write to $logdir/linode.month.log: $!\n";
    print OUT "$linodetime $monthgigabytespermonth\n";
    close OUT;
  }


  if (defined($oldtotal)) {
    $elapsed = $linodetime - $oldlinodetime;
    $bytes = $total - $oldtotal;
    $bytespersecond = $bytes / $elapsed;
    $bytespermonth = $bytespersecond * $days * 86400;
    $ratepercent = $bytespermonth * 100 / $quota;
    $rategigabytespermonth = $bytespersecond * $days * 86400 / 1024 / 1024 / 1024;
    
    if ($ratepercent > 100) {
      $cost = ($rategigabytespermonth - $quotagb) * $price;
    } else {
      $cost = 0;
    }

    $text .= "est. calculated from last time sample: $rategigabytespermonth gB/m, $ratepercent% of $quotagb gb, extra bandwidth cost: \$$cost\n";
    $mail = $ratepercent if ($ratepercent >= $threshold and $ratepercent > $mail);
    if (defined($logdir)) {
      open OUT, ">>$logdir/linode.period.log" or print STDERR "Couldn't write to $logdir/linode.period.log: $!\n";
      print OUT "$linodetime $rategigabytespermonth\n";
      close OUT;
    }
  }
  $oldtotal = $total;
  $oldyear = $year;
  $oldmonth = $month;
  $oldlinodetime = $linodetime;
  $total = 0;


  $localtotal = 0;
  $localtime = time;
  open(IN,"</proc/net/dev") or print STDERR "Couldn't read /procnet/dev: $!\n";
  <IN>; # skip first two lines
  <IN>;
  while ($line = <IN>) {
    ($dev,$data) = split(/:/,$line);
    $dev =~ s/\s*//g;

    ($rx,$tx) = (split(' ',$data))[0,8];

    $localtotal += $rx + $tx unless ($dev eq 'lo');
  }
  close IN;
  if (defined($oldlocaltime)) {
    $elapsed = $localtime - $oldlocaltime;
    $bytes = $localtotal - $oldlocaltotal;
    $bytespersecond = $bytes / $elapsed;
    $gigabytespermonth = $bytespersecond * $days * 86400 / 1024 / 1024 / 1024;
    $percent = $gigabytespermonth * 100 / $quotagb;
    if ($percent > 100) {
      $cost = ($gigabytespermonth - $quotagb) * $price;
    } else {
      $cost = 0;
    }
    $text .= "est. from local data for current period: $gigabytespermonth gB/m, $percent% of $quotagb gb, extra bandwidth cost: \$$cost\n";
    $mail = $percent if ($percent >= $threshold and $percent > $mail);
    if (defined($logdir)) {
      open OUT, ">>$logdir/linode.local.log" or print STDERR "Couldn't write to $logdir/linode.local.log: $!\n";
      print OUT "$linodetime $gigabytespermonth\n";
      close OUT;
    }
  }
  $oldlocaltime = $localtime;
  $oldlocaltotal = $localtotal;


  print $text;
  if ($mail > 0 and defined($notify)) {
    `/bin/echo '$text' | /usr/bin/mail -s \"linode bandwidth usage at $mail% of quota\" $notify`;
    print "sent mail to $notify\n";
  }

  sleep $delay;
} 

