/[nagios-plugins-perl]/trunk/plugins/check_racvision.pl
ViewVC logotype

Contents of /trunk/plugins/check_racvision.pl

Parent Directory Parent Directory | Revision Log Revision Log


Revision 207 - (show annotations) (download)
Thu Mar 21 15:09:17 2019 UTC (20 months, 1 week ago) by racvision
File MIME type: text/plain
File size: 16030 byte(s)
1 #!/usr/bin/perl -w
2 #
3 # Copyright (c) 2002-2019 Stéphane Urbanovski <stephane.urbanovski@ac-nancy-metz.fr>
4 #
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License
7 # as published by the Free Software Foundation; either version 2
8 # of the License, or (at your option) any later version.
9 #
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty
12 # of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
14 #
15 # you should have received a copy of the GNU General Public License
16 # along with this program (or with Nagios); if not, write to the
17 # Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 # Boston, MA 02111-1307, USA
19 #
20
21 use strict; # should never be differently :-)
22 use warnings;
23
24
25 use Locale::gettext;
26 use File::Basename; # get basename()
27
28 use POSIX qw(setlocale);
29 use Unicode::String qw(latin1 utf8); # Convert between iso-8859-1 and utf-8
30 use Time::HiRes qw(time); # get microtime
31 use POSIX qw(mktime);
32
33 use Nagios::Plugin;
34
35 use LWP::UserAgent; # http client
36 use HTTP::Request; # used by LWP::UserAgent
37 use HTTP::Status; # to get http err msg
38
39 use IO::Socket::SSL;
40 use XML::Parser::Expat;
41
42 use Data::Dumper;
43
44
45 my %RETURN_CODE_VALUES = (
46 'UNKNOWN' => UNKNOWN,
47 'OK' => OK,
48 'WARN' => WARNING,
49 'CRIT' => CRITICAL,
50 'MAINT' => UNKNOWN
51 );
52
53
54 my $PROGNAME = basename($0);
55 '$Revision: 2.0 $' =~ /^.*(\d+\.\d+) \$$/; # Use The Revision from RCS/CVS/SVN
56 my $VERSION = $1;
57
58 my $DEBUG = 0;
59 my $TIMEOUT = 9;
60
61 # i18n :
62 setlocale(LC_MESSAGES, '');
63 textdomain('nagios-plugins-perl');
64
65
66 my $np = Nagios::Plugin->new(
67 version => $VERSION,
68 blurb => _gt('Plugin to check Racvision web url'),
69 usage => "Usage: %s [ -v|--verbose ] -u <url> [-t <timeout>] [ -c|--critical=<threshold> ] [ -w|--warning=<threshold> ]",
70 timeout => $TIMEOUT+1
71 );
72 $np->add_arg (
73 spec => 'debug|d',
74 help => _gt('Debug level'),
75 default => 0,
76 );
77
78 $np->add_arg (
79 spec => 'a=f',
80 help => _gt('Age max (in minutes)'),
81 default => 0,
82 );
83 $np->add_arg (
84 spec => 'w=f',
85 help => _gt('Warning request time threshold (in seconds)'),
86 default => 2,
87 label => 'FLOAT'
88 );
89 $np->add_arg (
90 spec => 'c=f',
91 help => _gt('Critical request time threshold (in seconds)'),
92 default => 10,
93 label => 'FLOAT'
94 );
95 $np->add_arg (
96 spec => 'url|u=s',
97 help => _gt('URL of the racvision xml page.'),
98 required => 1,
99 );
100 $np->add_arg (
101 spec => 'encoding|e:s',
102 help => _gt('Enforce caracters encoding (\'utf-8\',\'iso-8859-1\', ...) when parsing XML DATA'),
103 default => '',
104 );
105 $np->add_arg (
106 spec => 'ignore|i=s@',
107 help => _gt('Ignore listed tests'),
108 );
109
110 $np->getopts;
111
112 $DEBUG = $np->opts->get('debug');
113 my $verbose = $np->opts->verbose;
114
115 # Thresholds :
116 # time
117 my $warn_t = $np->opts->get('w');
118 my $crit_t = $np->opts->get('c');
119
120
121 my $age_max = $np->opts->get('a');
122
123 my $url = $np->opts->get('url');
124 my $encoding = $np->opts->get('encoding');
125 my $ignore = $np->opts->get('ignore');
126
127
128 # IO::Socket::SSL::set_defaults('SSL_verify_mode' => SSL_VERIFY_NONE);
129
130 # Create a LWP user agent object:
131 my $ua = new LWP::UserAgent(
132 'env_proxy' => 0,
133 'timeout' => $TIMEOUT,
134 );
135 $ua->agent(basename($0));
136
137 # Workaround for LWP bug :
138 $ua->parse_head(0);
139
140
141 # Handle specific availability computing
142 my $availability = OK;
143 my $availability_mode = 'NAGIOS'; # Default to Nagios mode
144
145 if ( defined($ENV{'http_proxy'}) ) {
146 # Normal http proxy :
147 $ua->proxy(['http'], $ENV{'http_proxy'});
148 # Https must use Crypt::SSLeay https proxy (to use CONNECT method instead of GET)
149 $ENV{'HTTPS_PROXY'} = $ENV{'http_proxy'};
150 }
151
152 # Build and submit an http request :
153 my $request = HTTP::Request->new('GET', $url);
154 my $timer = time();
155 my $http_response = $ua->request( $request );
156 $timer = time()-$timer;
157
158
159 my $connection_status = $np->check_threshold(
160 'check' => $timer,
161 'warning' => $warn_t,
162 'critical' => $crit_t,
163 );
164
165 $np->add_perfdata(
166 'label' => 't',
167 'value' => sprintf('%.6f',$timer),
168 'min' => 0,
169 'uom' => 's',
170 'threshold' => $np->threshold()
171 );
172
173 if ( $connection_status > OK ) {
174 $np->add_message($connection_status, sprintf(_gt("Response time degraded: %.6fs !"),$timer) );
175 }
176
177
178 if ( $http_response->is_error() ) {
179 my $err = $http_response->code." ".status_message($http_response->code)." (".$http_response->message.")";
180 $np->nagios_exit(CRITICAL, _gt("HTTP error: ").$err );
181 }
182
183 if ( ! $http_response->is_success() ) {
184 my $err = $http_response->code." ".status_message($http_response->code)." (".$http_response->message.")";
185 $np->nagios_exit(CRITICAL, _gt("Internal error: ").$err );
186 }
187
188
189 # ($status, $message) = $np->check_messages();
190 my ($status, $message) = (OK, '');
191
192 # if ( $http_response->is_success() ) {
193
194 # Get xml content ...
195 my $xml = $http_response->content;
196 if ($DEBUG) {
197 print "------------------===http output===------------------\n$xml\n-----------------------------------------------------\n";
198 print "t=".$timer."s\n";
199 };
200
201 # Remove DOCTYPE and new lines:
202 $xml =~ s/\<\!DOCTYPE .*?\>// ;
203 my $fxml = '';
204 foreach ( split("[\r\n]+",$xml) ) {
205 s/^\s+//;
206 $fxml .= $_;
207 }
208 $xml = $fxml ;
209
210 if ($DEBUG) {
211 print "------------------===sanitized===------------------\n$xml\n-----------------------------------------------------\n";
212 };
213
214 # Get XML tools:
215 my $parser;
216
217 # Manage encoding :
218 if ( $encoding eq 'iso-8859-1' || $encoding eq 'iso-8859-15') {
219 # transcode iso-8859-1 to utf8 :
220 my $tmp = latin1($xml);
221 $xml = $tmp->utf8;
222 }
223
224 #Force xml output to utf-8
225 utf8::upgrade($xml);
226
227
228 my %d = (
229 '_ERROR' => '',
230 'DATE' => '',
231 'APPLICATION' => {
232 'NAME' => '',
233 'VERSION' => '',
234 'DESCRIPTION' => '',
235 'AVAILABILITY' => '',
236 'TEST' => [],
237 'PERIOD' => {
238 'START' => '',
239 'END' => ''
240 }
241 },
242 );
243
244 if ( !&xmlParse($xml,\%d) ) {
245 ### Try to detect login prompt (RSA protected URL)
246 if (
247 $xml =~ /ctlogonform/i
248 || $xml =~ /votre mot de passe/i
249 || $xml =~ /votre passcode otp/i
250 ) {
251 $np->nagios_exit(CRITICAL, _gt("Cannot parse XML data, maybe a RSA protected URL? XML parse error: ") . $d{'_ERROR'});
252 }
253 else {
254 $np->nagios_exit(CRITICAL, _gt("XML parse error: ") . $d{'_ERROR'});
255 }
256 }
257
258 # Now that we have something parsable, check if we are in application driven availability computing mode (aka EQOS)
259 if ( defined($ENV{'AVAILABILITY_MODE'}) && $ENV{'AVAILABILITY_MODE'} eq 'EQOS') {
260 $availability_mode = 'EQOS';
261 logD ('Switching to AVAILABILITY_MODE='.$availability_mode.' status='.$connection_status);
262 }
263 $availability = $connection_status;
264
265 logD ('name['.$d{'APPLICATION'}{'NAME'}.'] - version['.$d{'APPLICATION'}{'VERSION'}.']');
266
267 $np->shortname($d{'APPLICATION'}{'NAME'}.' ('.$d{'APPLICATION'}{'VERSION'}.')');
268
269
270 #
271 # Handle racvision tests
272 ################################
273
274 my $nb_test = scalar(@{$d{'APPLICATION'}{'TEST'}});
275 my $nb_test_done = 0;
276 my $nb_test_ignored = 0;
277 my $tmp_status = OK;
278
279 for ( my $i = 1 ; $i <= $nb_test ; $i++ ) {
280 my $test = $d{'APPLICATION'}{'TEST'}[$i-1];
281
282 $nb_test_done++;
283
284 logD ($i.'/'.$nb_test.') id=['.$test->{'ID'}.'] - description=['.$test->{'DESCRIPTION'}.'] - state=['.$test->{'STATE_VAL'}.'] - msg=['.$test->{'STATE'}.']');
285
286 if ( defined($RETURN_CODE_VALUES{$test->{'STATE_VAL'}}) ) {
287 $tmp_status = $RETURN_CODE_VALUES{$test->{'STATE_VAL'}};
288 } else {
289 $np->add_message(WARNING, sprintf(_gt('Bad status for test n°%d'),$i) );
290 $tmp_status = OK;
291 }
292
293 if ( $test->{'VALUE'} =~ /^\d+\.?\d*$/ ) {
294 if ( $test->{'ID'} =~ /^[\w_][\w\d\-_\.]*$/ ) {
295 $np->add_perfdata(
296 'label' => $test->{'ID'},
297 'value' => sprintf('%.6f',$test->{'VALUE'}),
298 'uom' => $test->{'VALUE_UNIT'},
299 );
300
301 logD ('DEBUG: value['.$test->{'VALUE'}.'] - unit['.$test->{'VALUE_UNIT'}.']');
302 } elsif ($DEBUG) {
303 logW ('Bad Id for test n°'.$i.": '".$test->{'ID'});
304 }
305 }
306
307 if ( defined($ignore) ) {
308 if ( grep ($_ eq $test->{'ID'}, @{$ignore}) ) {
309 logD ('Ignoring id='.$test->{'ID'});
310 $nb_test_ignored++;
311 $tmp_status = OK;
312 }
313 }
314 $np->add_message($tmp_status, sprintf(_gt('%s - %s : %s'),$test->{'STATE_VAL'},$test->{'DESCRIPTION'},$test->{'STATE'}) );
315
316 if ( ($availability_mode eq 'EQOS') && ($tmp_status != OK) ) {
317 if ( $test->{'AVAILABILITY'} ne 'ignore' ) {
318 $availability = CRITICAL;
319 } else {
320 logD ("Ignoring this test (".$test->{'ID'}.") for AVAILABILITY report !");
321 }
322 }
323 }
324 my $time_now = time();
325
326 if ( $age_max > 0 ) {
327 if ( !defined($d{'DATE'}) ) {
328 $np->add_message(WARNING, sprintf(_gt('No DATE defined !')) );
329 } elsif ( $d{'DATE'} =~ /^(\d{4})\-(\d{2})\-(\d{2})\s(\d{2}):(\d{2}):(\d{2})/) { #2005-09-15 08:00:00
330 my $age = $time_now - mktime($6, $5, $4, $3, $2-1, $1-1900)||0;
331 logD ('DATE='.$d{'DATE'}.' age='.$age);
332
333 if ( $age/60 > $age_max ) {
334 $np->add_message(CRITICAL, sprintf(_gt('Page too old ! (%ss)'),$age) );
335
336 }
337
338 } else {
339 logW ('Invalid value for DATE: '.$d{'DATE'});
340 }
341 }
342
343 if ( $verbose ) {
344 ($status, $message) = $np->check_messages('join' => '<br/>','join_all' => '<br/>');
345 } else {
346 ($status, $message) = $np->check_messages('join' => '<br/>');
347 }
348
349 if ( $status == OK ) {
350 $message = sprintf(_gt("%i test(s) done"),$nb_test_done);
351 if ($nb_test_ignored) {
352 $message .= sprintf(_gt(" and %i test(s) ignored"),$nb_test_ignored);
353 }
354 $np->add_message(OK, $message);
355 }
356
357
358 #
359 # Handle check periods
360 ################################
361
362 my $time_start = 0;
363 my $time_end = 0;
364 if ( $d{'APPLICATION'}{'PERIOD'}{'START'} ne '' ) {
365 if ( $d{'APPLICATION'}{'PERIOD'}{'START'} =~ /^(\d{4})\-(\d{2})\-(\d{2})\s(\d{2}):(\d{2}):(\d{2})/) { #2005-09-15 08:00:00
366 $time_start = mktime($6, $5, $4, $3, $2-1, $1-1900)||0;
367 logD ('Start period: '.$d{'APPLICATION'}{'PERIOD'}{'START'});
368 } else {
369 logW ('Invalid value for start period: '.$d{'APPLICATION'}{'PERIOD'}{'START'});
370 }
371 }
372 if ( $d{'APPLICATION'}{'PERIOD'}{'END'} ne '' ) {
373 if ( $d{'APPLICATION'}{'PERIOD'}{'END'} =~ /^(\d{4})\-(\d{2})\-(\d{2})\s(\d{2}):(\d{2}):(\d{2})/) { #2005-09-15 08:00:00
374 $time_end = mktime($6, $5, $4, $3, $2-1, $1-1900)||0;
375 logD ('End period: '.$d{'APPLICATION'}{'PERIOD'}{'END'});
376 } else {
377 logW ('Invalid value for end period: '.$d{'APPLICATION'}{'PERIOD'}{'END'});
378 }
379 }
380 my $out_of_period = 0;
381 logD("period : actual=$time_now start=$time_start end=$time_end");
382 if ( $time_start && ($time_now < $time_start) ) {
383 $message .= ' '.sprintf(_gt("(before campaign starting at %s)"),$d{'APPLICATION'}{'PERIOD'}{'START'});
384 $out_of_period = 1;
385 } elsif ( $time_end && ($time_now > $time_end) ) {
386 $message .= ' '.sprintf(_gt("(after campaign ending at %s)"),$d{'APPLICATION'}{'PERIOD'}{'END'});
387 $out_of_period = 1;
388 }
389 if ( $out_of_period && ($status == CRITICAL) ) {
390 # Lower the status to WARNING if out of period
391 $status = WARNING;
392 }
393
394 logD ("message=".$message);
395
396 #
397 # Handle EQOS mode
398 ################################
399
400 if ( $availability_mode eq 'EQOS' ) {
401 if ( $d{'APPLICATION'}{'AVAILABILITY'} eq 'UP' ) {
402 logD ("Force AVAILABILITY to OK due to application requirement");
403 $availability = $connection_status;
404
405 } elsif ( $d{'APPLICATION'}{'AVAILABILITY'} eq 'DOWN' ) {
406 logD ("Force AVAILABILITY to DOWN due to application requirement");
407 $availability = CRITICAL;
408 }
409 # Bypass normal Nagios exit status if we are in EQOS mode.
410 $status = $availability;
411 }
412
413 # }
414
415
416 $np->nagios_exit($status, $message );
417
418
419 sub xmlParse {
420 our ( $xml, $racvision ) = @_;
421
422 our @xpath = ();
423 our $currentTag = '';
424
425 our $curDataRef = undef;
426
427 my $parser = new XML::Parser::Expat('ProtocolEncoding' => 'utf-8');
428
429 $parser->setHandlers(
430 'Start' => \&start_handler,
431 'End' => \&end_handler,
432 'Char' => \&char_handler,
433 );
434
435 eval { $parser->parse($xml) };
436
437 if ( $@ ) {
438 $racvision->{'_ERROR'} = "Could not parse XML : $@";
439 $racvision->{'_ERROR'} =~ s/\n//g;
440 $racvision->{'_ERROR'} =~ s/at $0 line \d+//g;
441 return 0;
442 }
443
444 return 1;
445
446
447 # start element callback
448 sub start_handler {
449 my ($p, $el, %attr) = @_;
450 $el = uc($el);
451 my $unknownTag = '';
452
453 # logD ("start_handler for <$el>");
454
455 if ($currentTag eq '' && $el ne 'APPTEST') {
456 $unknownTag = $el;
457
458 } elsif ($currentTag eq 'APPTEST' ) {
459 if ( defined($racvision->{$el}) ) {
460 $curDataRef = \$racvision->{$el};
461 } else {
462 $unknownTag = $el;
463 $curDataRef = undef;
464 }
465 } elsif ($currentTag eq 'APPLICATION' ) {
466 if ( defined($racvision->{'APPLICATION'}{$el}) ) {
467 $curDataRef = \$racvision->{'APPLICATION'}{$el};
468 # print Dumper(\%attr);
469 if ($el eq 'TEST' ) {
470 push(@{$racvision->{'APPLICATION'}{'TEST'}}, {'DESCRIPTION' => '' , 'STATE' => '', 'STATE_VAL' => '', 'VALUE' => '', 'VALUE_UNIT' => '', 'AVAILABILITY' => ''});
471
472 if ( defined($attr{'id'}) ) {
473 $racvision->{'APPLICATION'}{'TEST'}[-1]{'ID'} = $attr{'id'};
474 } else {
475 logW ("No id attibute define for the current test");
476 }
477
478 if ( defined($attr{'availability'}) ) {
479 $racvision->{'APPLICATION'}{'TEST'}[-1]{'AVAILABILITY'} = $attr{'availability'};
480 }
481 } elsif ($el eq 'AVAILABILITY' ) {
482 if ( defined($attr{'available'}) ) {
483 $racvision->{'APPLICATION'}{'AVAILABILITY'} = $attr{'available'};
484 }
485 }
486 } else {
487 $unknownTag = $el;
488 $curDataRef = undef;
489 }
490 } elsif ($currentTag eq 'TEST' ) {
491 if ( $el eq 'DESCRIPTION' ) {
492 $curDataRef = \$racvision->{'APPLICATION'}{'TEST'}[-1]{$el};
493 } elsif ($el eq 'STATE' ) {
494 $curDataRef = \$racvision->{'APPLICATION'}{'TEST'}[-1]{$el};
495 if ( defined($attr{'val'}) ) {
496 $racvision->{'APPLICATION'}{'TEST'}[-1]{'STATE_VAL'} = $attr{'val'};
497 } else {
498 logW ("No state val attibute define for the current test");
499 }
500 } elsif ($el eq 'VALUE' ) {
501 $curDataRef = \$racvision->{'APPLICATION'}{'TEST'}[-1]{$el};
502 if ( defined($attr{'unit'}) ) {
503 $racvision->{'APPLICATION'}{'TEST'}[-1]{'VALUE_UNIT'} = $attr{'unit'};
504 }
505 } else {
506 $unknownTag = $el;
507 $curDataRef = undef;
508 }
509 } elsif ($currentTag eq 'PERIOD' ) {
510 if ( defined($racvision->{'APPLICATION'}{'PERIOD'}{$el}) ) {
511 $curDataRef = \$racvision->{'APPLICATION'}{'PERIOD'}{$el};
512 } else {
513 $unknownTag = $el;
514 $curDataRef = undef;
515 }
516 }
517
518 # logD ("ref (curDataRef)=" .ref($curDataRef));
519 if ( $unknownTag ne '' ) {
520 logW ("WARNING: Unknown tag in /".join('/',@xpath).": $el");
521 }
522 # print STDERR 'PATH /'.join('/',@xpath)." >> $el\n";
523
524 $currentTag = $el;
525 push (@xpath, $el);
526 }
527
528 # end element callback
529 sub end_handler {
530 my ($p, $el) = @_;
531 $el = uc($el);
532 # logD ("end_handler for <$el>");
533
534 if ( $currentTag eq $el ) {
535 pop(@xpath);
536 $currentTag = $xpath[-1];
537 } else {
538 logW ("BUG: el=$el currentTag=$currentTag");
539 }
540 $curDataRef = undef;
541 }
542
543 # characters beetwen elements callback
544 sub char_handler {
545 my ($p, $data) = @_;
546 if ( defined($curDataRef) && ref($curDataRef) eq 'SCALAR') {
547 # logD ("Set $currentTag=$data ref=".ref($curDataRef));
548 $$curDataRef = $data;
549 } else {
550 logD ("Can't set value '$data' for currentTag=$currentTag ref=".ref($curDataRef));
551 }
552 }
553
554 }
555 sub logD {
556 print STDERR 'DEBUG: '.$_[0]."\n" if ($DEBUG);
557 }
558 sub logW {
559 print STDERR 'WARNING: '.$_[0]."\n" if ($DEBUG);
560 }
561 # Gettext wrapper
562 sub _gt {
563 return gettext($_[0]);
564 }
565
566
567 __END__
568
569 =head1 NAME
570
571 This Nagios plugins check a specified Racvision url (xml document) returned by an application and parse its content.
572
573
574 =head1 NAGIOS CONGIGURATIONS
575
576 In F<checkcommands.cfg> you have to add :
577
578 define command {
579 command_name check_racvision
580 command_line $USER1$/check_racvision.pl -u $ARG1$
581 }
582
583
584 In F<services.cfg> you just have to add something like :
585
586 define service {
587 host_name www.exemple.org
588 normal_check_interval 10
589 retry_check_interval 5
590 contact_groups linux-admins
591 service_description My great web application
592 check_command check_racvision!http://www.exemple.org/myApplication/racvision
593 }
594
595 =head1 AUTHOR
596
597 Stéphane Urbanovski <stephane.urbanovski@ac-nancy-metz.fr>
598
599 =cut

Properties

Name Value
svn:executable *

  ViewVC Help
Powered by ViewVC 1.1.8