File Coverage

blib/lib/Nagios/Clientstatus.pm
Criterion Covered Total %
statement 98 103 95.1
branch 19 20 95.0
condition 8 12 66.6
subroutine 20 22 90.9
pod 4 4 100.0
total 149 161 92.5


line stmt bran cond sub pod time code
1             package Nagios::Clientstatus;
2 3     3   290616 use strict;
  3         9  
  3         465  
3 3     3   20 use warnings;
  3         6  
  3         261  
4 3     3   8322 use Getopt::Long;
  3         80993  
  3         22  
5 3     3   7029 use Data::Dumper;
  3         38242  
  3         270  
6 3     3   2745 use Log::Log4perl;
  3         85838  
  3         30  
7 3     3   140 use Exporter;
  3         5  
  3         127  
8              
9             BEGIN {
10 3     3   16 use Exporter ();
  3         6  
  3         65  
11 3     3   18 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
  3         6  
  3         356  
12 3     3   7 $VERSION = '0.06';
13 3         51 @ISA = qw(Exporter);
14              
15 3         7 @EXPORT = qw();
16 3         26 @EXPORT_OK = qw();
17 3         3379 %EXPORT_TAGS = ();
18             }
19              
20             =head1 NAME
21              
22             Nagios::Clientstatus - Framework for Nagios check-service programs
23              
24             =head1 SYNOPSIS
25              
26             use Nagios::Clientstatus;
27             # This is needed for logging
28             use Log::Log4perl qw/:easy/;
29             Log::Log4perl->easy_init($ERROR);
30             my $logger = Log::Log4perl->get_logger;
31            
32             # Arguments to the program are:
33             # --critical=40 --warning=35 --hostname=server1.zdf --sensor_nr=4
34             my $version = "0.01";
35             my $ncli = Nagios::Clientstatus->new(
36             help_subref => \&help,
37             version => $version,
38             # default is that the module checks commandline
39             dont_check_commandline_args => 0, # default
40             mandatory_args => [ "hostname", "sensor_nr", "critical", "warning" ],
41             );
42              
43             # ask only one time, because it's expensive
44             my $temperature = &get_temperature_of_sensor(
45             hostname => $ncli->get_given_arg('hostname'),
46             sensor_nr => $ncli->get_given_arg('sensor_nr'),
47             );
48              
49             # Message for the user to read
50             my $msg;
51             my $status;
52              
53             # strange case
54             if ( ( !defined $temperature )
55             || ( defined $temperature && $temperature eq "" ) )
56             {
57             $status = "unknown";
58             $msg = "Could not get temperature from sensor";
59             }
60             else {
61              
62             # We got a temperature
63             # worst case first
64             if ( $temperature > $ncli->get_given_arg('critical') ) {
65             $status = "critical";
66             }
67             elsif ( $temperature > $ncli->get_given_arg('warning') ) {
68             $status = "warning";
69             }
70             else {
71             $status = "ok";
72             }
73             $msg = sprintf "Temperature is %s degrees Celsius", $temperature;
74             }
75             printf "%s - %s", uc($status), $msg;
76             exit $ncli->exitvalue($status);
77              
78             sub help {
79             print "Usage:\n";
80             print "$0 --critical=40 --warning=35"
81             . " --hostname=server1.zdf --sensor_nr=4";
82              
83             # When supplying help you should exit, use class-method
84             # because we don't have an object
85             exit Nagios::Clientstatus::exitvalue( 'unknown' );
86             }
87              
88             sub get_temperature_of_sensor {
89             my(%args) = @_;
90             print "You should supply something useful here.\n";
91             printf "Hostname: %s, sensor: %s\n",
92             $args{hostname}, $args{sensor_nr};
93             print "Please enter a temperature: ";
94             my $temperature = ;
95             chomp $temperature;
96             return $temperature;
97              
98             };
99              
100              
101             =head1 DESCRIPTION
102              
103             Create a program to check the function of some service or device
104             for Nagios. This module helps you to check the mandatory and
105             optional arguments. It helps you to send the right output so that
106             Nagios can check wether the service works ok or not.
107              
108             =head1 METHODS
109              
110             =cut
111              
112             =head2 new
113              
114             Create the object. Immediately check commandline arguments which
115             are mandatory for every Nagios command.
116              
117             Usage:
118              
119             my $ncli = Nagios::Clientstatus->new(
120             help_subref => \&help,
121             version => $version,
122             dont_check_commandline_args => 0, # default
123             # mandatory_args is optional, maybe you don't need any
124             mandatory_args => [ 'url' ],
125             # optional_args is optional, maybe you don't need any
126             optional_args => [ 'carsize' ],
127             );
128              
129             =cut
130              
131             sub new {
132 14     14 1 13017 my ( $class, %args ) = @_;
133 14   33     193 my $new_object = bless(
134             {
135             help_subref => \&help_example,
136             version => $args{version},
137             mandatory_args => [],
138             # help, version, debug: but no arguments here
139             # like --help=specialvalue, only --help
140             optional_default_args => [],
141             # more optional values, all must have a value
142             # like --carsize=medium
143             optional_additional_args => [],
144             given_args => {},
145             dont_check_commandline_args => 0,
146             },
147             ref($class) || $class
148             );
149              
150 14 100       51 if ( exists $args{dont_check_commandline_args} ) {
151 3         15 $new_object->{dont_check_commandline_args} =
152             $args{dont_check_commandline_args};
153             }
154              
155             # help_subref
156 14 100 100     92 unless ( ( exists $args{help_subref} )
157             && ( ref $args{help_subref} eq "CODE" ) )
158             {
159 2         50 print STDERR
160             "Missing ref to help-subroutine. This sub could output this:\n";
161 2         8 $new_object->help_example;
162 2         8 my $msg =
163             sprintf "Mandatory argument help_subref must point"
164             . " to a help-subroutine, but it is a '%s'",
165             ref( $args{help_subref} );
166 2         31 die $msg;
167             }
168 12         30 $new_object->{help_subref} = $args{help_subref};
169              
170             # which optional args could be at the commandline?
171             # The usual ones
172 12         36 $new_object->{optional_default_args} = [ $new_object->_get_optional_default_args ];
173              
174             # The other one the user wants
175             # These arguments must be supplied like this:
176             # --carsize=medium, not valid: --carsize
177 12 100 66     61 if ( ( exists $args{optional_args} )
178             && ( ref $args{optional_args} eq "ARRAY" ) )
179             {
180 3         5 foreach my $optarg ( @{$args{optional_args}} ) {
  3         8  
181 3         5 push @{$new_object->{optional_additional_args}}, $optarg;
  3         1137  
182             }
183             }
184              
185             # are there mandatory arguments?
186 12 100 66     79 if ( ( exists $args{mandatory_args} )
187             && ( ref $args{mandatory_args} eq "ARRAY" ) )
188             {
189 9         11 $new_object->_set_mandatory_args( @{ $args{mandatory_args} } );
  9         36  
190             }
191              
192             # don't set mandatory args, sometimes it's critical when
193             # the service does not run -> very simple
194             # $new_object->_set_mandatory_args( "critical", "warning" );
195              
196 12         38 $new_object->_check_commandline_args;
197 6         26 return $new_object;
198             }
199              
200             sub _logger {
201 12     12   50 return Log::Log4perl->get_logger('Nagios.Clientstatus');
202             }
203              
204             sub _dont_check_commandline_args {
205              
206             # shall any commandline-arg be checked by Getopt::Long?
207 12     12   39 shift->{dont_check_commandline_args};
208             }
209              
210             #=head2_set_mandatory_args
211             #
212             #Remind arguments which user must supply when calling the program.
213             #Can be called several times.
214             #
215             #=cut
216              
217             sub _set_mandatory_args {
218 9     9   22 my ( $self, @args ) = @_;
219 9         12 push @{ $self->{mandatory_args} }, @args;
  9         28  
220             }
221              
222             #=head2 _get_mandatory_args
223             #
224             #Which args MUST be given to the programm? Each argument must have a value, too.
225             #
226             #=cut
227              
228             sub _get_mandatory_args {
229 24     24   31 my $self = shift;
230 24         24 @{ $self->{mandatory_args} };
  24         152  
231             }
232              
233             #=head2 _set_given_args
234             #
235             #Which arguments where given to the program?
236             #
237             #=cut
238              
239             sub _set_given_args {
240 0     0   0 my ( $self, $name, $value ) = @_;
241 0         0 $self->{given_args}->{name} = $value;
242             }
243              
244             =head2 get_given_arg
245              
246             Object-creator can ask for the value of an argument
247             given to the program. This can be a mandatory or
248             an optional argument. Not given optional arguments
249             return undef.
250              
251             When you create the object like this:
252              
253             my $ncli = Nagios::Clientstatus->new(
254             help_subref => \&help,
255             version => $version,
256             mandatory_args => [ 'url' ],
257             optional_args => [ 'carsize' ],
258             );
259              
260             If program is called: checkme --url=xx --carsize=medium
261              
262             # $value -> 'medium'
263             $value = $nc->get_given_arg('carsize');
264            
265             # $value -> 'xx'
266             $value = $nc->get_given_arg('url');
267              
268             # $value -> undef
269             $value = $nc->get_given_arg('carpoolnotgiven');
270              
271             =cut
272              
273             sub get_given_arg {
274 3     3 1 1129 my ( $self, $name ) = @_;
275 3 100       28 return exists $self->{given_args}->{$name}
276             ? $self->{given_args}->{$name}
277             : undef;
278             }
279              
280             #=head2 _check_commandline_args
281             #
282             #There are arguments which must exist when calling a Nagios-checker.
283             #warning|critcal are mandatory, other mandatory were given by new.
284             #
285             #=cut
286              
287             sub _check_commandline_args {
288 12     12   15 my $self = shift;
289 12         31 my $logger = $self->_logger;
290              
291 12         3399 my %getopt_long_arg;
292             my %got_this_option;
293              
294             # shall any commandline-arg be checked by Getopt::Long?
295 12 50       33 if ( $self->_dont_check_commandline_args ) {
296 0         0 $logger->info("Do not check any commandline arguments");
297 0         0 return;
298             }
299              
300             # Build up the argument hash for Getopt::Long
301              
302             # Build the hash for Getopt::Long
303 12         28 foreach ( $self->_get_optional_default_args ) {
304              
305             # Getopt::Long wants a ref to a scalar where value is stored in
306 36         104 $getopt_long_arg{$_} = $got_this_option{$_};
307             }
308              
309             # Maybe there are optional args supplied by new
310             # Must be all like: --carsize=medium (with value)
311 12         34 foreach ($self->_get_optional_additional_args) {
312 3         9 $getopt_long_arg{"$_=s"} = $got_this_option{$_};
313             }
314              
315 12         38 foreach ( $self->_get_mandatory_args ) {
316              
317             # Tell Getopt::Long that there must be an argument
318             # Getopt::Long wants a ref to a scalar where value is stored in
319 9         45 $getopt_long_arg{"$_=s"} = $got_this_option{$_};
320             }
321              
322             # Unusual syntax for daily life in GetOptions
323             # Look in manpage of Getopt::Long for this:
324             # Storing option values in a hash
325             # All given options are stored in hash given as first argument
326             GetOptions(
327 12         67 \%got_this_option,
328              
329             # all possible options are here:
330             keys %getopt_long_arg,
331             );
332              
333             # Now all arguments given to the program are in %got_this_option
334              
335             # Do mandatory args exist?
336 12         6308 my @mand_forgotten;
337 12         39 foreach ( $self->_get_mandatory_args ) {
338 9 100       36 unless ( exists $got_this_option{$_} ) {
339 6         19 push @mand_forgotten, $_;
340             }
341             }
342 12 100       42 if ( scalar @mand_forgotten > 0 ) {
343 6         41 $logger->debug('%getopt_long_arg was: ');
344 6         374 $logger->debug( Dumper( \%getopt_long_arg ) );
345 6         693 $logger->debug('%got_this_option was: ');
346 6         45 $logger->debug( Dumper( \%got_this_option ) );
347              
348 6         863 printf STDERR "Mandatory arguments not given: %s\n",
349             join( ", ", @mand_forgotten );
350 6         30 $self->{help_subref}->();
351 6         335 $self->_exit;
352             }
353              
354             # all arguments where checked, now put them into given_args
355 6         24 $self->{given_args} = \%got_this_option;
356             }
357              
358             =head2 exitvalue
359              
360             Return the value the Nagios-command must return to Nagios.
361             This is the only value which is important for the Nagios state.
362              
363             Use it like this:
364              
365             exit $ncli->exitvalue( $status );
366              
367             or without object as class-method:
368              
369             exit Nagios::Clientstatus::exitvalue( $status );
370              
371              
372             Returnvalue can be a string of these:
373              
374             OK|WARNING|CRITICAL|UNKNOWN
375              
376             =cut
377              
378             sub exitvalue {
379 9     9 1 2134 my $first_arg = shift;
380              
381             # Class-method or object-method?
382 9 100       26 my $status = ref($first_arg) ? shift: $first_arg;
383              
384 9         20 $status = uc $status;
385 9         41 my %nagios_returnvalue = (
386             'OK' => 0,
387             'WARNING' => 1,
388             'CRITICAL' => 2,
389             'UNKNOWN' => 3,
390             );
391 9 100       27 unless ( exists $nagios_returnvalue{$status} ) {
392 1         16 die "Wrong status '$status' to return, status can only be: " . join ",",
393             sort keys %nagios_returnvalue;
394             }
395 8         54 return $nagios_returnvalue{$status};
396             }
397              
398             =head2 help_example
399              
400             Give the user a hint how to use this programm.
401              
402             =cut
403              
404             sub help_example {
405 2     2 1 4 shift;
406 2         25 print <<"EOUSAGE";
407             This is $0
408              
409             Usage:
410              
411             $0 --warning 60 \\
412             --critical 130 \\
413             --your_argument_here_1 xx \\
414             --your_argument_here_2 xx \\
415             [--version]
416              
417             Tell the user what this programm does
418             EOUSAGE
419             }
420              
421             sub _get_optional_default_args {
422 24     24   25 shift;
423 24         107 qw{version help debug};
424             }
425              
426             #=head2 _get_optional_additional_args
427             #
428             #Get a list of args which could be given to the program.
429             #These are the optional args given in new, but not the
430             #default optional args 'help','debug','version'
431             #
432             #=cut
433              
434             sub _get_optional_additional_args {
435 12     12   16 my $self = shift;
436 12         17 @{$self->{optional_additional_args}};
  12         77  
437             }
438              
439             # for testing only, I can overwrite exit
440             # to let the program run after "exiting"
441              
442             sub _exit {
443 0     0     exit;
444             }
445              
446             =head1 AUTHOR
447              
448             Richard Lippmann
449             CPAN ID: HORSHACK
450             horshack@lisa.franken.de
451             http://lena.franken.de
452              
453             =head1 COPYRIGHT
454              
455             This program is free software; you can redistribute
456             it and/or modify it under the same terms as Perl itself.
457              
458             The full text of the license can be found in the
459             LICENSE file included with this module.
460              
461             =cut
462              
463             1;
464