diff --git a/plugins/other/boinc_credit b/plugins/other/boinc_credit new file mode 100755 index 00000000..b21472bb --- /dev/null +++ b/plugins/other/boinc_credit @@ -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 + +=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 <{id} <=> $b->{id} } @projdata ) { + my $fieldname = 'proj' . $_->{id}; + print <{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;