root / plugins / boinc / boinc_wus @ c40eabee
Historique | Voir | Annoter | Télécharger (12,9 ko)
| 1 | 6ddb169d | Paul Saunders | #!/usr/bin/perl -w |
|---|---|---|---|
| 2 | # |
||
| 3 | # boinc_wus - Munin plugin to monitor states of all BOINC WUs |
||
| 4 | # |
||
| 5 | # Run 'perldoc boinc_wus' for full man page |
||
| 6 | # |
||
| 7 | # Author: Palo M. <palo.gm@gmail.com> |
||
| 8 | # Modified by: Paul Saunders <darac+munin@darac.org.uk> |
||
| 9 | # License: GPLv3 <http://www.gnu.org/licenses/gpl-3.0.txt> |
||
| 10 | # |
||
| 11 | # |
||
| 12 | # Parameters supported: |
||
| 13 | # config |
||
| 14 | # |
||
| 15 | # |
||
| 16 | # Configurable variables |
||
| 17 | # boinccmd - command-line control program (default: boinc_cmd) |
||
| 18 | # host - Host to query (default: none) |
||
| 19 | # port - GUI RPC port (default: none = use BOINC-default) |
||
| 20 | # boincdir - Directory containing appropriate password file |
||
| 21 | # gui_rpc_auth.cfg (default: none) |
||
| 22 | # verbose - Whether display more detailed states (default: 0) |
||
| 23 | # password - Password for BOINC (default: none) !!! UNSAFE !!! |
||
| 24 | # |
||
| 25 | # |
||
| 26 | # $Log$ |
||
| 27 | # |
||
| 28 | # Revision 1.1 2011/03/22 Paul Saunders |
||
| 29 | # Update for BOINC 6.12 |
||
| 30 | # Add colours from http://boinc.netsoft-online.com/e107_plugins/forum/forum_viewtopic.php?3 |
||
| 31 | # Revision 1.0 2009/09/13 Palo M. |
||
| 32 | # Add documentation and license information |
||
| 33 | # Ready to publish on Munin Exchange |
||
| 34 | # Revision 0.9 2009/09/13 Palo M. |
||
| 35 | # Add possibility to read password from file |
||
| 36 | # Revision 0.8 2009/09/12 Palo M. |
||
| 37 | # Update default binary name: boinc_cmd -> boinccmd |
||
| 38 | # Revision 0.7 2008/08/29 Palo M. |
||
| 39 | # Creation - Attempt to port functionality from C++ code |
||
| 40 | # |
||
| 41 | # (Revisions 0.1 - 0.6) were done in C++ |
||
| 42 | # |
||
| 43 | # |
||
| 44 | # |
||
| 45 | # Magic markers: |
||
| 46 | #%# family=contrib |
||
| 47 | |||
| 48 | use strict; |
||
| 49 | |||
| 50 | |||
| 51 | ######################################################################### |
||
| 52 | # 1. Parse configuration variables |
||
| 53 | # |
||
| 54 | my $BOINCCMD = exists $ENV{'boinccmd'} ? $ENV{'boinccmd'} : "boinccmd";
|
||
| 55 | my $HOST = exists $ENV{'host'} ? $ENV{'host'} : undef;
|
||
| 56 | my $PORT = exists $ENV{'port'} ? $ENV{'port'} : undef;
|
||
| 57 | my $PASSWORD = exists $ENV{'password'} ? $ENV{'password'} : undef;
|
||
| 58 | my $BOINCDIR = exists $ENV{'boincdir'} ? $ENV{'boincdir'} : undef;
|
||
| 59 | my $VERBOSE = exists $ENV{'verbose'} ? $ENV{'verbose'} : "0";
|
||
| 60 | |||
| 61 | ######################################################################### |
||
| 62 | # 2. Basic executable |
||
| 63 | # |
||
| 64 | if (defined $HOST) {
|
||
| 65 | $BOINCCMD .= " --host $HOST"; |
||
| 66 | if (defined $PORT) {
|
||
| 67 | $BOINCCMD .= ":$PORT"; |
||
| 68 | } |
||
| 69 | } |
||
| 70 | if (defined $PASSWORD) {
|
||
| 71 | $BOINCCMD .= " --passwd $PASSWORD"; |
||
| 72 | } |
||
| 73 | if (defined $BOINCDIR) {
|
||
| 74 | chdir $BOINCDIR; |
||
| 75 | } |
||
| 76 | |||
| 77 | ######################################################################### |
||
| 78 | # 3. Initialize output structure |
||
| 79 | # |
||
| 80 | my $wu_states = {
|
||
| 81 | wu_run => 0, |
||
| 82 | wu_pre => 0, |
||
| 83 | wu_sus => 0, |
||
| 84 | wu_dld => 0, |
||
| 85 | wu_rtr => 0, |
||
| 86 | wu_dlg => 0, |
||
| 87 | wu_upl => 0, |
||
| 88 | wu_err => 0, |
||
| 89 | wu_abt => 0, |
||
| 90 | wu_other => 0 |
||
| 91 | }; |
||
| 92 | |||
| 93 | ######################################################################### |
||
| 94 | # 4. Fetch all needed data from BOINC-client with single call |
||
| 95 | # |
||
| 96 | my $prj_status = ""; |
||
| 97 | my $results = ""; |
||
| 98 | |||
| 99 | my $simpleGuiInfo = `$BOINCCMD --get_simple_gui_info 2>/dev/null`; |
||
| 100 | if ($simpleGuiInfo ne "") {
|
||
| 101 | # Some data were retrieved, so let's split them |
||
| 102 | my @sections; |
||
| 103 | my @section1; |
||
| 104 | @sections = split /=+ Projects =+\n/, $simpleGuiInfo; |
||
| 105 | @section1 = split /=+ [A-z]+ =+\n/, $sections[1]; |
||
| 106 | $prj_status = $section1[0]; |
||
| 107 | |||
| 108 | @sections = split /=+ (?:Results|Tasks) =+\n/, $simpleGuiInfo; |
||
| 109 | @section1 = split /=+ [A-z]+ =+\n/, $sections[1]; |
||
| 110 | $results = $section1[0]; |
||
| 111 | } |
||
| 112 | |||
| 113 | ######################################################################### |
||
| 114 | # 5. Parse BOINC data |
||
| 115 | # |
||
| 116 | # 5.a) Create project info structure |
||
| 117 | my @prjInfos = split /\d+\) -+\n/, $prj_status; |
||
| 118 | shift @prjInfos; # Throw out first empty line |
||
| 119 | |||
| 120 | my @susp_projects; # array of suspended projects |
||
| 121 | for my $prj_info (@prjInfos) {
|
||
| 122 | my @lines = split /\n/, $prj_info; |
||
| 123 | my @prjURL = grep /^\s+master URL: /,@lines; |
||
| 124 | if ($#prjURL != 0) {die "Unexpected output from boinccmd"; }
|
||
| 125 | my $prjURL =$prjURL[0]; |
||
| 126 | $prjURL =~ s/^\s+master URL: //; |
||
| 127 | my @suspGUI = grep /^\s+suspended via GUI: /,@lines; |
||
| 128 | if ($#suspGUI != 0) {die "Unexpected output from boinccmd"; }
|
||
| 129 | my $suspGUI =$suspGUI[0]; |
||
| 130 | $suspGUI =~ s/^\s+suspended via GUI: //; |
||
| 131 | if ($suspGUI eq "yes") {
|
||
| 132 | push @susp_projects, $prjURL |
||
| 133 | } |
||
| 134 | } |
||
| 135 | |||
| 136 | # 5.b) Parse results, check their states |
||
| 137 | my @rsltInfos = split /\d+\) -+\n/, $results; |
||
| 138 | shift @rsltInfos; # Throw out first empty line |
||
| 139 | |||
| 140 | for my $rslt_info (@rsltInfos) {
|
||
| 141 | my @lines = split /\n/, $rslt_info; |
||
| 142 | my @schedstat = grep /^\s+scheduler state: /,@lines; |
||
| 143 | my $schedstat = $schedstat[0]; |
||
| 144 | $schedstat =~ s/^\s+scheduler state: //; |
||
| 145 | my @state = grep /^\s+state: /,@lines; |
||
| 146 | my $state = $state[0]; |
||
| 147 | $state =~ s/^\s+state: //; |
||
| 148 | my @acttask = grep /^\s+active_task_state: /,@lines; |
||
| 149 | my $acttask = $acttask[0]; |
||
| 150 | $acttask =~ s/^\s+active_task_state: //; |
||
| 151 | my @suspGUI = grep /^\s+suspended via GUI: /,@lines; |
||
| 152 | my $suspGUI =$suspGUI[0]; |
||
| 153 | $suspGUI =~ s/^\s+suspended via GUI: //; |
||
| 154 | my @prjURL = grep /^\s+project URL: /,@lines; |
||
| 155 | my $prjURL =$prjURL[0]; |
||
| 156 | $prjURL =~ s/^\s+project URL: //; |
||
| 157 | if ($suspGUI eq "yes") {
|
||
| 158 | $wu_states->{wu_sus} += 1;
|
||
| 159 | next; |
||
| 160 | } |
||
| 161 | my @suspPRJ = grep /^$prjURL$/,@susp_projects; |
||
| 162 | if ($#suspPRJ == 0) {
|
||
| 163 | $wu_states->{wu_sus} += 1;
|
||
| 164 | next; |
||
| 165 | } |
||
| 166 | if ($state eq "1") {
|
||
| 167 | # RESULT_FILES_DOWNLOADING |
||
| 168 | $wu_states->{wu_dlg} += 1;
|
||
| 169 | next; |
||
| 170 | } |
||
| 171 | if ($state eq "2") {
|
||
| 172 | # RESULT_FILES_DOWNLOADED |
||
| 173 | if ($schedstat eq "0") {
|
||
| 174 | # CPU_SCHED_UNINITIALIZED 0 |
||
| 175 | $wu_states->{wu_dld} += 1;
|
||
| 176 | next; |
||
| 177 | } |
||
| 178 | if ($schedstat eq "1") {
|
||
| 179 | # CPU_SCHED_PREEMPTED 1 |
||
| 180 | $wu_states->{wu_pre} += 1;
|
||
| 181 | next; |
||
| 182 | } |
||
| 183 | if ($schedstat eq "2") {
|
||
| 184 | # CPU_SCHED_SCHEDULED 2 |
||
| 185 | if ($acttask eq "1") {
|
||
| 186 | # PROCESS_EXECUTING 1 |
||
| 187 | $wu_states->{wu_run} += 1;
|
||
| 188 | next; |
||
| 189 | } |
||
| 190 | if ( ($acttask eq "0") || ($acttask eq "9") ) {
|
||
| 191 | # PROCESS_UNINITIALIZED 0 |
||
| 192 | # PROCESS_SUSPENDED 9 |
||
| 193 | # suspended by "user active"? |
||
| 194 | $wu_states->{wu_sus} += 1;
|
||
| 195 | next; |
||
| 196 | } |
||
| 197 | $wu_states->{wu_other} += 1;
|
||
| 198 | next; |
||
| 199 | } |
||
| 200 | $wu_states->{wu_other} += 1;
|
||
| 201 | next; |
||
| 202 | } |
||
| 203 | if ($state eq "3") {
|
||
| 204 | # RESULT_COMPUTE_ERROR |
||
| 205 | $wu_states->{wu_err} += 1;
|
||
| 206 | next; |
||
| 207 | } |
||
| 208 | if ($state eq "4") {
|
||
| 209 | # RESULT_FILES_UPLOADING |
||
| 210 | $wu_states->{wu_upl} += 1;
|
||
| 211 | next; |
||
| 212 | } |
||
| 213 | if ($state eq "5") {
|
||
| 214 | # RESULT_FILES_UPLOADED |
||
| 215 | $wu_states->{wu_rtr} += 1;
|
||
| 216 | next; |
||
| 217 | } |
||
| 218 | if ($state eq "6") {
|
||
| 219 | # RESULT_ABORTED |
||
| 220 | $wu_states->{wu_abt} += 1;
|
||
| 221 | next; |
||
| 222 | } |
||
| 223 | $wu_states->{wu_other} += 1;
|
||
| 224 | } |
||
| 225 | |||
| 226 | |||
| 227 | ######################################################################### |
||
| 228 | # 6. Display output |
||
| 229 | # |
||
| 230 | |||
| 231 | if ( (defined $ARGV[0]) && ($ARGV[0] eq "config") ) {
|
||
| 232 | # |
||
| 233 | # 6.a) Display config |
||
| 234 | # |
||
| 235 | |||
| 236 | if (defined $HOST) {
|
||
| 237 | print "host_name $HOST\n"; |
||
| 238 | } |
||
| 239 | |||
| 240 | print "graph_title BOINC work status\n"; |
||
| 241 | print "graph_category BOINC\n"; |
||
| 242 | print "graph_args --base 1000 -l 0\n"; |
||
| 243 | print "graph_vlabel Workunits\n"; |
||
| 244 | print "graph_total total\n"; |
||
| 245 | |||
| 246 | # First state is AREA, next are STACK |
||
| 247 | print "wu_run.label Running\n"; |
||
| 248 | print "wu_run.draw AREA\n"; |
||
| 249 | print "wu_run.type GAUGE\n"; |
||
| 250 | print "wu_pre.label Preempted\n"; |
||
| 251 | print "wu_pre.draw STACK\n"; |
||
| 252 | print "wu_pre.type GAUGE\n"; |
||
| 253 | print "wu_sus.label Suspended\n"; |
||
| 254 | print "wu_sus.draw STACK\n"; |
||
| 255 | print "wu_sus.type GAUGE\n"; |
||
| 256 | print "wu_dld.label Ready to run\n"; |
||
| 257 | print "wu_dld.draw STACK\n"; |
||
| 258 | print "wu_dld.type GAUGE\n"; |
||
| 259 | print "wu_rtr.label Ready to report\n"; |
||
| 260 | print "wu_rtr.draw STACK\n"; |
||
| 261 | print "wu_rtr.type GAUGE\n"; |
||
| 262 | print "wu_dlg.label Downloading\n"; |
||
| 263 | print "wu_dlg.draw STACK\n"; |
||
| 264 | print "wu_dlg.type GAUGE\n"; |
||
| 265 | print "wu_upl.label Uploading\n"; |
||
| 266 | print "wu_upl.draw STACK\n"; |
||
| 267 | print "wu_upl.type GAUGE\n"; |
||
| 268 | if ($VERBOSE ne "0") {
|
||
| 269 | print "wu_err.label Computation Error\n"; |
||
| 270 | print "wu_err.draw STACK\n"; |
||
| 271 | print "wu_err.type GAUGE\n"; |
||
| 272 | print "wu_abt.label Aborted\n"; |
||
| 273 | print "wu_abt.draw STACK\n"; |
||
| 274 | print "wu_abt.type GAUGE\n"; |
||
| 275 | } |
||
| 276 | print "wu_other.label other states\n"; |
||
| 277 | print "wu_other.draw STACK\n"; |
||
| 278 | print "wu_other.type GAUGE\n"; |
||
| 279 | |||
| 280 | exit 0; |
||
| 281 | } |
||
| 282 | |||
| 283 | # |
||
| 284 | # 6.b) Display state of WUs |
||
| 285 | # |
||
| 286 | |||
| 287 | print "wu_run.value $wu_states->{wu_run}\n";
|
||
| 288 | print "wu_pre.value $wu_states->{wu_pre}\n";
|
||
| 289 | print "wu_sus.value $wu_states->{wu_sus}\n";
|
||
| 290 | print "wu_dld.value $wu_states->{wu_dld}\n";
|
||
| 291 | print "wu_rtr.value $wu_states->{wu_rtr}\n";
|
||
| 292 | print "wu_dlg.value $wu_states->{wu_dlg}\n";
|
||
| 293 | print "wu_upl.value $wu_states->{wu_upl}\n";
|
||
| 294 | if ($VERBOSE ne "0") {
|
||
| 295 | print "wu_err.value $wu_states->{wu_err}\n";
|
||
| 296 | print "wu_abt.value $wu_states->{wu_abt}\n";
|
||
| 297 | print "wu_other.value $wu_states->{wu_other}\n";
|
||
| 298 | } |
||
| 299 | else {
|
||
| 300 | my $other = $wu_states->{wu_err} + $wu_states->{wu_abt} + $wu_states->{wu_other};
|
||
| 301 | print "wu_other.value $other\n"; |
||
| 302 | } |
||
| 303 | |||
| 304 | exit 0; |
||
| 305 | |||
| 306 | |||
| 307 | ######################################################################### |
||
| 308 | # perldoc section |
||
| 309 | |||
| 310 | =head1 NAME |
||
| 311 | |||
| 312 | boinc_wus - Munin plugin to monitor states of all BOINC WUs |
||
| 313 | |||
| 314 | =head1 APPLICABLE SYSTEMS |
||
| 315 | |||
| 316 | Linux machines running BOINC and munin-node |
||
| 317 | |||
| 318 | - or - |
||
| 319 | |||
| 320 | Linux servers (running munin-node) used to collect data from other systems |
||
| 321 | which are running BOINC, but not running munin-node (e.g. non-Linux systems) |
||
| 322 | |||
| 323 | =head1 CONFIGURATION |
||
| 324 | |||
| 325 | Following configuration variables are supported: |
||
| 326 | |||
| 327 | =over 12 |
||
| 328 | |||
| 329 | =item B<boinccmd> |
||
| 330 | |||
| 331 | command-line control program (default: boinccmd) |
||
| 332 | |||
| 333 | =item B<host> |
||
| 334 | |||
| 335 | Host to query (default: none) |
||
| 336 | |||
| 337 | =item B<port> |
||
| 338 | |||
| 339 | GUI RPC port (default: none = use BOINC-default) |
||
| 340 | |||
| 341 | =item B<boincdir> |
||
| 342 | |||
| 343 | Directory containing appropriate file gui_rpc_auth.cfg (default: none) |
||
| 344 | |||
| 345 | =item B<verbose> |
||
| 346 | |||
| 347 | Display unusual states details (default: 0 = Summarize unusual states as C<other>) |
||
| 348 | |||
| 349 | =item B<password> |
||
| 350 | |||
| 351 | Password for BOINC (default: none) |
||
| 352 | |||
| 353 | =back |
||
| 354 | |||
| 355 | =head2 B<Security Consideration:> |
||
| 356 | |||
| 357 | Using of variable B<password> poses a security risk. Even if the Munin |
||
| 358 | configuration file for this plugin containing BOINC-password is properly |
||
| 359 | protected, the password is exposed as environment variable and finally passed |
||
| 360 | to boinccmd as a parameter. It is therefore possible for local users of the |
||
| 361 | machine running this plugin to eavesdrop the BOINC password. |
||
| 362 | |||
| 363 | Using of variable password is therefore strongly discouraged and is left here |
||
| 364 | as a legacy option and for testing purposes. |
||
| 365 | |||
| 366 | It should be always possible to use B<boincdir> variable instead - in such case |
||
| 367 | the file gui_rpc_auth.cfg is read by boinccmd binary directly. |
||
| 368 | If this plugin is used to fetch data from remote system, the gui_rpc_auth.cfg |
||
| 369 | can be copied to special directory in a secure way (e.g. via scp) and properly |
||
| 370 | protected by file permissions. |
||
| 371 | |||
| 372 | =head1 INTERPRETATION |
||
| 373 | |||
| 374 | This plugin shows how many BOINC workunits are in all the various states. |
||
| 375 | The most important states C<Running>, C<Preempted>, C<Suspended>, |
||
| 376 | C<Ready to run>, C<Ready to report>, C<Downloading> and C<Uploading> are always |
||
| 377 | displayed. All other states are shown as C<other>. |
||
| 378 | |||
| 379 | If the variable B<verbose> is used, additionally also states |
||
| 380 | C<Computation Error> and C<Aborted> are shown separately (they are included in |
||
| 381 | C<other> otherwise). |
||
| 382 | |||
| 383 | =head1 EXAMPLES |
||
| 384 | |||
| 385 | =head2 Local BOINC Example |
||
| 386 | |||
| 387 | BOINC is running on local machine. The BOINC binaries are installed in |
||
| 388 | F</opt/boinc/custom-6.10.1/>, the BOINC is running in directory |
||
| 389 | F</usr/local/boinc/> under username boinc, group boinc and the password is used |
||
| 390 | to protect access to BOINC: |
||
| 391 | |||
| 392 | [boinc_*] |
||
| 393 | group boinc |
||
| 394 | env.boinccmd /opt/boinc/custom-6.10.1/boinccmd |
||
| 395 | env.boincdir /usr/local/boinc |
||
| 396 | env.verbose 1 |
||
| 397 | |||
| 398 | =head2 Remote BOINC Example |
||
| 399 | |||
| 400 | BOINC is running on 2 remote machines C<foo> and C<bar>. |
||
| 401 | On the local machine the binary of command-line interface is installed in |
||
| 402 | directory F</usr/local/bin/>. |
||
| 403 | The BOINC password used on the remote machine C<foo> is stored in file |
||
| 404 | F</etc/munin/boinc/foo/gui_rpc_auth.cfg>. |
||
| 405 | The BOINC password used on the remote machine C<bar> is stored in file |
||
| 406 | F</etc/munin/boinc/bar/gui_rpc_auth.cfg>. |
||
| 407 | These files are owned and readable by root, readable by group munin and not |
||
| 408 | readable by others. |
||
| 409 | There are 2 symbolic links to this plugin created in the munin plugins |
||
| 410 | directory (usually F</etc/munin/plugins/>): F<snmp_foo_boincwus> and |
||
| 411 | F<snmp_bar_boincwus> |
||
| 412 | |||
| 413 | [snmp_foo_boinc*] |
||
| 414 | group munin |
||
| 415 | env.boinccmd /usr/local/bin/boinccmd |
||
| 416 | env.host foo |
||
| 417 | env.boincdir /etc/munin/boinc/foo |
||
| 418 | |||
| 419 | [snmp_bar_boinc*] |
||
| 420 | group munin |
||
| 421 | env.boinccmd /usr/local/bin/boinccmd |
||
| 422 | env.host bar |
||
| 423 | env.boincdir /etc/munin/boinc/bar |
||
| 424 | |||
| 425 | This way the plugin can be used by Munin the same way as the Munin plugins |
||
| 426 | utilizng SNMP (although this plugin itself does not use SNMP). |
||
| 427 | |||
| 428 | =head1 BUGS |
||
| 429 | |||
| 430 | There is no C<autoconf> capability at the moment. This is due to the fact, that |
||
| 431 | BOINC installations may vary over different systems, sometimes using default |
||
| 432 | directory from distribution (e.g. F</var/lib/boinc/> in Debian or Ubuntu), but |
||
| 433 | often running in user directories or in other separate directories. |
||
| 434 | Also the user-ID under which BOINC runs often differs. |
||
| 435 | Under these circumstances the C<autoconf> would be either lame or too |
||
| 436 | complicated. |
||
| 437 | |||
| 438 | =head1 AUTHOR |
||
| 439 | |||
| 440 | Palo M. <palo.gm@gmail.com> |
||
| 441 | |||
| 442 | =head1 LICENSE |
||
| 443 | |||
| 444 | GPLv3 L<http://www.gnu.org/licenses/gpl-3.0.txt> |
||
| 445 | |||
| 446 | =cut |
||
| 447 | |||
| 448 | # vim:syntax=perl |
