2012-01-12 13:46:25 +01:00
#!/usr/bin/perl
# -*- cperl -*-
=head1 NAME
zenus_ - Munin plugin to monitor the usage of a Zen Internet Broadband account
=head1 APPLICABLE SYSTEMS
Any (Though most likely those connected to a Zen Internet Broadband connection)
=head1 CONFIGURATION
This plugin requires the C<zenus> module to be installed. This can be fetched
from L<http://www.rachaelandtom.info/zenus>.
Tip: To see if it's already setup correctly, just run this plugin
with the parameter 'autoconf'. If you get a "yes", everything should
work like a charm already.
This configuration section shows the defaults of the plugin:
[zenus_*]
env.user
env.pass
env.account
env.tick 60
You must supply C<env.user> and C<env.pass>. These will be your PORTAL username and password, NOT your ADSL account details.
You may either specify the account to report through C<env.account> (e.g. zen12345@zen) or by naming the symlink in your plugins directory (e.g. zenus_zen12345_zen). If no account is specified, the default account will be chosen.
C<env.tick> specifies how often (in minutes) the data will be refreshed from upstream. This is to avoid hitting the Zen servers every five minutes. Data is cached between runs.
=head1 INTERPRETATION
The plugin shows the amount of data downloaded and uploaded since the beginning of the chargable period (i.e. the calendar month). A thick line shows the current download allowance (planned plus banked). A thinner line shows the estimated amount remaining at the end of the month. If you are estimated to exceed your allowance before the end of the month, a second line appears showing the rate at which you're downloading beyond the sustainable rate.
Warnings are sent if your estimated allowance drops below 25% of your cap and if the overspeed rate rises above zero. Critical warnings are sent if your estimated allowance drops below 10% of your cap.
=head1 MAGIC MARKERS
#%# family=auto contrib
#%# capabilities=autoconf suggest
=head1 AUTHOR
2012-01-12 13:50:02 +01:00
Paul Saunders L<darac+munin@darac.org.uk>
2012-01-12 13:46:25 +01:00
=cut
use strict;
use warnings;
use lib $ENV{'MUNIN_LIBDIR'};
use Munin::Plugin;
use Data::Dump qw(pp);
use Time::Local;
my $ret = undef;
# Load modules like so
if ( !eval "require zenus;" ) {
$ret =
"Could not load zenus. Get it from http://www.rachaelandtom.info/zenus\n";
$ret .= "(BTW, \@INC is: " . join( ', ', @INC ) . ")\n";
}
2015-07-20 11:23:32 +02:00
my $CAN_LOG = 1;
if ( !eval "require Log::Log4perl;" ) {
$CAN_LOG = 0;
}
elsif ( !eval "require Log::Dispatch::FileRotate;" ) {
$CAN_LOG = 0;
}
2012-01-12 13:46:25 +01:00
my $USER = $ENV{user};
my $PASS = $ENV{pass};
my $ACCT = $ENV{account} || "";
my $TICK = $ENV{tick} || 60; # minutes
my @name_fields = split /_/, $0;
if ( scalar @name_fields == 3 ) {
if ( $name_fields[1] eq '' or $name_fields[2] eq '' ) {
print "Misconfigured symlink. See Documentation\n";
exit 1;
}
else {
$ACCT = $name_fields[1] . '@' . $name_fields[2];
}
}
# If there are more or less than 3 components to the filename,
# we just carry on with the default account
2015-07-20 11:23:32 +02:00
if ($CAN_LOG) {
my $loggerconf = q(
log4perl.rootLogger = DEBUG, LOG1
log4perl.appender.LOG1 = Log::Dispatch::FileRotate
log4perl.appender.LOG1.filename = /var/log/munin/zenus.log
log4perl.appender.LOG1.mode = append
log4perl.appender.LOG1.DatePattern = yyyy-ww
log4perl.appender.LOG1.layout = Log::Log4perl::Layout::PatternLayout
log4perl.appender.LOG1.layout.ConversionPattern = %d %p %c: %m%n
);
Log::Log4perl::init( \$loggerconf );
our $logger = Log::Log4perl->get_logger($ACCT);
2015-07-20 11:45:54 +02:00
} else {
2015-07-20 12:03:25 +02:00
our $logger = "";
2015-07-20 11:23:32 +02:00
}
2012-01-12 13:46:25 +01:00
my $lastread;
sub save_data {
my $hashref = shift;
2015-07-20 11:23:32 +02:00
my $update_lastread = shift || 1;
2012-01-12 13:46:25 +01:00
# Do we need to save this info
2015-07-20 11:23:32 +02:00
if ( !defined $lastread or time > $lastread + ( $TICK * 60 ) ) {
2012-01-12 13:46:25 +01:00
2015-07-20 11:23:32 +02:00
$lastread = time if $update_lastread;
print "# Updating LastRead to " . localtime($lastread) . "\n";
2012-01-12 13:46:25 +01:00
my @save_vector;
push @save_vector, $lastread;
# Push the hash values on to the array
foreach ( keys %$hashref ) {
push @save_vector, $_ . '<27> ' . $hashref->{$_};
}
2015-07-20 12:03:25 +02:00
$::logger->info(
2015-07-20 11:23:32 +02:00
"Saving Data (LastRead is " . localtime($lastread) . "\n" )
if $CAN_LOG;
2012-01-12 13:46:25 +01:00
#Go!
save_state(@save_vector);
}
}
sub load_data {
# Bring the data back in
my @save_vector = restore_state();
# Read the timestamp. Do we need to refresh the data?
$lastread = shift @save_vector;
my $hashref;
foreach (@save_vector) {
my ( $key, $value ) = split /<2F> /;
$hashref->{$key} = $value;
}
2015-07-20 11:23:32 +02:00
my $force_save = 0;
2012-01-12 13:46:25 +01:00
if ( !defined $lastread or time >= ( $lastread + ( $TICK * 60 ) ) ) {
# Data is stale
2015-07-20 11:23:32 +02:00
print "# Data is " . ( time - $lastread ) . " seconds old\n";
2015-07-20 12:03:25 +02:00
$::logger->info( "Data is " . ( time - $lastread ) . " seconds old\n" )
2015-07-20 11:23:32 +02:00
if $CAN_LOG;
2012-01-12 13:46:25 +01:00
2015-07-20 11:23:32 +02:00
if ( exists $hashref->{backoff} ) {
if ( time <= $hashref->{backoff} ) {
# We're in a back-off period
print "# Back-off in effect\n";
2015-07-20 12:03:25 +02:00
$::logger->info("Back-off in effect\n") if $CAN_LOG;
2015-07-20 11:23:32 +02:00
exit 0;
}
else {
# We've just come out of a back-off period
print "# Back-off ends\n";
2015-07-20 12:03:25 +02:00
$::logger->info("Back-off ends\n") if $CAN_LOG;
2015-07-20 11:23:32 +02:00
delete $hashref->{backoff};
}
}
elsif ( !defined $lastread ) {
# Initial run. Carry on
print "# Initial Run\n";
my $force_save = 1;
}
elsif ( time >= ( $lastread + ( 120 * 60 ) ) ) {
# Data is VERY Stale. Assume a login issue and
# back-off for 24 hours.
print "# Starting Back-Off\n";
$hashref->{backoff} = time + ( 24 * 60 * 60 );
2015-07-20 12:03:25 +02:00
$::logger->info(
2015-07-20 11:23:32 +02:00
"Backing off until " . localtime( $hashref->{backoff} ) . "\n" )
if $CAN_LOG;
save_data( $hashref, 0 );
exit 1;
}
print "# REFRESHING DATA\n";
2015-07-20 12:03:25 +02:00
$::logger->info("Refreshing Data\n") if $CAN_LOG;
2012-01-12 13:46:25 +01:00
my $temphash;
eval {
zenus::login( $USER, $PASS );
(
$temphash->{name}, $temphash->{used},
$temphash->{avail}, $temphash->{per},
$temphash->{uploadAmount}
) = zenus::getUsage($ACCT);
(
$temphash->{dayofmonth}, $temphash->{permonth},
$temphash->{avedaily}, $temphash->{maxdaily},
$temphash->{daysremain}, $temphash->{estusage},
$temphash->{daysinmonth}
)
= zenus::estimateRemaining( $temphash->{used},
$temphash->{avail} );
$hashref = $temphash;
2015-07-20 11:23:32 +02:00
1;
2012-01-12 13:46:25 +01:00
# If zenus threw an error we won't copy the data over,
# so we still use the cached data.
2015-07-20 11:23:32 +02:00
} or do {
print "# Zenus Error $@\n";
2012-01-12 13:46:25 +01:00
};
}
2015-07-20 11:23:32 +02:00
else {
print "# Using existing data\n";
2015-07-20 12:03:25 +02:00
$::logger->info("Using existing data\n") if $CAN_LOG;
2015-07-20 11:23:32 +02:00
}
save_data( $hashref, 1 ) if $force_save;
2012-01-12 13:46:25 +01:00
return $hashref;
}
if ( defined $ARGV[0] and $ARGV[0] eq "autoconf" ) {
if ($ret) {
print "no ($ret)\n";
exit 1;
}
print "yes\n";
exit 0;
}
if ( defined $ARGV[0] and $ARGV[0] eq "suggest" ) {
if ( defined $USER and defined $PASS ) {
zenus::login( $USER, $PASS );
for my $account ( zenus::getAccounts() ) {
if ( defined $account ) {
$account =~ s/\@/_/g;
print "$account\n";
}
}
}
exit 0;
}
if ( defined $ARGV[0] and $ARGV[0] eq "config" ) {
if ($ret) {
print $ret;
exit 1;
}
my $data = load_data();
my $datestr = scalar( localtime($lastread) );
print <<EOF;
graph_args --base 1000
graph_vlabel GBytes out (-) / in (+)
2018-08-01 23:56:56 +02:00
graph_category network
2015-07-20 11:23:32 +02:00
graph_title Zen Broadband Usage ($ACCT)
2012-01-12 13:46:25 +01:00
graph_info Usage of your Zen Broadband account: $data->{name}
upload.label Uploaded
upload.type GAUGE
upload.info Amount of data uploaded (No upper limit)
upload.draw AREA
upload.graph no
upload.colour 00CC00EE
download.label Transfer
download.type GAUGE
download.draw AREA
download.extinfo Last Read was $datestr
download.negative upload
download.colour 00CC00EE
2015-07-20 11:23:32 +02:00
EOF
if ( defined $data->{avail} and $data->{avail} != 0 ) {
my $cap = sprintf( "%.3f", $data->{avail} );
my $warn = sprintf( "%.3f", $cap * 0.25 );
my $crit = sprintf( "%.3f", $cap * 0.1 );
print <<EOF;
download.info Amount of data downloaded (Limit is $cap GB)
2012-01-12 13:46:25 +01:00
allowance.label Allowance
allowance.type GAUGE
allowance.info Amount of data you are allowed to download this month
allowance.draw LINE2
remaining.label Est'd Remaining
remaining.type GAUGE
remaining.info Estimated amount of data transfer remaining at the end of the month
remaining.draw LINE1
remaining.min 0
remaining.max $cap
remaining.warning $warn:
remaining.critical $crit:
overrate.label Overspeed Rate
overrate.type GAUGE
overrate.info Rate at which you're downloading beyond the sustainable rate
overrate.draw LINE1
overrate.min 0
overrate.warning 0:
EOF
2015-07-20 11:23:32 +02:00
}
else {
# Probably an unlimited contract
print "download.info Amount of data downloaded\n";
}
2012-01-12 13:46:25 +01:00
save_data($data);
exit 0;
}
my $data = load_data();
print "upload.value " . $data->{uploadAmount} . "\n";
print "download.value " . $data->{used} . "\n";
2015-07-20 11:23:32 +02:00
if ( defined $data->{avail} and $data->{avail} != 0 ) {
print "allowance.value " . $data->{avail} . "\n";
my $remain = $data->{avail} - $data->{estusage};
$remain = 0 if $remain < 0;
print "remaining.value " . $remain . "\n";
my $overrate = $data->{avedaily} - $data->{maxdaily};
$overrate = 0 if $overrate < 0;
print "overrate.value " . $overrate . "\n";
}
2012-01-12 13:46:25 +01:00
save_data($data);
exit 0;