1
0
Fork 0
mirror of https://github.com/munin-monitoring/contrib.git synced 2025-07-22 02:51:03 +00:00
This commit is contained in:
Steve Schnepp 2012-01-12 15:31:22 +01:00
commit a2fead05ca
7 changed files with 2136 additions and 454 deletions

234
plugins/other/boinc_credit Executable file
View file

@ -0,0 +1,234 @@
#!/usr/bin/perl
# -*- cperl -*-
=head1 NAME
boinc_credit - Munin plugin to monitor BOINC credit for a user
=head1 APPLICABLE SYSTEMS
Any
=head1 CONFIGURATION
All that should be needed is to add the following to your config:
[boinc_credit]
env.cpid 1234abcd....
Where the value is your Cross Project ID (CPID).
=head1 MAGIC MARKERS
#%# family=auto contrib
#%# capabilities=autoconf
=head1 VERSION
1.0
=head1 AUTHOR
Paul Saunders <darac+munin@darac.org.uk>
=cut
use strict;
use warnings;
use lib $ENV{'MUNIN_LIBDIR'};
use Munin::Plugin;
my $CPID = $ENV{cpid};
my $STATSURL = $ENV{statsurl}
|| "http://boinc.netsoft-online.com/get_user.php?cpid=$CPID";
my $TICK = $ENV{tick} || 60; # minutes
my $ret;
if ( !eval "require XML::Simple;" ) {
$ret += "Could not load XML::Simple; ";
}
if ( !eval "require LWP::Simple;" ) {
$ret += "Could not load LWP::Simple; ";
}
if ( defined $ARGV[0] and $ARGV[0] eq 'autoconf' ) {
# Can't auto configure at the moment.
# At least, until we can calculate CPID
print "no\n";
exit 0;
}
my $lastread;
sub save_data {
my @projdata = @_;
# Do we need to save this data?
if ( !defined $lastread or time >= $lastread + ( $TICK * 60 ) ) {
$lastread = time;
my @save_vector;
push @save_vector, $lastread;
foreach (@projdata) {
# Serialise the hash
my @tempbuf;
foreach my $key ( keys %{$_} ) {
push @tempbuf, $key . '¬' . $_->{$key};
}
push @save_vector, join( '^^', @tempbuf );
}
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 @projarray;
foreach (@save_vector) {
my $hashref;
foreach ( split /\^\^/ ) {
my ( $key, $value ) = split /¬/;
$hashref->{$key} = $value;
}
push @projarray, $hashref;
}
if ( !defined $lastread or time >= ( $lastread + ( $TICK * 60 ) ) ) {
# Data is stale
eval {
# Fetch the XML
my $content;
unless ( defined( $content = LWP::Simple::get $STATSURL) ) {
die "Could not get $STATSURL";
}
my $xmlref = XML::Simple::XMLin( $content, ForceArray => 1 );
my @temparray;
foreach ( @{ $xmlref->{project} } ) {
my $temphash;
$temphash->{name} = $_->{name}[0];
$temphash->{id} = $_->{project_id}[0];
$temphash->{credit} = $_->{total_credit}[0];
$temphash->{creditfract} =
$_->{total_credit}[0] / $xmlref->{total_credit}[0];
$temphash->{totalcredit} = $xmlref->{total_credit}[0];
$temphash->{rank} = $_->{project_rank_total_credit}[0];
push @temparray, $temphash;
}
# If the above threw an error, we won't overwrite the old data
@projarray = @temparray;
1;
} or do {
print $@;
}
}
return @projarray;
}
# Project Colours from http://boinc.netsoft-online.com/e107_plugins/forum/forum_viewtopic.php?3
sub rgb($$$) {
return sprintf( '%02x%02x%02x', shift, shift, shift );
}
my %project_colour = (
'climatepredition.net' => rgb( 0, 139, 69 ),
'Predictor@Home' => rgb( 135, 206, 235 ),
'SETI@home' => rgb( 65, 105, 225 ),
'Einstein@Home' => rgb( 255, 165, 0 ),
'Rosetta@home' => rgb( 238, 130, 238 ),
'PrimeGrid' => rgb( 205, 197, 191 ),
'LHC@home' => rgb( 255, 127, 80 ),
'World Community Grid' => rgb( 250, 128, 114 ),
'BURP' => rgb( 0, 255, 127 ),
'SZTAKI Desktop Grid' => rgb( 205, 79, 57 ),
'uFluids' => rgb( 0, 0, 0 ),
'SIMAP' => rgb( 143, 188, 143 ),
'Folding@Home' => rgb( 153, 50, 204 ),
'MalariaControl' => rgb( 30, 144, 255 ),
'The Lattice Project' => rgb( 0, 100, 0 ),
'Pirates@Home' => rgb( 127, 255, 0 ),
'BBC Climate Change Experiment' => rgb( 205, 173, 0 ),
'Leiden Classical' => rgb( 140, 34, 34 ),
'SETI@home Beta' => rgb( 152, 245, 255 ),
'RALPH@Home' => rgb( 250, 240, 230 ),
'QMC@HOME' => rgb( 144, 238, 144 ),
'XtremLab' => rgb( 130, 130, 130 ),
'HashClash' => rgb( 255, 105, 180 ),
'cpdn seasonal' => rgb( 255, 255, 255 ),
'Chess960@Home Alpha' => rgb( 165, 42, 42 ),
'vtu@home' => rgb( 255, 0, 0 ),
'LHC@home alpha' => rgb( 205, 133, 63 ),
'TANPAKU' => rgb( 189, 183, 107 ),
'other' => rgb( 255, 193, 37 ),
'Rectilinear Crossing Number' => rgb( 83, 134, 139 ),
'Nano-Hive@Home' => rgb( 193, 205, 193 ),
'Spinhenge@home' => rgb( 255, 240, 245 ),
'RieselSieve' => rgb( 205, 183, 158 ),
'Project Neuron' => rgb( 139, 58, 98 ),
'RenderFarm@Home' => rgb( 210, 105, 30 ),
'Docking@Home' => rgb( 178, 223, 238 ),
'proteins@home' => rgb( 0, 0, 255 ),
'DepSpid' => rgb( 139, 90, 43 ),
'ABC@home' => rgb( 222, 184, 135 ),
'BOINC alpha test' => rgb( 245, 245, 220 ),
'WEP-M+2' => rgb( 0, 250, 154 ),
'Zivis Superordenador Ciudadano' => rgb( 255, 239, 219 ),
'SciLINC' => rgb( 240, 248, 255 ),
'APS@Home' => rgb( 205, 91, 69 ),
'PS3GRID' => rgb( 0, 139, 139 ),
'Superlink@Technion' => rgb( 202, 255, 112 ),
'BRaTS@Home' => rgb( 255, 106, 106 ),
'Cosmology@Home' => rgb( 240, 230, 140 ),
'SHA 1 Collision Search' => rgb( 255, 250, 205 ),
);
if ( defined $ARGV[0] and $ARGV[0] eq 'config' ) {
if ($ret) {
print $ret;
exit 1;
}
my @projdata = load_data();
print <<EOF;
graph_args --base 1000 --logarithmic
graph_vlabel Cobblestones
graph_category boinc
graph_title BOINC Total Credit
EOF
foreach ( sort { $a->{id} <=> $b->{id} } @projdata ) {
my $fieldname = 'proj' . $_->{id};
print <<EOF;
$fieldname.label $_->{name}
$fieldname.type GAUGE
$fieldname.info Total Credit for project $_->{name}
EOF
if ( exists $project_colour{ $_->{name} } ) {
print "$fieldname.colour $project_colour{$_->{name}}\n";
}
}
save_data(@projdata);
exit 0;
}
my @projdata = load_data();
foreach ( sort { $a->{id} <=> $b->{id} } @projdata ) {
my $fieldname = 'proj' . $_->{id};
print "$fieldname.value $_->{credit}\n";
printf "$fieldname.extinfo %.2f%% of Total Credit (%.2f out of %.2f)\n",
$_->{creditfract} * 100, $_->{credit}, $_->{totalcredit};
}
save_data(@projdata);
exit 0;

View file

@ -226,6 +226,7 @@ if ( (defined $ARGV[0]) && ($ARGV[0] eq "config") ) {
print "graph_category BOINC\n";
print "graph_args --base 1000 -l 0\n";
print "graph_vlabel BOINC applications\n";
print "graph_total Total\n";
# First project is AREA, next are STACK
# Not nice, but fast:

View file

@ -37,17 +37,18 @@ Time::Local
This configuration section shows the defaults of the plugin:
[currentcost]
env.device /dev/ttyUSB0
env.baud 2400
env.tick 6
env.currency £
env.rate1 13.9
env.rate1qty 900
env.rate2 8.2
env.nightrate 0
env.nighthours 23:30-06:30
env.standingcharge 0.0
[currentcost]
env.device /dev/ttyUSB0
env.baud 2400
env.tick 6
env.currency £
env.rate1 13.9
nenv.rate1qty 900
env.rate2 8.2
env.nightrate 0
env.nighthours 23:30-06:30
env.standingcharge 0.0
env.metertype CC128
The configuration can be broken down into the following subsections:
@ -101,16 +102,20 @@ The time period for which C<env.nightrate> applies. This should be of the form C
The standing charge in hundreths of a C<env.currency> per month. If you do not have a standing charge, set this to 0.
=item env.metertype
The type of the meter. Currently "CC128" and "CC02" are supported.
=back
=head1 MAGIC MARKERS
#%# family=manual contrib
#%# capabilities=multigraph
#%# family=auto contrib
#%# capabilities=multigraph autoconf
=head1 AUTHOR
Paul Saunders L<darac@darac.org.uk>
Paul Saunders L<darac+munin@darac.org.uk>
=cut
@ -118,24 +123,25 @@ use strict;
use warnings;
use utf8;
use Munin::Plugin;
use Data::Dump qw{pp};
need_multigraph();
my $device_node = $ENV{device} || "/dev/ttyUSB0";
my $baud_rate = $ENV{baud} || "2400"; # or 9600 or 57600
my $tick_rate = $ENV{tick} || "6";
my $device_node = $ENV{device} || "/dev/ttyUSB0";
my $baud_rate = $ENV{baud} || "2400"; # or 9600 or 57600
my $tick_rate = $ENV{tick} || "6";
# Tick_Rate is how long to consider data valid for (in seconds)
# Costs
my $currency = $ENV{currency} || "£"; # £ or €
my $rate1 = $ENV{rate1} || "13.9"; # in pence/cents
my $rate1qty = $ENV{rate1qty} || "900"; # in kWh, 0 to use fixed rate2
my $rate2 = $ENV{rate2} || "8.2"; # in pence/cents
my $nightrate = $ENV{nightrate} || "0"; # 0 = disabled
my $nighthours = $ENV{nighthours} || "23:30-06:30";
my $standingcharge = $ENV{standingcharge} || "0.0"; # pence/cents per month
my $currency = $ENV{currency} || "£"; # £ or €
my $rate1 = $ENV{rate1} || "13.9"; # in pence/cents
my $rate1qty = $ENV{rate1qty} || "900"; # in kWh, 0 to use fixed rate2
my $rate2 = $ENV{rate2} || "8.2"; # in pence/cents
my $nightrate = $ENV{nightrate} || "0"; # 0 = disabled
my $nighthours = $ENV{nighthours} || "23:30-06:30";
my $standingcharge = $ENV{standingcharge} || "0.0"; # pence/cents per month
my $metertype = $ENV{metertype} || "CC128"; # or "CC02"
my $MUNIN_DEBUG = $ENV{MUNIN_DEBUG} || 0; # Set by munin-run
my $ret;
if ( !eval "require XML::Simple;" ) {
@ -168,6 +174,10 @@ sub save_data {
if ( !@lastread
or time >= Time::Local::timelocal(@lastread) + ($tick_rate) )
{
print "# Saving Data (Data is "
. ( time - Time::Local::timelocal(@lastread) )
. " seconds old)\n"
if $MUNIN_DEBUG;
@lastread = localtime(time);
my @save_vector;
@ -198,12 +208,27 @@ sub load_data {
{
# Data is stale
print "# Data is stale ("
. ( time - Time::Local::timelocal(@lastread) )
. " seconds). Re-reading device\n"
if $MUNIN_DEBUG;
eval {
# Fetch the XML
my @temparray = collect_cc128_data( $device_node, $baud_rate );
my @temparray;
if ( $metertype eq "CC128" ) {
@temparray = collect_cc128_data( $device_node, $baud_rate );
}
elsif ( $metertype eq "CC02" ) {
@temparray = collect_cc02_data( $device_node, $baud_rate );
}
else {
die "Unknown meter type $metertype.";
}
# Read the time so we know whether to reset daily/monthly/yearly counters
# Read the time so we know whether to reset
# daily/monthly/yearly counters
my @now = localtime(time);
my %is_new;
$is_new{daily} = ( $now[3] != $lastread[3] ) ? 1 : 0;
@ -222,6 +247,8 @@ sub load_data {
if ( $is_new{$period} ) {
# Start of a new period, reset the counter
print "# Start of a new $period period." .
" Resetting counter\n" if $MUNIN_DEBUG;
$temparray[$i]->{data}[$j]->{$period} = 0;
}
else {
@ -266,9 +293,11 @@ sub is_night_rate {
if ( $now_time >= $start_time
or $now_time <= $stop_time )
{
print "# Night rate is ACTIVE\n" if $MUNIN_DEBUG;
return 1;
}
else {
print "# Night rate is enabled, but NOT active\n" if $MUNIN_DEBUG;
return 0;
}
}
@ -279,34 +308,34 @@ The device will periodically output a string of XML. The string will be all on o
=head2 Classic format
Note: I can't find an official spec for this format so, for now, this plugin doesn't support it.
As per L<http://cumbers.wordpress.com/2008/05/07/breakdown-of-currentcost-xml-output/>:
<msg>
<date>
<dsb>00014</dsb>
<hr>14</hr>
<dsb>00014</dsb> days since birth
<hr>14</hr> the time
<min>07</min>
<sec>07</sec>
</date>
<src>
<name>CC02</name>
<id>03280</id>
<type>1</type>
<sver>0.07</sver>
<name>CC02</name> name of this device
<id>03280</id> communication channel for device
<type>1</type> hardware version of the device
<sver>0.07</sver> software version
</src>
<ch1>
<watts>00080</watts>
<watts>00080</watts> value from the first channel clamp
</ch1>
<ch2>
<watts>00000</watts>
<watts>00000</watts> value from the second channel clamp
</ch2>
<ch3>
<watts>00000</watts>
<watts>00000</watts> value from the third channel clamp
</ch3>
<tmpr>28.8</tmpr>
<tmpr>28.8</tmpr> current temperature (degrees celsius)
<hist>
<hrs>
<h02>000.0</h02>
<h02>000.0</h02> total Kwh used in 2 hour blocks
<h04>000.1</h04>
<h06>000.1</h06>
<h08>000.0</h08>
@ -321,7 +350,7 @@ Note: I can't find an official spec for this format so, for now, this plugin doe
<h26>000.0</h26>
</hrs>
<days>
<d01>0000</d01>
<d01>0000</d01> total Kwh used per day(s)
<d02>0000</d02>
<d03>0000</d03>
<d04>0000</d04>
@ -354,7 +383,7 @@ Note: I can't find an official spec for this format so, for now, this plugin doe
<d31>0000</d31>
</days>
<mths>
<m01>0000</m01>
<m01>0000</m01> total Kwh used per month(s)
<m02>0000</m02>
<m03>0000</m03>
<m04>0000</m04>
@ -368,7 +397,7 @@ Note: I can't find an official spec for this format so, for now, this plugin doe
<m12>0000</m12>
</mths>
<yrs>
<y1>0000000</y1>
<y1>0000000</y1> total Kwh used per year(s)
<y2>0000000</y2>
<y3>0000000</y3>
<y4>0000000</y4>
@ -419,15 +448,18 @@ sub collect_cc128_data {
my %seen_sensors;
while (<$ttydev>) {
print "# Read from device: $_\n" if $MUNIN_DEBUG;
if (m{(<msg>.*</msg>)}) {
my $xmlref = XML::Simple::XMLin( $1, KeepRoot => 1 );
my $sensor = $xmlref->{msg}->{sensor};
print "# Parsing Sensor $sensor\n" if $MUNIN_DEBUG;
next unless defined $sensor;
if ( defined $seen_sensors{$sensor} ) {
# We've seen this sensor before.
# Time to stop reading data
print "# Hello again, Sensor $sensor\n" if $MUNIN_DEBUG;
last;
}
$seen_sensors{$sensor} = 1;
@ -436,6 +468,56 @@ sub collect_cc128_data {
$temphash->{sensor} = $sensor;
$temphash->{temp} = $xmlref->{msg}->{tmpr};
my @temparr;
foreach my $key ( keys %{ $xmlref->{msg} } ) {
if ( $key =~ /ch(\d+)/ ) {
my $channel = $1;
my $unit = ( keys %{ $xmlref->{msg}->{"ch$channel"} } )[0];
my $val = $xmlref->{msg}->{"ch$channel"}->{$unit};
print "# Channel $channel, Unit $unit, Value $val\n"
if $MUNIN_DEBUG;
push @temparr,
{
"channel" => $channel,
"unit" => $unit,
"value" => $val
};
}
}
$temphash->{data} = \@temparr;
push @cc_data_arr, $temphash;
}
}
close($ttydev);
return @cc_data_arr;
}
sub collect_cc02_data {
# Function supplied by David Edmondson <dme@dme.org>
# Read data from the serial port
my ( $port, $baud ) = @_;
my $tty = Device::SerialPort->new($port) || die "Can't open $port: $!";
$tty->baudrate($baud) || die "Can't set serial baudrate";
$tty->parity("none") || die "Can't set serial parity";
$tty->databits(8) || die "Can't set serial databits";
$tty->handshake("none") || die "Can't set serial handshake";
$tty->write_settings || die "Can't set serial parameters";
open( my $ttydev, "<", $port ) || die "Can't open $port: $?";
my @cc_data_arr;
while (<$ttydev>) {
if (m{(<msg>.*</msg>)}) {
my $xmlref = XML::Simple::XMLin( $1, KeepRoot => 1 );
my $temphash;
$temphash->{sensor} = 0; # Only one sensor.
$temphash->{temp} = $xmlref->{msg}->{tmpr};
my @temparr;
foreach my $key ( keys %{ $xmlref->{msg} } ) {
if ( $key =~ /ch(\d+)/ ) {
my $channel = $1;
@ -452,6 +534,8 @@ sub collect_cc128_data {
$temphash->{data} = \@temparr;
push @cc_data_arr, $temphash;
last;
}
}
close($ttydev);
@ -475,7 +559,7 @@ if ( defined $ARGV[0] and $ARGV[0] eq 'config' ) {
}
elsif ( $unit != $key->{unit} ) {
print STDERR
"Conflicting units ($unit and $key->{unit}) on sensor $datum->{sensor}";
"# Conflicting units ($unit and $key->{unit}) on sensor $datum->{sensor}";
}
}
@ -512,9 +596,10 @@ ${fieldname}_n.type GAUGE
${fieldname}_n.min 0
${fieldname}_t.cdef ${fieldname}_n,UN,${fieldname},${fieldname},IF,3600,TRENDNAN
EOF
} else {
print "${fieldname}_t.cdef ${fieldname},3600,TRENDNAN\n";
}
}
else {
print "${fieldname}_t.cdef ${fieldname},3600,TRENDNAN\n";
}
}
# Output the Root cumulative graph
@ -542,7 +627,7 @@ ${fieldname}_d.min 0
EOF
}
print "\n$confstr";
print <<EOF;
print <<EOF;
fudge.label (fudge)
fudge.type GAUGE
fudge.graph yes

View file

@ -1,9 +1,44 @@
#!/bin/bash
: <<=cut
=head1 NAME
sa-learn - Munin plugin to monitor spamassasin bayes database size
=head1 APPLICABLE SYSTEMS
Any server running spamassassin
=head1 CONFIGURATION
This plugin assumes your Spamassassin database is in /var/lib/MailScanner.
If it's elsewhere (or you want to use a different database) you will need
a configuration such as:
[sa-learn]
env.BayesDir /path/to/bayes/directory/
user mail
(where 'user mail' refers to a user that can read the database).
=head1 MAGIC MARKERS
#%# family=contrib
=head1 VERSION
2
=head1 AUTHOR
Paul Saunders L<darac+munin@darac.org.uk>
=cut
#'
case $1 in
config)
cat <<'EOM'
config)
cat <<'EOM'
graph_title SA-Learn Magic
graph_vlabel Count
graph_args --base 1000 -l 0
@ -15,26 +50,26 @@ ham.type GAUGE
tokens.label Num Tokens
tokens.type GAUGE
EOM
exit 0;;
exit 0;;
esac
## Print values
BayesDir=${BayesDir:-/var/lib/MailScanner}
sa-learn --dbpath $BayesDir --dump magic 2>/dev/null | while read line
sa-learn --dbpath $BayesDir/ --dump magic 2>/dev/null | while read line
do
case "$line" in
*nspam*)
echo -n "spam.value "
echo $line | awk '{print $3}'
;;
*nham*)
echo -n "ham.value "
echo $line | awk '{print $3}'
;;
*ntokens*)
echo -n "tokens.value "
echo $line | awk '{print $3}'
;;
esac
case "$line" in
*nspam*)
echo -n "spam.value "
echo $line | awk '{print $3}'
;;
*nham*)
echo -n "ham.value "
echo $line | awk '{print $3}'
;;
*ntokens*)
echo -n "tokens.value "
echo $line | awk '{print $3}'
;;
esac
done

File diff suppressed because it is too large Load diff

67
plugins/other/xfs_frag Executable file
View file

@ -0,0 +1,67 @@
#!/bin/bash
: <<=cut
=head1 NAME
xfs_frag - Munin plugin to monitor the fragmentation level on your XFS filesystems
=head1 APPLICABLE SYSTEMS
Any machine with an XFS file system.
=head1 CONFIGURATION
None, generally, but you may want to run as root and set a timeout.
[xfs_frag]
user root
timeout 90
=head1 MAGIC MARKERS
#%# family=auto contrib
#%# capabilities=
=head1 VERSION
1
=head1 AUTHOR
Paul Saunders L<darac+munin@darac.org.uk>
=cut
declare -a ARRY
shopt -s nocasematch
case $1 in
config)
cat <<'EOF'
graph_title XFS fragmentation
graph_vlabel Percent
graph_category disk
EOF
cat /etc/mtab | awk '{print $2 " " $3}' | while read LINE
do
ARRY=($LINE)
if [[ ${ARRY[1]} =~ xfs ]]; then
FIELDNAME=$(echo ${ARRY[0]} | sed 's/^[^A-Za-z_]/_/; s/[^A-Za-z0-9_]/_/g')
echo "$FIELDNAME.label ${ARRY[0]}"
echo "$FIELDNAME.type GAUGE"
fi
done
exit 0
;;
esac
cat /etc/mtab | awk '{print $2 " " $3 " " $1}' | while read LINE
do
ARRY=($LINE)
if [[ ${ARRY[1]} =~ xfs ]]; then
FIELDNAME=$(echo ${ARRY[0]} | sed 's/^[^A-Za-z_]/_/; s/[^A-Za-z0-9_]/_/g')
FRAG=$(xfs_db -c frag -r ${ARRY[2]} | sed 's/.*fragmentation factor \(.*\)%.*/\1/')
echo $FIELDNAME.value $FRAG
fi
done

View file

@ -1,235 +1,243 @@
#!/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
Paul Saunders L<darac@darac.org.uk>
=cut
use strict;
use warnings;
use lib $ENV{'MUNIN_LIBDIR'};
use Munin::Plugin;
use Data::Dump qw(pp);
my $ret = undef;
# Load modules like so
if ( !eval "require zenus;" ) {
$ret =
"Could not load zenus. Get it from http://www.rachaelandtom.info/zenus";
}
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
my $lastread;
sub save_data {
my $hashref = shift;
# Do we need to save this info
if ( time > $lastread + ( $TICK * 60 ) ) {
$lastread = time;
my @save_vector;
push @save_vector, $lastread;
# Push the hash values on to the array
foreach ( keys %$hashref ) {
push @save_vector, $_ . '¬' . $hashref->{$_};
}
#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 /¬/;
$hashref->{$key} = $value;
}
if ( !defined $lastread or time >= ( $lastread + ( $TICK * 60 ) ) ) {
# Data is stale
#print STDERR "REFRESHING DATA\n";
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;
# If zenus threw an error we won't copy the data over,
# so we still use the cached data.
};
}
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 $cap = sprintf( "%.3f", $data->{avail} );
my $warn = sprintf( "%.3f", $cap * 0.25 );
my $crit = sprintf( "%.3f", $cap * 0.1 );
print <<EOF;
graph_args --base 1000
graph_vlabel GBytes
graph_category Network
graph_title Zen Broadband Usage
graph_info Usage of your Zen Broadband account: $data->{name}
download.label Downloaded
download.type GAUGE
download.info Amount of data downloaded (Limit is $cap GB)
download.draw AREA
upload.label Uploaded
upload.type GAUGE
upload.info Amount of data uploaded (No upper limit)
upload.draw AREA
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:
graph_order download upload allowance remaining overrate
EOF
save_data($data);
exit 0;
}
my $data = load_data();
print "upload.value " . $data->{uploadAmount} . "\n";
print "download.value " . $data->{used} . "\n";
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";
save_data($data);
exit 0;
#!/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
Paul Saunders L<darac+munin@darac.org.uk>
=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";
}
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
my $lastread;
sub save_data {
my $hashref = shift;
# Do we need to save this info
if ( time > $lastread + ( $TICK * 60 ) ) {
$lastread = time;
my @save_vector;
push @save_vector, $lastread;
# Push the hash values on to the array
foreach ( keys %$hashref ) {
push @save_vector, $_ . '¬' . $hashref->{$_};
}
#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 /¬/;
$hashref->{$key} = $value;
}
if ( !defined $lastread or time >= ( $lastread + ( $TICK * 60 ) ) ) {
# Data is stale
#print STDERR "REFRESHING DATA\n";
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;
# If zenus threw an error we won't copy the data over,
# so we still use the cached data.
};
}
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 $cap = sprintf( "%.3f", $data->{avail} );
my $warn = sprintf( "%.3f", $cap * 0.25 );
my $crit = sprintf( "%.3f", $cap * 0.1 );
my $datestr = scalar( localtime($lastread) );
print <<EOF;
graph_args --base 1000
graph_vlabel GBytes out (-) / in (+)
graph_category Network
graph_title Zen Broadband Usage
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.info Amount of data downloaded (Limit is $cap GB)
download.draw AREA
download.extinfo Last Read was $datestr
download.negative upload
download.colour 00CC00EE
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:
#graph_order download upload allowance remaining overrate
EOF
save_data($data);
exit 0;
}
my $data = load_data();
print "upload.value " . $data->{uploadAmount} . "\n";
print "download.value " . $data->{used} . "\n";
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";
save_data($data);
exit 0;