File Coverage

lib/Nagios/StatusLog.pm
Criterion Covered Total %
statement 201 320 62.8
branch 63 142 44.3
condition 2 18 11.1
subroutine 33 47 70.2
pod 16 23 69.5
total 315 550 57.2


line stmt bran cond sub pod time code
1             ###########################################################################
2             # #
3             # Nagios::StatusLog, Nagios::(Service|Host|Program)::Status #
4             # Maintained by Duncan Ferguson #
5             # Written by Albert Tobey #
6             # Copyright 2003-2009, Albert P Tobey #
7             # Copyright 2009, Albert P Tobey and Duncan Ferguson #
8             # #
9             # This program is free software; you can redistribute it and/or modify it #
10             # under the terms of the GNU General Public License as published by the #
11             # Free Software Foundation; either version 2, or (at your option) any #
12             # later version. #
13             # #
14             # This program is distributed in the hope that it will be useful, but #
15             # WITHOUT ANY WARRANTY; without even the implied warranty of #
16             # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU #
17             # General Public License for more details. #
18             # #
19             ###########################################################################
20             package Nagios::StatusLog;
21 2     2   181231 use Carp;
  2         5  
  2         251  
22 2     2   13 use strict qw( subs vars );
  2         6  
  2         75  
23 2     2   10 use warnings;
  2         5  
  2         1483  
24 2     2   2219 use Symbol;
  2         5939  
  2         190  
25 2     2   20 use Scalar::Util;
  2         5  
  2         565  
26              
27             # NOTE: due to CPAN version checks this cannot currently be changed to a
28             # standard version string, i.e. '0.21'
29             our $VERSION = '45';
30              
31             # this is going to be rewritten to use AUTOLOAD + method caching in a future version
32             BEGIN {
33              
34             # first block of items is from Nagios v1, second is new stuff in Nagios v2
35 2     2   98 my %_tags = (
36             Service => [
37             qw(
38             host_name description status current_attempt state_type last_check next_check check_type checks_enabled accept_passive_service_checks event_handler_enabled last_state_change problem_has_been_acknowledged last_hard_state time_ok time_unknown time_warning time_critical last_notification current_notification_number notifications_enabled latency execution_time flap_detection_enabled is_flapping percent_state_change scheduled_downtime_depth failure_prediction_enabled process_performance_data obsess_over_service plugin_output
39             service_description modified_attributes check_command event_handler has_been_checked should_be_scheduled check_execution_time check_latency current_state max_attempts last_hard_state_change last_time_ok last_time_warning last_time_unknown last_time_critical performance_data next_notification no_more_notifications active_checks_enabled passive_checks_enabled acknowledgement_type last_update
40             check_interval check_options check_period current_event_id current_notification_id current_problem_id last_event_id last_problem_id long_plugin_output notification_period retry_interval
41             )
42             ],
43              
44             Host => [
45             qw(
46             host_name status last_check last_state_change problem_has_been_acknowledged time_up time_down time_unreachable last_notification current_notification_number notifications_enabled event_handler_enabled checks_enabled flap_detection_enabled is_flapping percent_state_change scheduled_downtime_depth failure_prediction_enabled process_performance_data plugin_output
47             modified_attributes check_command event_handler has_been_checked should_be_scheduled check_execution_time check_latency current_state last_hard_state check_type performance_data next_check current_attempt max_attempts state_type last_hard_state_change last_time_up last_time_down last_time_unreachable next_notification no_more_notifications acknowledgement_type active_checks_enabled passive_checks_enabled obsess_over_host last_update
48             check_interval check_options check_period current_event_id current_notification_id current_problem_id last_event_id last_problem_id long_plugin_output notification_period retry_interval
49             )
50             ],
51              
52             Program => [
53             qw(
54             program_start nagios_pid daemon_mode last_command_check last_log_rotation enable_notifications execute_service_checks accept_passive_service_checks enable_event_handlers obsess_over_services enable_flap_detection enable_failure_prediction process_performance_data
55             modified_host_attributes modified_service_attributes active_service_checks_enabled passive_service_checks_enabled active_host_checks_enabled passive_host_checks_enabled obsess_over_hosts check_service_freshness check_host_freshness global_host_event_handler global_service_event_handler
56             active_ondemand_host_check_stats active_ondemand_service_check_stats active_scheduled_host_check_stats active_scheduled_service_check_stats cached_host_check_stats cached_service_check_stats external_command_stats high_external_command_buffer_slots next_comment_id next_downtime_id next_event_id next_notification_id next_problem_id parallel_host_check_stats passive_host_check_stats passive_service_check_stats serial_host_check_stats total_external_command_buffer_slots used_external_command_buffer_slots
57             )
58             ],
59             Contact => [
60             qw(
61             contact_name modified_attributes modified_host_attributes modified_service_attributes host_notification_period service_notification_period last_host_notification last_service_notification host_notifications_enabled service_notifications_enabled
62             )
63             ],
64             Servicecomment => [
65             qw(
66             host_name service_description entry_type comment_id source persistent entry_time expires expire_time author comment_data
67             )
68             ],
69             Hostcomment => [
70             qw(
71             host_name entry_type comment_id source persistent entry_time expires expire_time author comment_data
72             )
73             ],
74             Servicedowntime => [
75             qw(
76             host_name service_description downtime_id entry_time start_time end_time triggered_by fixed duration author comment
77             )
78             ],
79             Hostdowntime => [
80             qw(
81             host_name downtime_id entry_time start_time end_time triggered_by fixed duration author comment
82             )
83             ],
84             Info => [qw( created version )]
85             );
86              
87 2         412 GENESIS: {
88 2     2   14 no warnings;
  2         4  
  2         5  
89              
90             # create the Nagios::*::Status packages at compile time
91 2         17 foreach my $key ( keys(%_tags) ) {
92 18         44 my $pkg = 'Nagios::' . $key . '::Status::';
93              
94             # store the list of tags for this package to access later
95 18         22 do { ${"${pkg}tags"} = $_tags{$key} };
  18         26  
  18         169  
96              
97             # modify @ISA for each class
98 18         21 my $isa = do { \@{"${pkg}ISA"} };
  18         18  
  18         94  
99 18         197 push( @$isa, 'Nagios::StatusLog' );
100              
101 18         30 foreach my $method ( @{ $_tags{$key} } ) {
  18         42  
102              
103             # the manually implemented status method is described below
104 432     4   29045 *{"$pkg$method"} = sub { $_[0]->{$method} }
  4         23  
105 436 100       8901 unless $method eq 'status';
106             }
107             }
108             }
109             }
110              
111             =head1 NAME
112              
113             Nagios::StatusLog, Nagios::(Service|Host|Program)::Status - Perl objects to represent the Nagios status file
114              
115             =head1 DESCRIPTION
116              
117             Reads the Nagios status log and returns ::Status objects that can
118             be used to get status information about a host. For Nagios version 2.x logs,
119             pass in the Version => 2.0 parameter to new(). And similarly, pass in the
120             Version => 3.0 parameter to new() for Nagios version 3.x logs.
121              
122             my $log = Nagios::StatusLog->new(
123             Filename => "/var/opt/nagios/status.log",
124             Version => 1.0
125             );
126             $localhost = $log->host( "localhost" );
127             print "status of localhost is now ",$localhost->status(),"\n";
128             $log->update();
129             print "status of localhost is now ",$localhost->status(),"\n";
130              
131             # for Nagios v2.0
132             my $log = Nagios::StatusLog->new(
133             Filename => "/var/opt/nagios/status.dat",
134             Version => 2.0
135             );
136              
137             # for Nagios v3.0
138             my $log = Nagios::StatusLog->new(
139             Filename => "/var/opt/nagios/status.dat",
140             Version => 3.0
141             );
142              
143             =head1 METHODS
144              
145             =over 4
146              
147             =item new()
148              
149             Create a new Nagios::StatusLog instance. The object will
150             be initialized for you (using $self->update()).
151             Nagios::StatusLog->new( "/var/opt/nagios/status.log" );
152              
153             =cut
154              
155             sub new {
156 3     3 1 1129 my $type = shift;
157 3 100       18 my $logfile = $_[0] if ( @_ == 1 );
158 3         7 my $version = 1;
159              
160 3 100       20 if ( @_ % 2 == 0 ) {
161 2         12 my %args = @_;
162 2         14 while ( my ( $param, $value ) = each %args ) {
163 4 100       22 if ( lc $param eq 'filename' ) {
    50          
164 2         10 $logfile = $value;
165             }
166             elsif ( lc $param eq 'version' ) {
167 2         11 $version = int($value);
168             }
169             }
170             }
171              
172 3 50 33     112 if ( !defined($logfile) || !-r $logfile ) {
173 0         0 die "could not open $logfile for reading: $!";
174             }
175              
176 3         40 my $self = bless(
177             { LOGFILE => $logfile,
178             VERSION => $version,
179             INFO => {},
180             CONTACT => {},
181             PROGRAM => {},
182             HOST => {},
183             HOSTCOMMENT => {},
184             HOSTDOWNTIME => {},
185             SERVICE => {},
186             SERVICECOMMENT => {},
187             SERVICEDOWNTIME => {}
188             },
189             $type
190             );
191              
192 3         16 $self->update();
193 3         19 return $self;
194             }
195              
196             =item update()
197              
198             Updates the internal data structures from the logfile.
199             $log->update();
200              
201             =cut
202              
203             sub update {
204 4     4 1 5 my $self = shift;
205 4 100       24 if ( $self->{VERSION} >= 3 ) {
206 1         5 return $self->update_v3(@_);
207             }
208 3 100       9 if ( $self->{VERSION} == 2 ) {
209 1         4 return $self->update_v2(@_);
210             }
211 2         6 return $self->update_v1(@_);
212             }
213              
214             sub update_v1 ($) {
215 2     2 0 3 my $self = shift;
216              
217             # break the line down into a hash, return a reference
218             sub hashline ($ $ $) {
219 30     30 0 40 my ( $line, $no, $ar ) = @_;
220 30         219 my @parts = split( /;/, $$line, $no + 1 );
221              
222             # create the hash using the constant array (defined at top
223             # of this file) and the split line
224 30         82 my %data = map { $ar->[$_] => $parts[$_] } 0 .. $no;
  850         1717  
225 30         188 return \%data;
226             }
227              
228 2         7 my $log_fh = gensym;
229 2 50       106 open( $log_fh, "<$self->{LOGFILE}" )
230             || croak "could not open file $self->{LOGFILE} for reading: $!";
231 2         82 my @LOG = <$log_fh>;
232 2         20 close($log_fh);
233              
234 2         42 for ( my $i = 0; $i < @LOG; $i++ ) {
235 32         44 chomp( $LOG[$i] );
236 32         61 $LOG[$i] =~ s/#.*$//;
237 32 100 33     122 next if ( !defined( $LOG[$i] ) || !length( $LOG[$i] ) );
238 30         92 $LOG[$i] =~ m/^(\[\d+])\s+([A-Z]+);(.*)$/;
239 30         79 my ( $ts, $type, $line ) = ( $1, $2, $3 );
240              
241             # set some variables to switch between SERVICES|HOST|PROGRAM
242             # $no must be the number of elements - 1 (because arrays start on 0)
243              
244 30         38 my ( $ldata, $ref ) = ( {}, undef );
245 30 100       55 if ( $type eq 'SERVICE' ) {
    100          
    50          
246              
247             # let the hashline() function do the work of creating the hashref
248 24         54 $ldata = hashline( \$line, 30,
249             Nagios::Service::Status->list_tags() );
250              
251             # if it already exists, we'll copy data after this if/else tree
252 24 100       86 if (!exists(
253             $self->{$type}{ $ldata->{host_name} }
254             { $ldata->{description} }
255             )
256             )
257             {
258 12         29 $self->{$type}{ $ldata->{host_name} }{ $ldata->{description} }
259             = $ldata;
260             }
261              
262             # 1st time we've seen this combination, use the new svc hashref
263             else {
264 12         25 $ref = $self->{$type}{ $ldata->{host_name} }
265             { $ldata->{description} };
266             }
267             }
268             elsif ( $type eq 'HOST' ) {
269 4         14 $ldata
270             = hashline( \$line, 19, Nagios::Host::Status->list_tags() );
271 4 100       14 if ( !exists( $self->{$type}{ $ldata->{host_name} } ) ) {
272 2         6 $self->{$type}{ $ldata->{host_name} } = $ldata;
273             }
274             else {
275 2         5 $ref = $self->{$type}{ $ldata->{host_name} };
276             }
277             }
278             elsif ( $type eq 'PROGRAM' ) {
279 2         14 $ldata = hashline( \$line, 12,
280             Nagios::Program::Status->list_tags() );
281 2 50       8 if ( !defined( $self->{$type} ) ) {
282 0         0 $self->{$type} = $ldata;
283             }
284             else {
285 2         3 $ref = $self->{$type};
286             }
287             }
288 0         0 else { croak "unknown tag ($type) in logfile"; }
289              
290             # update existing data without changing the location the reference points to
291 30 100       71 if ( defined($ref) ) {
292 16         62 foreach my $key ( keys(%$ldata) ) {
293 438         543 $ref->{$key} = $ldata->{$key};
294             }
295             }
296             }
297 2         13 1;
298             }
299              
300             # be compatible with StatusLog which makes sure that references
301             # held in client code remain valid during update (also prevents
302             # some memory leaks)
303             sub _copy {
304 20     20   25 %{ $_[1] } = %{ $_[0] };
  20         615  
  20         114  
305             }
306              
307             sub update_v2 ($) {
308 1     1 0 2 my $self = shift;
309              
310             my %handlers = (
311             host => sub {
312 1     1   2 my $item = shift;
313 1         2 my $host = $item->{host_name};
314 1 50       6 if ( !exists $self->{HOST}{$host} ) {
315 1         4 $self->{HOST}{$host} = {};
316             }
317 1         4 _copy( $item, $self->{HOST}{$host} );
318             },
319             service => sub {
320 11     11   13 my $item = shift;
321 11         17 my $host = $item->{host_name};
322 11         14 my $svc = $item->{service_description};
323              
324 11 50       29 if ( !exists $self->{SERVICE}{$host}{$svc} ) {
325 11         41 $self->{SERVICE}{$host}{$svc} = {};
326             }
327 11         24 _copy( $item, $self->{SERVICE}{$host}{$svc} );
328             },
329             info => sub {
330 1     1   5 _copy( shift, $self->{INFO} );
331             },
332             program => sub {
333 1     1   4 _copy( shift, $self->{PROGRAM} );
334             }
335              
336 1         13 );
337              
338 1         5 my $log_fh = gensym;
339 1 50       63 open( $log_fh, "<$self->{LOGFILE}" )
340             || croak "could not open file $self->{LOGFILE} for reading: $!";
341              
342             # change the first line of the RE to this:
343             # (info|program|host|service) \s* {(
344             # to make it a bit more careful, but it has a measurable cost on runtime
345 1         5 my $entry_re = qr/
346             # capture the type into $1
347             (\w+) \s*
348             # capture all of the text between the brackets into $2
349             {( .*? )}
350             /xs;
351              
352 1         449 my @lines = <$log_fh>;
353 1         129 my $file = "@lines";
354              
355             #Drop comments if we don't need them as it should speed things up a little bit.
356             #Comment out the line below if you do want to keep comments
357 1         123 $file =~ s/#.*\n//mg;
358 1         354 $file =~ s/[\r\n]+\s*/\n/g; # clean up whitespace and newlines
359              
360 1         11 while ( $file =~ /$entry_re/g ) {
361 14         54 ( my $type, my $text ) = ( $1, $2 );
362 14         165 my %item = map { split /\s*=\s*/, $_, 2 } split /\n/, $text;
  526         1516  
363 14         106 $handlers{$type}->( \%item );
364             }
365              
366 1         18 close($log_fh);
367              
368 1         44 1;
369             }
370              
371             sub update_v3 ($) {
372 1     1 0 2 my $self = shift;
373              
374             my %handlers = (
375             hoststatus => sub {
376 1     1   2 my $item = shift;
377 1         4 my $host = $item->{host_name};
378 1 50       6 if ( !exists $self->{HOST}{$host} ) {
379 1         6 $self->{HOST}{$host} = {};
380             }
381 1         5 _copy( $item, $self->{HOST}{$host} );
382             },
383             servicestatus => sub {
384 1     1   3 my $item = shift;
385 1         4 my $host = $item->{host_name};
386 1         2 my $svc = $item->{service_description};
387              
388 1 50       8 if ( !exists $self->{SERVICE}{$host}{$svc} ) {
389 1         5 $self->{SERVICE}{$host}{$svc} = {};
390             }
391 1         13 _copy( $item, $self->{SERVICE}{$host}{$svc} );
392             },
393             contactstatus => sub {
394 2     2   3 my $item = shift;
395 2         11 my $contact = $item->{contact_name};
396 2 50       8 if ( !exists $self->{CONTACT}{$contact} ) {
397 2         5 $self->{CONTACT}{$contact} = {};
398             }
399 2         5 _copy( $item, $self->{CONTACT}{$contact} );
400             },
401             info => sub {
402 1     1   5 _copy( shift, $self->{INFO} );
403             },
404             programstatus => sub {
405 1     1   5 _copy( shift, $self->{PROGRAM} );
406             },
407              
408             # Hosts & services can each have multiple comments & downtime, distinguished only by their Id:
409             servicecomment => sub {
410 0     0   0 my $item = shift;
411 0         0 my $host = $item->{host_name};
412 0         0 my $svc = $item->{service_description};
413 0         0 my $id = $item->{comment_id};
414              
415 0 0       0 if ( !exists $self->{SERVICECOMMENT}{$host}{$svc}{$id} ) {
416 0         0 $self->{SERVICECOMMENT}{$host}{$svc}{$id} = {};
417             }
418 0         0 _copy( $item, $self->{SERVICECOMMENT}{$host}{$svc}{$id} );
419             },
420             hostcomment => sub {
421 0     0   0 my $item = shift;
422 0         0 my $host = $item->{host_name};
423 0         0 my $id = $item->{comment_id};
424              
425 0 0       0 if ( !exists $self->{HOSTCOMMENT}{$host}{$id} ) {
426 0         0 $self->{HOSTCOMMENT}{$host}{$id} = {};
427             }
428 0         0 _copy( $item, $self->{HOSTCOMMENT}{$host}{$id} );
429             },
430             servicedowntime => sub {
431 0     0   0 my $item = shift;
432 0         0 my $host = $item->{host_name};
433 0         0 my $svc = $item->{service_description};
434 0         0 my $id = $item->{downtime_id};
435              
436 0 0       0 if ( !exists $self->{SERVICEDOWNTIME}{$host}{$svc}{$id} ) {
437 0         0 $self->{SERVICEDOWNTIME}{$host}{$svc}{$id} = {};
438             }
439 0         0 _copy( $item, $self->{SERVICEDOWNTIME}{$host}{$svc}{$id} );
440             },
441             hostdowntime => sub {
442 0     0   0 my $item = shift;
443 0         0 my $host = $item->{host_name};
444 0         0 my $id = $item->{downtime_id};
445              
446 0 0       0 if ( !exists $self->{HOSTDOWNTIME}{$host}{$id} ) {
447 0         0 $self->{HOSTDOWNTIME}{$host}{$id} = {};
448             }
449 0         0 _copy( $item, $self->{HOSTDOWNTIME}{$host}{$id} );
450             },
451              
452 1         57 );
453              
454 1         7 my $log_fh = gensym;
455 1 50       64 open( $log_fh, "<$self->{LOGFILE}" )
456             || croak "could not open file $self->{LOGFILE} for reading: $!";
457              
458 1         28 my %valid_types = map { ( $_ => 1 ) }
  9         22  
459             qw(info programstatus hoststatus servicestatus contactstatus servicecomment hostcomment servicedowntime hostdowntime);
460 1         2 my $entry = '';
461 1         2 my %attributes;
462 1         2 my $type = 0;
463 1         37 while ( my $line = <$log_fh> ) {
464 198 100       1558 next if ( $line =~ /^\s*#|^\s*$/ );
465 185 100       687 if ( $line =~ /\s*(\w+)=(.*)$/ ) {
    100          
    50          
466 173         777 $attributes{$1} = $2;
467             }
468             elsif ( $line =~ /^\s*(\w+)\s*{\s*$/ ) {
469 6         32 %attributes = ();
470 6 50       21 if ( exists $valid_types{$1} ) {
471 6         23 $type = $1;
472             }
473             else {
474 0         0 $type = 0;
475             }
476             }
477             elsif ( $line =~ /^\s*}\s*$/ ) {
478             # Only save the object if it is a valid type
479 6 50       13 if ($type) {
480 6         23 $handlers{$type}->( \%attributes );
481             }
482             }
483             }
484              
485 1         15 close($log_fh);
486              
487 1         46 1;
488             }
489              
490             sub list_tags {
491 30 50   30 0 90 my $type = ref( $_[0] ) ? ref( $_[0] ) : $_[0];
492 30         23 my $listref = ${"$type\::tags"};
  30         68  
493 30 50       68 return wantarray ? @$listref : $listref;
494             }
495              
496             =item service()
497              
498             Returns a Nagios::Service::Status object. Input arguments can be a host_name and description list, or a Nagios::Service object.
499              
500             my $svc_stat = $log->service( "localhost", "SSH" );
501             my $svc_stat = $log->service( $localhost_ssh_svc_object );
502              
503             Nagios::Service::Status has the following accessor methods (For V1):
504             host_name
505             description
506             status
507             current_attempt
508             state_type
509             last_check next_check
510             check_type
511             checks_enabled
512             accept_passive_service_checks
513             event_handler_enabled
514             last_state_change
515             problem_has_been_acknowledged
516             last_hard_state
517             time_ok
518             current_notification_number
519             time_warning
520             time_critical
521             process_performance_data
522             notifications_enabled
523             latency
524             scheduled_downtime_depth
525             is_flapping
526             plugin_output
527             percent_state_change
528             execution_time
529             time_unknown
530             failure_prediction_enabled
531             last_notification
532             obsess_over_service
533             flap_detection_enabled
534              
535             =cut
536              
537             sub service {
538 5     5 1 3166 my ( $self, $host, $service ) = @_;
539              
540 5 50       23 if ( ref($host) eq 'Nagios::Host' ) {
541 0         0 $host = $host->host_name;
542             }
543              
544             # allow just a service to be passed in
545 5 50       16 if ( ref($host) eq 'Nagios::Service' ) {
546 0         0 $service = $host;
547 0         0 $host = $service->host_name;
548             }
549 5 50       16 if ( ref($service) eq 'Nagios::Service' ) {
550 0         0 $service = $service->service_description;
551             }
552              
553 5 50       21 confess "host \"$host\" does not seem to be valid"
554             if ( !$self->{SERVICE}{$host} );
555 5 50       24 confess "service \"$service\" does not seem to be valid on host \"$host\""
556             if ( !$self->{SERVICE}{$host}{$service} );
557              
558 5         20 $self->{SERVICE}{$host}{$service}{__parent} = $self;
559 5         23 Scalar::Util::weaken($self->{SERVICE}{$host}{$service}{__parent});
560 5         33 bless( $self->{SERVICE}{$host}{$service}, 'Nagios::Service::Status' );
561             }
562              
563             =item list_services()
564              
565             Returns an array of all service descriptions in the status log. Services that
566             may be listed on more than one host are only listed once here.
567              
568             my @all_services = $log->list_services;
569              
570             =cut
571              
572             sub list_services {
573 1     1 1 2 my $self = shift;
574 1         2 my %list = ();
575 1         2 foreach my $host ( keys %{ $self->{SERVICE} } ) {
  1         3  
576 1         1 foreach my $service ( keys %{ $self->{SERVICE}{$host} } ) {
  1         6  
577 11         17 $list{$service} = 1;
578             }
579             }
580 1         9 return keys %list;
581             }
582              
583             =item list_services_on_host()
584              
585             Returns an array of services descriptions for a given host.
586              
587             my @host_services = $log->list_services_on_host($hostname);
588             my @host_services = $log->list_services_on_host($nagios_object);
589              
590             =cut
591              
592             sub list_services_on_host {
593 0     0 1 0 my ( $self, $host ) = @_;
594 0 0 0     0 if ( ref($host) && UNIVERSAL::can( $host, 'host_name' ) ) {
595 0         0 $host = $host->host_name;
596             }
597 0         0 return keys %{ $self->{SERVICE}{$host} };
  0         0  
598             }
599              
600             =item serviceproblems()
601              
602             Returns a hash of all services that are not in an OK state
603              
604             my %serviceproblems = $log->serviceproblems();
605              
606             =cut
607              
608             sub serviceproblems {
609 0     0 1 0 my ( $self, $host, $service ) = @_;
610 0         0 my %list = ();
611              
612 0         0 $self->{SERVICE}{$host}{$service}{__parent} = $self;
613 0         0 Scalar::Util::weaken($self->{SERVICE}{$host}{$service}{__parent});
614            
615 0         0 foreach my $host ( keys %{ $self->{SERVICE} } ) {
  0         0  
616 0         0 foreach my $service ( keys %{ $self->{SERVICE}{$host} } ) {
  0         0  
617 0 0       0 $list{$host}{$service} = $self->{SERVICE}{$host}{$service} unless $self->{SERVICE}{$host}{$service}{current_state} == 0;
618             }
619             }
620 0         0 return %list;
621             }
622              
623             =item host()
624              
625             Returns a Nagios::Host::Status object. Input can be a simple host_name, a Nagios::Host object, or a Nagios::Service object.
626              
627             my $hst_stat = $log->host( 'localhost' );
628             my $hst_stat = $log->host( $host_object );
629             my $hst_stat = $log->host( $svc_object );
630              
631             Nagios::Host::Status has the following accessor methods (for V1):
632             host_name
633             status
634             last_check
635             last_state_change
636             problem_has_been_acknowledged
637             time_up
638             time_down
639             time_unreachable
640             last_notification
641             current_notification_number
642             notifications_enabled
643             event_handler_enabled
644             checks_enabled
645             flap_detection_enabled
646             is_flapping
647             percent_state_change
648             scheduled_downtime_depth
649             failure_prediction_enabled
650             process_performance_data
651             plugin_output
652              
653             =cut
654              
655             sub host {
656 3     3 1 5108 my ( $self, $host ) = @_;
657              
658 3 50       28 if ( ref($host) =~ /^Nagios::(Host|Service)$/ ) {
659 0         0 $host = $host->host_name;
660             }
661              
662 3 50       20 confess "host \"$host\" does not seem to be valid"
663             if ( !$self->{HOST}{$host} );
664              
665 3         15 $self->{HOST}{$host}{__parent} = $self;
666 3         20 Scalar::Util::weaken($self->{HOST}{$host}{__parent});
667 3         21 bless( $self->{HOST}{$host}, 'Nagios::Host::Status' );
668             }
669              
670             =item list_hosts()
671              
672             Returns a simple array of host names (no objects).
673              
674             my @hosts = $log->list_hosts;
675              
676             =cut
677              
678 0     0 1 0 sub list_hosts { keys %{ $_[0]->{HOST} }; }
  0         0  
679              
680             =item list_hostdowntime()
681              
682             Returns a simple array of host downtimes (no objects)
683              
684             my @hostdowntimes = $log->list_hostdowntime;
685              
686             =cut
687              
688 0     0 1 0 sub list_hostdowntime { keys %{ $_[0]->{HOSTDOWNTIME} }; }
  0         0  
689              
690             =item info() [Nagios v2 & v3 logs only]
691              
692             Returns a Nagios::Info::Status object. It only has two methods, created()
693             and version().
694              
695             my $i = $log->info;
696             printf "Logfile created at %s unix epoch time for Nagios verion %s\n",
697             $i->created,
698             $i->version;
699              
700             =cut
701              
702             sub info {
703 1     1 1 2 my $self = shift;
704 1         7 return bless $self->{INFO}, 'Nagios::Info::Status';
705             }
706              
707             =item contact() [Nagios v3 logs only]
708              
709             Returns a Nagios::Contact::Status object. Input can be a simple contact_name, or a Nagios::Contact object.
710              
711             my $c = $log->contact( 'john' );
712             my $c = $log->contact( $contact_object );
713              
714             Nagios::Contact::Status has the following accessor methods (for v3):
715             contact_name
716             modified_attributes
717             modified_host_attributes
718             modified_service_attributes
719             host_notification_period
720             service_notification_period
721             last_host_notification
722             last_service_notification
723             host_notifications_enabled
724             service_notifications_enabled
725              
726             =cut
727              
728             sub contact {
729 2     2 1 10872245 my ( $self, $name ) = @_;
730              
731 2 50       10 $name = $name->contact_name if ( ref($name) eq 'Nagios::Contact' );
732              
733 2 50       12 return undef if ( !$self->{CONTACT}{$name} );
734              
735 2         14 bless( $self->{CONTACT}{$name}, 'Nagios::Contact::Status' );
736             }
737              
738             =item hostcomment() [Nagios v3 logs only]
739              
740             Returns a Nagios::Hostcomment::Status object. Input can be a simple host_name, or a Nagios::Host or Nagios::Service object.
741              
742             my $c = $log->hostcomment( 'localhost' );
743             my $c = $log->hostcomment( $localhost_object );
744             my $c = $log->hostcomment( $localhost_service_object );
745             foreach my $id (sort keys %$c) {
746             printf "Host %s has a comment[$id] made by %s on %s: %s",
747             $c->{$id}->host_name, $c->{$id}->author, scalar localtime $c->{$id}->entry_time, $c->{$id}->comment_data;
748             }
749              
750             Nagios::Hostcomment::Status is a perl HASH, keyed with the Nagios comment IDs, where each ID has the following accessor methods (for v3):
751             host_name
752             entry_type
753             comment_id
754             source
755             persistent
756             entry_time
757             expires
758             expire_time
759             author
760             comment_data
761              
762             =cut
763              
764             sub hostcomment {
765 0     0 1 0 my ( $self, $host ) = @_;
766              
767 0 0       0 $host = $host->host_name if ( ref($host) =~ /^Nagios::(Host|Service)$/ );
768              
769 0 0       0 return undef if ( !$self->{HOSTCOMMENT}{$host} );
770 0         0 foreach my $id ( keys %{ $self->{HOSTCOMMENT}{$host} } ) {
  0         0  
771 0         0 bless( $self->{HOSTCOMMENT}{$host}{$id},
772             'Nagios::Hostcomment::Status' );
773             }
774 0         0 return $self->{HOSTCOMMENT}{$host};
775             }
776              
777             =item servicecomment() [Nagios v3 logs only]
778              
779             Returns a Nagios::Servicecomment::Status object. Input can be a simple host_name or Nagios::Host object with
780             a service description or Nagios::Service object, or just a Nagios::Service object by itself.
781              
782             my $c = $log->servicecomment( 'localhost', 'SSH' );
783             my $c = $log->servicecomment( $localhost_object, $localhost_ssh_svc_object );
784             my $c = $log->servicecomment( $localhost_ssh_svc_object );
785             foreach my $id (sort keys %$c) {
786             printf "Service %s on %s has a comment[$id] made by %s on %s: %s",
787             $c->{$id}->service_description, $c->{$id}->host_name, $c->{$id}->author, scalar localtime $c->{$id}->entry_time, $c->{$id}->comment_data;
788             }
789              
790             Nagios::Servicecomment::Status is a perl HASH, keyed with the Nagios comment IDs, where each ID has the following accessor methods (for v3):
791             host_name
792             service_description
793             entry_type
794             comment_id
795             source
796             persistent
797             entry_time
798             expires
799             expire_time
800             author
801             comment_data
802              
803             =cut
804              
805             sub servicecomment {
806 0     0 1 0 my ( $self, $host, $service ) = @_;
807              
808 0 0       0 $host = $host->host_name if ( ref($host) eq 'Nagios::Host' );
809              
810             # allow just a service to be passed in
811 0 0       0 if ( ref($host) eq 'Nagios::Service' ) {
812 0         0 $service = $host;
813 0         0 $host = $service->host_name;
814             }
815 0 0       0 $service = $service->service_description
816             if ( ref($service) eq 'Nagios::Service' );
817              
818             return undef
819 0 0 0     0 if ( !$host
      0        
820             || !$service
821             || !$self->{SERVICECOMMENT}{$host}{$service} );
822 0         0 foreach my $id ( keys %{ $self->{SERVICECOMMENT}{$host}{$service} } ) {
  0         0  
823 0         0 bless( $self->{SERVICECOMMENT}{$host}{$service}{$id},
824             'Nagios::Servicecomment::Status' );
825             }
826 0         0 return $self->{SERVICECOMMENT}{$host}{$service};
827             }
828              
829             =item hostdowntime() [Nagios v3 logs only]
830              
831             Returns a Nagios::Hostdowntime::Status object. Input can be a simple host_name, or a Nagios::Host or Nagios::Service object.
832              
833             my $d = $log->hostdowntime( 'localhost' );
834             my $d = $log->hostdowntime( $localhost_object );
835             my $d = $log->hostdowntime( $localhost_service_object );
836             foreach my $id (sort keys %$d) {
837             printf "Host %s has scheduled downtime[$id] made by %s on %s for %.1f hours [%s - %s]: %s",
838             $d->{$id}->host_name, $d->{$id}->author, scalar localtime $d->{$id}->entry_time, ($d->{$id}->duration)/3600.0,
839             scalar localtime $d->{$id}->start_time, scalar localtime $d->{$id}->end_time, $d->{$id}->comment;
840             }
841              
842             Nagios::Hostdowntime::Status is a perl HASH, keyed with the Nagios downtime IDs, where each ID has the following accessor methods (for v3):
843             host_name
844             downtime_id
845             entry_time
846             start_time
847             end_time
848             triggered_by
849             fixed
850             duration
851             author
852             comment
853              
854             =cut
855              
856             sub hostdowntime {
857 0     0 1 0 my ( $self, $host ) = @_;
858              
859 0 0       0 $host = $host->host_name if ( ref($host) =~ /^Nagios::(Host|Service)$/ );
860              
861 0 0       0 return undef if ( !$self->{HOSTDOWNTIME}{$host} );
862 0         0 foreach my $id ( keys %{ $self->{HOSTDOWNTIME}{$host} } ) {
  0         0  
863 0         0 bless(
864             $self->{HOSTDOWNTIME}{$host}{$id},
865             'Nagios::Hostdowntime::Status'
866             );
867             }
868 0         0 return $self->{HOSTDOWNTIME}{$host};
869             }
870              
871             =item servicedowntime() [Nagios v3 logs only]
872              
873             Returns a Nagios::Servicedowntime::Status object. Input can be a simple host_name or Nagios::Host object with
874             a service description or Nagios::Service object, or just a Nagios::Service object by itself.
875              
876             my $c = $log->servicedowntime( 'localhost', 'SSH' );
877             my $c = $log->servicedowntime( $localhost_object, $localhost_ssh_svc_object );
878             my $c = $log->servicedowntime( $localhost_ssh_svc_object );
879             foreach my $id (sort keys %$d) {
880             printf "Service %s on %s has scheduled downtime[$id] made by %s on %s for %.1f hours [%s - %s]: %s",
881             $d->{$id}->service_description, $d->{$id}->host_name, $d->{$id}->author, scalar localtime $d->{$id}->entry_time, ($d->{$id}->duration)/3600.0,
882             scalar localtime $d->{$id}->start_time, scalar localtime $d->{$id}->end_time, $d->{$id}->comment;
883             }
884              
885             Nagios::Servicedowntime::Status is a perl HASH, keyed with the Nagios downtime IDs, where each ID has the following accessor methods (for v3):
886             host_name
887             service_description
888             downtime_id
889             entry_time
890             start_time
891             end_time
892             triggered_by
893             fixed
894             duration
895             author
896             comment
897              
898             =cut
899              
900             sub servicedowntime {
901 0     0 1 0 my ( $self, $host, $service ) = @_;
902              
903 0 0       0 $host = $host->host_name if ( ref($host) eq 'Nagios::Host' );
904              
905             # allow just a service to be passed in
906 0 0       0 if ( ref($host) eq 'Nagios::Service' ) {
907 0         0 $service = $host;
908 0         0 $host = $service->host_name;
909             }
910 0 0       0 $service = $service->service_description
911             if ( ref($service) eq 'Nagios::Service' );
912              
913 0 0       0 return undef if ( !$self->{SERVICEDOWNTIME}{$host}{$service} );
914 0         0 foreach my $id ( keys %{ $self->{SERVICEDOWNTIME}{$host}{$service} } ) {
  0         0  
915 0         0 bless( $self->{SERVICEDOWNTIME}{$host}{$service}{$id},
916             'Nagios::Servicedowntime::Status' );
917             }
918 0         0 return $self->{SERVICEDOWNTIME}{$host}{$service};
919             }
920              
921             =item program()
922              
923             Returns a Nagios::Program::Status object. No arguments.
924              
925             my $prog_st = $log->program;
926              
927             Nagios::Program::Status has the following accessor methods (For V1):
928             program_start
929             nagios_pid
930             daemon_mode
931             last_command_check
932             last_log_rotation
933             enable_notifications
934             execute_service_checks
935             accept_passive_service_checks
936             enable_event_handlers
937             obsess_over_services
938             enable_flap_detection
939             enable_failure_prediction
940             process_performance_data
941              
942             =cut
943              
944 1     1 1 6 sub program ($) { bless( $_[0]->{PROGRAM}, 'Nagios::Program::Status' ); }
945              
946             sub write {
947 0     0 0 0 my ( $self, $filename ) = @_;
948 0         0 my $ts = time;
949              
950 0         0 my $fh = gensym;
951 0 0       0 open( $fh, ">$filename" )
952             || die "could not open file \"$filename\" for writing: $!";
953              
954 0         0 print $fh, "[$ts] PROGRAM;",
955             Nagios::Program::Status->csvline( $self->{PROGRAM} ), "\n";
956              
957 0         0 foreach my $host ( keys %{ $self->{HOST} } ) {
  0         0  
958 0         0 print $fh "[$ts] HOST;",
959             Nagios::Host::Status->csvline( $self->{HOST}{$host} ), "\n";
960             }
961 0         0 foreach my $host ( keys %{ $self->{SERVICE} } ) {
  0         0  
962 0         0 foreach my $svc ( keys %{ $self->{SERVICE}{$host} } ) {
  0         0  
963 0         0 my $ref = $self->{SERVICE}{$host}{$svc};
964 0         0 print $fh "[$ts] SERVICE;",
965             Nagios::Service::Status->csvline($ref), "\n";
966             }
967             }
968              
969 0         0 close($fh);
970             }
971              
972             sub csvline {
973 0     0 0 0 my $self = shift;
974 0   0     0 my $data = shift || $self;
975 0         0 join( ';', map { $data->{$_} } ( $self->list_tags ) );
  0         0  
976             }
977              
978             =back
979              
980             =head1 STRUCTURE
981              
982             This module contains 4 packages: Nagios::StatusLog, Nagios::Host::Status,
983             Nagios::Service::Status, and Nagios::Program::Status. The latter 3 of
984             them are mostly generated at compile-time in the BEGIN block. The
985             accessor methods are real subroutines, not AUTOLOAD, so making a ton
986             of calls to this module should be fairly quick. Also, update() is set
987             up to only do what it says - updating from a fresh logfile should not
988             invalidate your existing ::Status objects.
989              
990             =head1 AUTHOR
991              
992             Al Tobey
993              
994             =head1 SEE ALSO
995              
996             Nagios::Host Nagios::Service
997              
998             =cut
999              
1000             package Nagios::Service::Status;
1001              
1002             our $VERSION = '0.1';
1003              
1004             # Nagios 2.x has current_state instead of status, but since anybody
1005             # using this module is probably using status and does not want to
1006             # mess around with converting the integer, this method wraps it up
1007             # to return like Nagios 1.x did.
1008             sub status {
1009 2     2   5 my $self = shift;
1010 2 50       9 if ( $self->{__parent}{VERSION} > 1.9999999 ) {
1011 2 50       6 if ( $self->{current_state} == 0 ) {
    0          
    0          
    0          
1012 2 100       7 if ( $self->{has_been_checked} == 0 ) {
1013 1         4 return 'PENDING';
1014             }
1015             else {
1016 1         5 return 'OK';
1017             }
1018             }
1019             elsif ( $self->{current_state} == 1 ) {
1020 0         0 return 'WARNING';
1021             }
1022             elsif ( $self->{current_state} == 2 ) {
1023 0         0 return 'CRITICAL';
1024             }
1025             elsif ( $self->{current_state} == 3 ) {
1026 0         0 return 'UNKNOWN';
1027             }
1028             else {
1029 0         0 return "Unknown Status '$self->{current_state}'";
1030             }
1031             }
1032             else {
1033 0         0 return $self->{status};
1034             }
1035             }
1036              
1037             package Nagios::Host::Status;
1038             our $VERSION = '0.1';
1039              
1040             # same deal as Nagios::Service::Status::status()
1041             sub status {
1042 1     1   3 my $self = shift;
1043 1 50       5 if ( $self->{__parent}{VERSION} > 1.9999999 ) {
1044 1 50       5 if ( $self->{current_state} == 0 ) {
    0          
    0          
1045 1 50       7 if ( $self->{has_been_checked} == 0 ) {
1046 0         0 return 'PENDING';
1047             }
1048             else {
1049 1         155 return 'OK';
1050             }
1051             }
1052             elsif ( $self->{current_state} == 1 ) {
1053 0           return 'DOWN';
1054             }
1055             elsif ( $self->{current_state} == 2 ) {
1056 0           return 'UNREACHABLE';
1057             }
1058             else {
1059 0           return "Unknown Status '$self->{current_state}'";
1060             }
1061             }
1062             else {
1063 0           return $self->{status};
1064             }
1065             }
1066              
1067             package Nagios::Program::Status;
1068             our $VERSION = '0.1';
1069              
1070             package Nagios::Info::Status;
1071             our $VERSION = '0.1';
1072              
1073             1;
1074