Repository
Munin (contrib)
Last change
2018-08-02
Graph Categories
Family
contrib
Keywords
Language
Perl
License
GPL-3.0-only
Authors

boinc_wus

Name

boinc_wus - Munin plugin to monitor states of all BOINC WUs

Applicable Systems

Linux machines running BOINC and munin-node

- or -

Linux servers (running munin-node) used to collect data from other systems which are running BOINC, but not running munin-node (e.g. non-Linux systems)

Configuration

Following configuration variables are supported:

  • boinccmd

    command-line control program (default: boinccmd)

  • host

    Host to query (default: none)

  • port

    GUI RPC port (default: none = use BOINC-default)

  • boincdir

    Directory containing appropriate file gui_rpc_auth.cfg (default: none)

  • verbose

    Display unusual states details (default: 0 = Summarize unusual states as other)

  • password

    Password for BOINC (default: none)

Security Consideration:

Using of variable password poses a security risk. Even if the Munin configuration file for this plugin containing BOINC-password is properly protected, the password is exposed as environment variable and finally passed to boinccmd as a parameter. It is therefore possible for local users of the machine running this plugin to eavesdrop the BOINC password.

Using of variable password is therefore strongly discouraged and is left here as a legacy option and for testing purposes.

It should be always possible to use boincdir variable instead - in such case the file gui_rpc_auth.cfg is read by boinccmd binary directly. If this plugin is used to fetch data from remote system, the gui_rpc_auth.cfg can be copied to special directory in a secure way (e.g. via scp) and properly protected by file permissions.

Interpretation

This plugin shows how many BOINC workunits are in all the various states. The most important states Running, Preempted, Suspended, Ready to run, Ready to report, Downloading and Uploading are always displayed. All other states are shown as other.

If the variable verbose is used, additionally also states Computation Error and Aborted are shown separately (they are included in other otherwise).

Examples

Local Boinc Example

BOINC is running on local machine. The BOINC binaries are installed in /opt/boinc/custom-6.10.1/, the BOINC is running in directory /usr/local/boinc/ under username boinc, group boinc and the password is used to protect access to BOINC:

[boinc_*]
group boinc
env.boinccmd /opt/boinc/custom-6.10.1/boinccmd
env.boincdir /usr/local/boinc
env.verbose 1

Remote Boinc Example

BOINC is running on 2 remote machines foo and bar. On the local machine the binary of command-line interface is installed in directory /usr/local/bin/. The BOINC password used on the remote machine foo is stored in file /etc/munin/boinc/foo/gui_rpc_auth.cfg. The BOINC password used on the remote machine bar is stored in file /etc/munin/boinc/bar/gui_rpc_auth.cfg. These files are owned and readable by root, readable by group munin and not readable by others. There are 2 symbolic links to this plugin created in the munin plugins directory (usually /etc/munin/plugins/): snmp_foo_boincwus and snmp_bar_boincwus

[snmp_foo_boinc*]
group munin
env.boinccmd /usr/local/bin/boinccmd
env.host foo
env.boincdir /etc/munin/boinc/foo

[snmp_bar_boinc*]
group munin
env.boinccmd /usr/local/bin/boinccmd
env.host bar
env.boincdir /etc/munin/boinc/bar

This way the plugin can be used by Munin the same way as the Munin plugins utilizng SNMP (although this plugin itself does not use SNMP).

Bugs

There is no autoconf capability at the moment. This is due to the fact, that BOINC installations may vary over different systems, sometimes using default directory from distribution (e.g. /var/lib/boinc/ in Debian or Ubuntu), but often running in user directories or in other separate directories. Also the user-ID under which BOINC runs often differs. Under these circumstances the autoconf would be either lame or too complicated.

Author

Palo M. palo.gm@gmail.com

License

GPLv3 http://www.gnu.org/licenses/gpl-3.0.txt

#!/usr/bin/perl -w
#
# boinc_wus - Munin plugin to monitor states of all BOINC WUs
#
# Run 'perldoc boinc_wus' for full man page
#
# Author:  Palo M. <palo.gm@gmail.com>
# Modified by: Paul Saunders <darac+munin@darac.org.uk>
# License: GPLv3 <http://www.gnu.org/licenses/gpl-3.0.txt>
#
#
# Parameters supported:
# 	config
#
#
# Configurable variables
#       boinccmd   - command-line control program (default: boinc_cmd)
# 	host       - Host to query (default: none)
#       port       - GUI RPC port (default: none = use BOINC-default)
#       boincdir   - Directory containing appropriate password file
#                    gui_rpc_auth.cfg (default: none)
#       verbose    - Whether display more detailed states (default: 0)
#       password   - Password for BOINC (default: none) !!! UNSAFE !!!
#
#
# $Log$
#
# Revision 1.1  2011/03/22  Paul Saunders
#   Update for BOINC 6.12
#   Add colours from http://boinc.netsoft-online.com/e107_plugins/forum/forum_viewtopic.php?3
# Revision 1.0  2009/09/13  Palo M.
#   Add documentation and license information
#   Ready to publish on Munin Exchange
# Revision 0.9  2009/09/13  Palo M.
#   Add possibility to read password from file
# Revision 0.8  2009/09/12  Palo M.
#   Update default binary name: boinc_cmd -> boinccmd
# Revision 0.7  2008/08/29  Palo M.
#   Creation - Attempt to port functionality from C++ code
#
# (Revisions 0.1 - 0.6) were done in C++
#
#
#
# Magic markers:
#%# family=contrib

use strict;


#########################################################################
# 1. Parse configuration variables
#
my $BOINCCMD = exists $ENV{'boinccmd'} ? $ENV{'boinccmd'} : "boinccmd";
my $HOST = exists $ENV{'host'} ? $ENV{'host'} : undef;
my $PORT = exists $ENV{'port'} ? $ENV{'port'} : undef;
my $PASSWORD = exists $ENV{'password'} ? $ENV{'password'} : undef;
my $BOINCDIR = exists $ENV{'boincdir'} ? $ENV{'boincdir'} : undef;
my $VERBOSE = exists $ENV{'verbose'} ? $ENV{'verbose'} : "0";

#########################################################################
# 2. Basic executable
#
if (defined $HOST) {
  $BOINCCMD .= " --host $HOST";
  if (defined $PORT) {
    $BOINCCMD .= ":$PORT";
  }
}
if (defined $PASSWORD) {
  $BOINCCMD .= " --passwd $PASSWORD";
}
if (defined $BOINCDIR) {
  chdir $BOINCDIR;
}

#########################################################################
# 3. Initialize output structure
#
my $wu_states = {
		 wu_run => 0,
		 wu_pre => 0,
		 wu_sus => 0,
		 wu_dld => 0,
		 wu_rtr => 0,
		 wu_dlg => 0,
		 wu_upl => 0,
		 wu_err => 0,
		 wu_abt => 0,
		 wu_other => 0
		};

#########################################################################
# 4. Fetch all needed data from BOINC-client with single call
#
my $prj_status = "";
my $results = "";

my $simpleGuiInfo = `$BOINCCMD --get_simple_gui_info 2>/dev/null`;
if ($simpleGuiInfo ne "") {
  # Some data were retrieved, so let's split them
  my @sections;
  my @section1;
  @sections = split /=+ Projects =+\n/, $simpleGuiInfo;
  @section1 = split /=+ [A-z]+ =+\n/, $sections[1];
  $prj_status = $section1[0];

  @sections = split /=+ (?:Results|Tasks) =+\n/, $simpleGuiInfo;
  @section1 = split /=+ [A-z]+ =+\n/, $sections[1];
  $results = $section1[0];
}

#########################################################################
# 5. Parse BOINC data
#
# 5.a) Create project info structure
my @prjInfos = split /\d+\) -+\n/, $prj_status;
shift @prjInfos; # Throw out first empty line

my @susp_projects;    # array of suspended projects
for my $prj_info (@prjInfos) {
  my @lines = split /\n/, $prj_info;
  my @prjURL = grep /^\s+master URL: /,@lines;
  if ($#prjURL != 0) {die "Unexpected output from boinccmd"; }
  my $prjURL =$prjURL[0];
  $prjURL =~ s/^\s+master URL: //;
  my @suspGUI = grep /^\s+suspended via GUI: /,@lines;
  if ($#suspGUI != 0) {die "Unexpected output from boinccmd"; }
  my $suspGUI =$suspGUI[0];
  $suspGUI =~ s/^\s+suspended via GUI: //;
  if ($suspGUI eq "yes") {
    push @susp_projects, $prjURL
  }
}

# 5.b) Parse results, check their states
my @rsltInfos = split /\d+\) -+\n/, $results;
shift @rsltInfos; # Throw out first empty line

for my $rslt_info (@rsltInfos) {
  my @lines = split /\n/, $rslt_info;
  my @schedstat = grep /^\s+scheduler state: /,@lines;
  my $schedstat = $schedstat[0];
  $schedstat =~ s/^\s+scheduler state: //;
  my @state = grep /^\s+state: /,@lines;
  my $state = $state[0];
  $state =~ s/^\s+state: //;
  my @acttask = grep /^\s+active_task_state: /,@lines;
  my $acttask = $acttask[0];
  $acttask =~ s/^\s+active_task_state: //;
  my @suspGUI = grep /^\s+suspended via GUI: /,@lines;
  my $suspGUI =$suspGUI[0];
  $suspGUI =~ s/^\s+suspended via GUI: //;
  my @prjURL = grep /^\s+project URL: /,@lines;
  my $prjURL =$prjURL[0];
  $prjURL =~ s/^\s+project URL: //;
  if ($suspGUI eq "yes") {
    $wu_states->{wu_sus} += 1;
    next;
  }
  my @suspPRJ = grep /^$prjURL$/,@susp_projects;
  if ($#suspPRJ == 0) {
    $wu_states->{wu_sus} += 1;
    next;
  }
  if ($state eq "1") {
    # RESULT_FILES_DOWNLOADING
    $wu_states->{wu_dlg} += 1;
    next;
  }
  if ($state eq "2") {
    # RESULT_FILES_DOWNLOADED
    if ($schedstat eq "0") {
      # CPU_SCHED_UNINITIALIZED   0
      $wu_states->{wu_dld} += 1;
      next;
    }
    if ($schedstat eq "1") {
      # CPU_SCHED_PREEMPTED       1
      $wu_states->{wu_pre} += 1;
      next;
    }
    if ($schedstat eq "2") {
      # CPU_SCHED_SCHEDULED       2
      if ($acttask eq "1") {
	# PROCESS_EXECUTING       1
	$wu_states->{wu_run} += 1;
	next;
      }
      if ( ($acttask eq "0") || ($acttask eq "9") ) {
	# PROCESS_UNINITIALIZED   0
	# PROCESS_SUSPENDED       9
	# suspended by "user active"?
	$wu_states->{wu_sus} += 1;
	next;
      }
      $wu_states->{wu_other} += 1;
      next;
    }
    $wu_states->{wu_other} += 1;
    next;
  }
  if ($state eq "3") {
    # RESULT_COMPUTE_ERROR
    $wu_states->{wu_err} += 1;
    next;
  }
  if ($state eq "4") {
    # RESULT_FILES_UPLOADING
    $wu_states->{wu_upl} += 1;
    next;
  }
  if ($state eq "5") {
    # RESULT_FILES_UPLOADED
    $wu_states->{wu_rtr} += 1;
    next;
  }
  if ($state eq "6") {
    # RESULT_ABORTED
    $wu_states->{wu_abt} += 1;
    next;
  }
  $wu_states->{wu_other} += 1;
}


#########################################################################
# 6. Display output
#

if ( (defined $ARGV[0]) && ($ARGV[0] eq "config") ) {
#
# 6.a) Display config
#

  if (defined $HOST) {
    print "host_name $HOST\n";
  }

  print "graph_title BOINC work status\n";
  print "graph_category htc\n";
  print "graph_args --base 1000 -l 0\n";
  print "graph_vlabel Workunits\n";
  print "graph_total total\n";

  # First state is AREA, next are STACK
  print "wu_run.label Running\n";
  print "wu_run.draw AREA\n";
  print "wu_run.type GAUGE\n";
  print "wu_pre.label Preempted\n";
  print "wu_pre.draw STACK\n";
  print "wu_pre.type GAUGE\n";
  print "wu_sus.label Suspended\n";
  print "wu_sus.draw STACK\n";
  print "wu_sus.type GAUGE\n";
  print "wu_dld.label Ready to run\n";
  print "wu_dld.draw STACK\n";
  print "wu_dld.type GAUGE\n";
  print "wu_rtr.label Ready to report\n";
  print "wu_rtr.draw STACK\n";
  print "wu_rtr.type GAUGE\n";
  print "wu_dlg.label Downloading\n";
  print "wu_dlg.draw STACK\n";
  print "wu_dlg.type GAUGE\n";
  print "wu_upl.label Uploading\n";
  print "wu_upl.draw STACK\n";
  print "wu_upl.type GAUGE\n";
  if ($VERBOSE ne "0") {
    print "wu_err.label Computation Error\n";
    print "wu_err.draw STACK\n";
    print "wu_err.type GAUGE\n";
    print "wu_abt.label Aborted\n";
    print "wu_abt.draw STACK\n";
    print "wu_abt.type GAUGE\n";
  }
  print "wu_other.label other states\n";
  print "wu_other.draw STACK\n";
  print "wu_other.type GAUGE\n";

  exit 0;
}

#
# 6.b) Display state of WUs
#

print "wu_run.value $wu_states->{wu_run}\n";
print "wu_pre.value $wu_states->{wu_pre}\n";
print "wu_sus.value $wu_states->{wu_sus}\n";
print "wu_dld.value $wu_states->{wu_dld}\n";
print "wu_rtr.value $wu_states->{wu_rtr}\n";
print "wu_dlg.value $wu_states->{wu_dlg}\n";
print "wu_upl.value $wu_states->{wu_upl}\n";
if ($VERBOSE ne "0") {
  print "wu_err.value $wu_states->{wu_err}\n";
  print "wu_abt.value $wu_states->{wu_abt}\n";
  print "wu_other.value $wu_states->{wu_other}\n";
}
else {
  my $other = $wu_states->{wu_err} + $wu_states->{wu_abt} + $wu_states->{wu_other};
  print "wu_other.value $other\n";
}

exit 0;


#########################################################################
# perldoc section

=head1 NAME

boinc_wus - Munin plugin to monitor states of all BOINC WUs

=head1 APPLICABLE SYSTEMS

Linux machines running BOINC and munin-node

- or -

Linux servers (running munin-node) used to collect data from other systems
which are running BOINC, but not running munin-node (e.g. non-Linux systems)

=head1 CONFIGURATION

Following configuration variables are supported:

=over 12

=item B<boinccmd>

command-line control program (default: boinccmd)

=item B<host>

Host to query (default: none)

=item B<port>

GUI RPC port (default: none = use BOINC-default)

=item B<boincdir>

Directory containing appropriate file gui_rpc_auth.cfg (default: none)

=item B<verbose>

Display unusual states details (default: 0 = Summarize unusual states as C<other>)

=item B<password>

Password for BOINC (default: none)

=back

=head2 B<Security Consideration:>

Using of variable B<password> poses a security risk. Even if the Munin
configuration file for this plugin containing BOINC-password is properly
protected, the password is exposed as environment variable and finally passed
to boinccmd as a parameter. It is therefore possible for local users of the
machine running this plugin to eavesdrop the BOINC password.

Using of variable password is therefore strongly discouraged and is left here
as a legacy option and for testing purposes.

It should be always possible to use B<boincdir> variable instead - in such case
the file gui_rpc_auth.cfg is read by boinccmd binary directly.
If this plugin is used to fetch data from remote system, the gui_rpc_auth.cfg
can be copied to special directory in a secure way (e.g. via scp) and properly
protected by file permissions.

=head1 INTERPRETATION

This plugin shows how many BOINC workunits are in all the various states.
The most important states C<Running>, C<Preempted>, C<Suspended>,
C<Ready to run>, C<Ready to report>, C<Downloading> and C<Uploading> are always
displayed. All other states are shown as C<other>.

If the variable B<verbose> is used, additionally also states
C<Computation Error> and C<Aborted> are shown separately (they are included in
C<other> otherwise).

=head1 EXAMPLES

=head2 Local BOINC Example

BOINC is running on local machine. The BOINC binaries are installed in
F</opt/boinc/custom-6.10.1/>, the BOINC is running in directory
F</usr/local/boinc/> under username boinc, group boinc and the password is used
to protect access to BOINC:

  [boinc_*]
  group boinc
  env.boinccmd /opt/boinc/custom-6.10.1/boinccmd
  env.boincdir /usr/local/boinc
  env.verbose 1

=head2 Remote BOINC Example

BOINC is running on 2 remote machines C<foo> and C<bar>.
On the local machine the binary of command-line interface is installed in
directory F</usr/local/bin/>.
The BOINC password used on the remote machine C<foo> is stored in file
F</etc/munin/boinc/foo/gui_rpc_auth.cfg>.
The BOINC password used on the remote machine C<bar> is stored in file
F</etc/munin/boinc/bar/gui_rpc_auth.cfg>.
These files are owned and readable by root, readable by group munin and not
readable by others.
There are 2 symbolic links to this plugin created in the munin plugins
directory (usually F</etc/munin/plugins/>): F<snmp_foo_boincwus> and
F<snmp_bar_boincwus>

  [snmp_foo_boinc*]
  group munin
  env.boinccmd /usr/local/bin/boinccmd
  env.host foo
  env.boincdir /etc/munin/boinc/foo

  [snmp_bar_boinc*]
  group munin
  env.boinccmd /usr/local/bin/boinccmd
  env.host bar
  env.boincdir /etc/munin/boinc/bar

This way the plugin can be used by Munin the same way as the Munin plugins
utilizng SNMP (although this plugin itself does not use SNMP).

=head1 BUGS

There is no C<autoconf> capability at the moment. This is due to the fact, that
BOINC installations may vary over different systems, sometimes using default
directory from distribution (e.g. F</var/lib/boinc/> in Debian or Ubuntu), but
often running in user directories or in other separate directories.
Also the user-ID under which BOINC runs often differs.
Under these circumstances the C<autoconf> would be either lame or too
complicated.

=head1 AUTHOR

Palo M. <palo.gm@gmail.com>

=head1 LICENSE

GPLv3 L<http://www.gnu.org/licenses/gpl-3.0.txt>

=cut

# vim:syntax=perl