235 lines
7.3 KiB
Plaintext
235 lines
7.3 KiB
Plaintext
|
#!/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;
|