#!/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;