#!/usr/local/bin/perl # gen_flat_status_page # # generate a flat status html page of the nagios services # # by Doke Scott, doke at udel dot edu # # $Header: /home/doke/work/nagios/RCS/gen_flat_status_page,v 1.12 2016/01/07 14:49:11 doke Exp $ # use strict; use warnings; use Getopt::Long; use CGI qw( escapeHTML ); my $duration_file = "/usr/local/nagios/share/flat_status.html"; my $name_file = "/usr/local/nagios/share/flat_status_name.html"; my $objects_file = "/usr/local/nagios/var/objects.cache"; my $status_file = "/usr/local/nagios/var/status.dat"; my $pending_state = 9; # fake state for pending services my $min_host_importance = 200; my $min_service_importance = 200; my $noop = 0; my $verbose = 0; my $help = 0; sub usage { my( $rc ) = @_; print "Usage: $0 [-nvh] -n no-operation, don't actually move the new file into place -v verbose -h this help \n"; exit $rc; } Getopt::Long::Configure ("bundling"); GetOptions( 'n' => \$noop, 'v+' => \$verbose, 'h' => \$help, ); &usage( 0 ) if ( $help ); my( %hosts, %services, $hostname, $now, $nowstr, @num_in_state ); chomp( $hostname = `hostname` ); $now = time; $nowstr = localtime( $now ); read_objectsfile(); read_statusfile(); counts(); gen_duration_page(); gen_name_page(); exit 0; ########################## sub read_objectsfile { open( fH, $objects_file ) || die "can't read-open $objects_file: $!\n"; my $in_service = 0; my $in_host = 0; my $host = undef; my $service = undef; my $importance = 0; my $icon_image = ''; my $nservices = 0; while ( ) { if ( $in_service || $in_host ) { # end of the block? if ( m/^\s*(?#{)}\s*$/ ) { if ( $in_service ) { $verbose && print "host $host, service $service, ", "importance $importance, ", "icon_image $icon_image\n"; if ( $host && $service ) { $services{ "$host/$service" }{ 'host' } = $host; $services{ "$host/$service" }{ 'service' } = $service; $services{ "$host/$service" }{ 'importance' } = $importance; $services{ "$host/$service" }{ 'icon_image' } = $icon_image; $nservices++; } } elsif ( $in_host ) { $verbose && print "host $host, ", "importance $importance, ", "icon_image $icon_image\n"; if ( $host ) { $hosts{ $host }{ 'importance' } = $importance; $hosts{ $host }{ 'total_importance' } = $importance; $hosts{ $host }{ 'icon_image' } = $icon_image; } } $in_service = 0; $in_host = 0; $host = undef; $service = undef; $importance = 0; $icon_image = ''; } elsif ( m/^\s*host_name\s+(\S.*)/ ) { $host = $1; } elsif ( m/^\s*service_description\s+(\S.*)/ ) { $service = $1; } elsif ( m/^\s*importance\s+(\d+)/ ) { $importance = $1; } elsif ( m/^\s*icon_image\s+(\S.*)/ ) { $icon_image = $1; } } elsif ( m/^define service\s*{(?#})/ ) { $in_service = 1; } elsif ( m/^define host\s*{(?#})/ ) { $in_host = 1; } } if ( $verbose ) { print "read $. object lines\n"; printf "found %d host objects\n", scalar( keys %hosts ); printf "found %d service objects\n", $nservices; } close fH; } sub read_statusfile { open( fH, $status_file ) || die "can't read-open $status_file: $!\n"; my $in_servicestatus = 0; my $in_hoststatus = 0; my $in_servicecomment = 0; my $in_hostdowntime = 0; my $in_servicedowntime = 0; my $host = undef; my $service = undef; my $current_state = 0; my $acked = 0; my $state_type = 0; my $current_attempt = 0; my $max_attempts = 0; my $last_state_change = 0; my $last_hard_state_change = 0; my $last_check = 0; my $plugin_output = ''; my $current_notification_number = 0; my $has_been_checked = 0; my $should_be_scheduled = 0; my $comment_id = 0; my $comment = ''; my $author = ''; my $downtime_id = 0; my $entry_time = 0; my $start_time = 0; my $end_time = 0; my $is_in_effect = 0; my $nservices = 0; while ( ) { if ( $in_servicestatus || $in_hoststatus || $in_servicecomment || $in_hostdowntime || $in_servicedowntime ) { # end of the block? if ( m/^\s*(?#{)}\s*$/ ) { if ( $in_servicestatus ) { $verbose && print "host $host, service $service, ", "current_state $current_state, state_type $state_type, ", "acked $acked\n"; if ( $host && $service ) { $services{ "$host/$service" }{ 'host' } = $host; $services{ "$host/$service" }{ 'service' } = $service; $services{ "$host/$service" }{ 'current_state' } = $current_state; $services{ "$host/$service" }{ 'acked' } = $acked; $services{ "$host/$service" }{ 'state_type' } = $state_type; $services{ "$host/$service" }{ 'current_attempt' } = $current_attempt; $services{ "$host/$service" }{ 'max_attempts' } = $max_attempts; $services{ "$host/$service" }{ 'last_state_change' } = $last_state_change; $services{ "$host/$service" }{ 'last_hard_state_change' } = $last_hard_state_change; $services{ "$host/$service" }{ 'last_check' } = $last_check; $services{ "$host/$service" }{ 'plugin_output' } = $plugin_output; $services{ "$host/$service" }{ 'current_notification_number' } = $current_notification_number; $services{ "$host/$service" }{ 'has_been_checked' } = $has_been_checked; $services{ "$host/$service" }{ 'should_be_scheduled' } = $should_be_scheduled; $nservices ++; } } elsif ( $in_hoststatus ) { $verbose && print "host $host, ", "current_state $current_state, state_type $state_type, ", "acked $acked\n"; if ( $host ) { $hosts{ $host }{ 'current_state' } = $current_state; $hosts{ $host }{ 'acked' } = $acked; $hosts{ $host }{ 'state_type' } = $state_type; $hosts{ $host }{ 'current_attempt' } = $current_attempt; $hosts{ $host }{ 'max_attempts' } = $max_attempts; $hosts{ $host }{ 'last_state_change' } = $last_state_change; $hosts{ $host }{ 'last_hard_state_change' } = $last_hard_state_change; $hosts{ $host }{ 'last_check' } = $last_check; $hosts{ $host }{ 'plugin_output' } = $plugin_output; } } elsif ( $in_servicecomment ) { $verbose && print "host $host, service $service, ", "comment_id $comment_id, comment $comment, ", "author $author\n"; if ( $host && $service && $comment_id && $comment ) { $services{ "$host/$service" }{ 'comments' }{ $comment_id }{ 'entry_time' } = $entry_time; $services{ "$host/$service" }{ 'comments' }{ $comment_id }{ 'author' } = $author; $services{ "$host/$service" }{ 'comments' }{ $comment_id }{ 'comment' } = $comment; } } elsif ( $in_hostdowntime ) { $verbose && print "host $host, ", "downtime_id $downtime_id, is_in_effect $is_in_effect, ", "downtime start $start_time, end $end_time, ", "comment $comment, author $author\n"; if ( $host && $downtime_id ) { $hosts{ $host }{ 'downtime' }{ $downtime_id }{ 'entry_time' } = $entry_time; $hosts{ $host }{ 'downtime' }{ $downtime_id }{ 'start_time' } = $start_time; $hosts{ $host }{ 'downtime' }{ $downtime_id }{ 'end_time' } = $end_time; $hosts{ $host }{ 'downtime' }{ $downtime_id }{ 'is_in_effect' } = $is_in_effect; $hosts{ $host }{ 'downtime' }{ $downtime_id }{ 'author' } = $author; $hosts{ $host }{ 'downtime' }{ $downtime_id }{ 'comment' } = $comment; } } elsif ( $in_servicedowntime ) { $verbose && print "host $host, service $service, ", "downtime_id $downtime_id, is_in_effect $is_in_effect, ", "downtime start $start_time, end $end_time, ", "comment $comment, author $author\n"; if ( $host && $service && $downtime_id ) { $services{ "$host/$service" }{ 'downtime' }{ $downtime_id }{ 'entry_time' } = $entry_time; $services{ "$host/$service" }{ 'downtime' }{ $downtime_id }{ 'start_time' } = $start_time; $services{ "$host/$service" }{ 'downtime' }{ $downtime_id }{ 'end_time' } = $end_time; $services{ "$host/$service" }{ 'downtime' }{ $downtime_id }{ 'is_in_effect' } = $is_in_effect; $services{ "$host/$service" }{ 'downtime' }{ $downtime_id }{ 'author' } = $author; $services{ "$host/$service" }{ 'downtime' }{ $downtime_id }{ 'comment' } = $comment; } } $in_servicestatus = 0; $in_hoststatus = 0; $in_servicecomment = 0; $in_hostdowntime = 0; $in_servicedowntime = 0; $host = undef; $service = undef; $current_state = 0; $acked = 0; $state_type = 0; $current_attempt = 0; $max_attempts = 0; $last_state_change = 0; $last_hard_state_change = 0; $last_check = 0; $plugin_output = ''; $current_notification_number = 0; $has_been_checked = 0; $should_be_scheduled = 0; $comment_id = 0; $comment = ''; $author = ''; $downtime_id = 0; $start_time = 0; $end_time = 0; $is_in_effect = 0; } # host stuff elsif ( m/^\s*host_name=(.*)/ ) { $host = $1; } # service status stuff elsif ( m/^\s*service_description=(.*)/ ) { $service = $1; } elsif ( m/^\s*current_state=(\d+)/ ) { $current_state = $1; } elsif ( m/^\s*problem_has_been_acknowledged=(\d+)/ ) { $acked = $1; } elsif ( m/^\s*state_type=(\d+)/ ) { $state_type = $1; } elsif ( m/^\s*current_attempt=(\d+)/ ) { $current_attempt = $1; } elsif ( m/^\s*max_attempts=(\d+)/ ) { $max_attempts = $1; } elsif ( m/^\s*last_state_change=(\d+)/ ) { $last_state_change = $1; } elsif ( m/^\s*last_hard_state_change=(\d+)/ ) { $last_hard_state_change = $1; } elsif ( m/^\s*last_check=(\d+)/ ) { $last_check = $1; } elsif ( m/^\s*plugin_output=(.+)/ ) { $plugin_output = $1; } elsif ( m/^\s*current_notification_number=(.+)/ ) { $current_notification_number = $1; } elsif ( m/^\s*has_been_checked=(.+)/ ) { $has_been_checked = $1; } elsif ( m/^\s*should_be_scheduled=(.+)/ ) { $should_be_scheduled = $1; } # comment stuff elsif ( m/^\s*author=(.+)/ ) { $author = $1; } elsif ( m/^\s*comment_id=(\d+)/ ) { $comment_id = $1; } elsif ( m/^\s*(?:comment|comment_data)=(.+)/ ) { $comment = $1; } elsif ( m/^\s*entry_time=(\d+)/ ) { $entry_time = $1; } # downtime stuff elsif ( m/^\s*downtime_id=(\d+)/ ) { $downtime_id = $1; } elsif ( m/^\s*start_time=(\d+)/ ) { $start_time = $1; } elsif ( m/^\s*end_time=(\d+)/ ) { $end_time = $1; } elsif ( m/^\s*is_in_effect=(\d+)/ ) { $is_in_effect = $1; } } elsif ( m/^servicestatus\s*{(?#})/ ) { $in_servicestatus = 1; } elsif ( m/^hoststatus\s*{(?#})/ ) { $in_hoststatus = 1; } elsif ( m/^servicecomment\s*{(?#})/ ) { $in_servicecomment = 1; } elsif ( m/^hostdowntime\s*{(?#})/ ) { $in_hostdowntime = 1; } elsif ( m/^servicedowntime\s*{(?#})/ ) { $in_servicedowntime = 1; } } if ( $verbose ) { print "read $. status lines\n"; printf "found %d host statuses\n", scalar( keys %hosts ); printf "found %d service statuses\n", $nservices; } close fH; } sub counts { my( $hostservice, $current_state, $host, $downtime_id, $is_in_effect ); foreach $hostservice ( keys %services ) { # state counts if ( ! $services{ $hostservice }{ 'has_been_checked' } ) { $services{ $hostservice }{ 'current_state' } = $pending_state; $num_in_state[ $pending_state ] ++; } else { $current_state = $services{ $hostservice }{ 'current_state' }; $verbose && print "$hostservice: current_state $current_state\n"; $num_in_state[ $current_state ] ++; } # in host downtime? $host = $services{ $hostservice }{ 'host' }; if ( $hosts{ $host }{ 'in_downtime' } ) { $services{ $hostservice }{ 'in_downtime' } = 1; } else { foreach $downtime_id ( keys %{$hosts{ $host }{ 'downtime' }} ) { $is_in_effect = $hosts{ $host }{ 'downtime' }{ $downtime_id }{ 'is_in_effect' }; $verbose && print "$host: $downtime_id is_in_effect $is_in_effect\n"; if ( $is_in_effect ) { $services{ $hostservice }{ 'in_downtime' } = 1; $hosts{ $host }{ 'in_downtime' } = 1; last; } } # in service downtime? if ( ! $services{ $hostservice }{ 'in_downtime' } ) { foreach $downtime_id ( keys %{$services{ $hostservice }{ 'downtime' }} ) { $is_in_effect = $services{ $hostservice }{ 'downtime' }{ $downtime_id }{ 'is_in_effect' }; $verbose && print "$hostservice: $downtime_id is_in_effect $is_in_effect\n"; if ( $is_in_effect ) { $services{ $hostservice }{ 'in_downtime' } = 1; last; } } } } # host importance if ( $services{ $hostservice }{ 'importance' } ) { $hosts{ $host }{ 'total_importance' } += $services{ $hostservice }{ 'importance' }; } } } sub gen_duration_page { my( $newfile, $oH, $hostservice, $current_state, $state_type, $acked, $ndisplayed, %display_services, @display_services ); $newfile = $duration_file . ".new"; unlink $newfile; open( $oH, ">$newfile" ) || die "can't write-open $newfile: $!\n"; chmod 0644, $newfile; $ndisplayed = 0; hostservice_loop: foreach $hostservice ( keys %services ) { # pending next if ( ! $services{ $hostservice }{ 'has_been_checked' } ); # problem $current_state = $services{ $hostservice }{ 'current_state' }; $verbose && print "$hostservice: current_state $current_state\n"; next if ( $current_state == 0 ); # ok # hard state? $state_type = $services{ $hostservice }{ 'state_type' }; $verbose && print "$hostservice: state_type $state_type\n"; next if ( $state_type != 1 ); # hard # acked? $acked = $services{ $hostservice }{ 'acked' }; $verbose && print "$hostservice: acked $acked\n"; next if ( $acked == 1 ); # in downtime? $verbose && print "$hostservice: in_downtime $services{ $hostservice }{ 'in_downtime' }\n"; next if ( $services{ $hostservice }{ 'in_downtime' } ); $ndisplayed ++; $display_services{ $hostservice } = 1; } @display_services = sort duration_sorter keys %display_services; # header( output_handle, crits, warns, unknowns, oks, pending ) header( $oH ); print $oH qq{ $ndisplayed problems in hard state, not acked, not in downtime, sorted by duration, view in Thruk, view sorted by name }; gen_table( $oH, @display_services ); footer( $oH ); close $oH; move_page( $duration_file ); } sub duration_sorter { my $dura = $now - $services{ $a }{ 'last_hard_state_change' }; my $durb = $now - $services{ $b }{ 'last_hard_state_change' }; return $dura <=> $durb; } sub gen_name_page { my( $newfile, $oH, $hostservice, $current_state, $state_type, $acked, $ndisplayed, %display_services, @display_services ); $newfile = $name_file . ".new"; unlink $newfile; open( $oH, ">$newfile" ) || die "can't write-open $newfile: $!\n"; chmod 0644, $newfile; $ndisplayed = 0; hostservice_loop: foreach $hostservice ( keys %services ) { # pending next if ( ! $services{ $hostservice }{ 'has_been_checked' } ); # problem $current_state = $services{ $hostservice }{ 'current_state' }; $verbose && print "$hostservice: current_state $current_state\n"; next if ( $current_state == 0 ); # ok # hard state? $state_type = $services{ $hostservice }{ 'state_type' }; $verbose && print "$hostservice: state_type $state_type\n"; next if ( $state_type != 1 ); # hard # acked? $acked = $services{ $hostservice }{ 'acked' }; $verbose && print "$hostservice: acked $acked\n"; next if ( $acked == 1 ); # in downtime? next if ( $services{ $hostservice }{ 'in_downtime' } ); $ndisplayed ++; $display_services{ $hostservice } = 1; } @display_services = sort keys %display_services; # header( output_handle, crits, warns, unknowns, oks, pending ) header( $oH ); print $oH qq{ $ndisplayed problems in hard state, not acked, not in downtime, sorted by name, view in Thruk, view sorted by duration }; gen_table( $oH, @display_services ); footer( $oH ); close $oH; move_page( $name_file ); } # draw the page header # header( output_handle ) sub header { my( $oH ) = @_; my( $ncriticals, $nwarnings, $nunknowns, $noks, $npending ); $ncriticals = $num_in_state[ 2 ]; $ncriticals ||= 0; $nwarnings = $num_in_state[ 1 ]; $nwarnings ||= 0; $nunknowns = $num_in_state[ 3 ]; $nunknowns ||= 0; $noks = $num_in_state[ 0 ]; $noks ||= 0; $npending = $num_in_state[ $pending_state ]; $npending ||= 0; print $oH qq{ Nagios $hostname

Nagios status for $hostname

at $nowstr
critical warning unknown ok pending
$ncriticals $nwarnings $nunknowns $noks $npending
}; } sub gen_table { my( $oH, @display_services ) = @_; my( $hostservice, $host, $service, $class, $host_prev, $service_prev, $current_state, $state_type, $acked, $downtime_id, $is_in_effect, $host_current_state, $last_check, $last_hard_state_change, $current_attempt, $max_attempts, $plugin_output, $url, $comment_html, $comment_id, $entry_time, $author, $comment, $current_notification_number, $img, $icon_html ); return if ( $#display_services < 0 ); print $oH qq{

}; $host_prev = ''; $service_prev = ''; foreach $hostservice ( @display_services ) { print $oH qq{}; $host = $services{ $hostservice }{ 'host' }; $host_current_state = $hosts{ $host }{ 'current_state' }; # host if ( $host eq $host_prev ) { printf $oH qq{}, pick_host_class( $host_current_state ); } else { $url = sprintf "/thruk/cgi-bin/status.cgi?host=%s", escape_url( $host ); # host importance / firestars if ( $hosts{ $host }{ 'icon_image' } ) { #$img = $hosts{ $host }{ 'icon_image' }; #$icon_html = qq{}; $icon_html = ''; } elsif ( $hosts{ $host }{ 'total_importance' } >= $min_host_importance ) { #$img = "firestar.gif"; #$icon_html = qq{}; $icon_html = ''; } else { $icon_html = ''; } printf $oH qq{}, pick_host_class( $host_current_state ), $url, $host, $icon_html; $service_prev = ''; } # service $service = $services{ $hostservice }{ 'service' }; $current_state = $services{ $hostservice }{ 'current_state' }; $class = pick_service_class( $current_state ); $url = sprintf "/thruk/cgi-bin/extinfo.cgi?type=2&host=%s&service=%s", escape_url( $host ), escape_url( $service ); # service comments if ( defined $services{ $hostservice }{ 'comments' } ) { $comment_html = qq{} . qq{}; $comment_html .= qq{
hostservicestatuslast checkeddurationattempt output
 %s%s
}; foreach $comment_id ( keys %{$services{ $hostservice }{ 'comments' }} ) { $entry_time = $services{ "$host/$service" }{ 'comments' }{ $comment_id }{ 'entry_time' }; $author = $services{ "$host/$service" }{ 'comments' }{ $comment_id }{ 'author' }; $comment = $services{ "$host/$service" }{ 'comments' }{ $comment_id }{ 'comment' }; $comment_html .= qq{"; } $comment_html .= "
$comment$author} . localtime( $entry_time ) . "
"; } else { $comment_html = ''; } # service importance / firestars if ( $services{ $hostservice }{ 'icon_image' } ) { $img = $services{ $hostservice }{ 'icon_image' }; $icon_html = qq{}; $verbose && print "$hostservice has icon_image $img\n"; } elsif ( ( $hosts{ $host }{ 'importance' } + $services{ $hostservice }{ 'importance' } ) >= $min_service_importance ) { $img = "firestar.gif"; $icon_html = qq{}; $verbose && printf "$hostservice has importance %d\n", $hosts{ $host }{ 'importance' } + $services{ $hostservice }{ 'importance' }; } else { $icon_html = ''; } printf $oH qq{%s%s%s}, $class, $url, ( $service ne $service_prev ) ? $service : '', $comment_html, $icon_html; # status printf $oH qq{%s}, $class, $class; # last_check $last_check = $services{ $hostservice }{ 'last_check' }; printf $oH qq{%s}, $class, pretty_time( $last_check ); # duration $last_hard_state_change = $services{ $hostservice }{ 'last_hard_state_change' }; printf $oH qq{%s}, $class, pretty_duration( $now - $last_hard_state_change ); # attempt # FIXME, notification number $current_attempt = $services{ $hostservice }{ 'current_attempt' }; $max_attempts = $services{ $hostservice }{ 'max_attempts' }; $current_notification_number = $services{ $hostservice }{ 'current_notification_number' }; if ( $current_notification_number > 0 ) { printf $oH qq{%d/%d #%d}, $class, $current_attempt, $max_attempts, $current_notification_number; } else { printf $oH qq{%d/%d}, $class, $current_attempt, $max_attempts; } # plugin $plugin_output = $services{ $hostservice }{ 'plugin_output' }; printf $oH qq{%s}, $class, escapeHTML( $plugin_output ); # fixme, comments appended to plugin output? print $oH qq{\n}; $host_prev = $host; $service_prev = $service; } print $oH qq{\n}; } sub footer { my( $oH ) = @_; print $oH qq{


Nagios on $hostname, at $nowstr\ }; } sub pick_host_class { my( $state ) = @_; my $class = 'critical'; if ( $state == 0 ) { $class = 'ok'; } else { $class = 'critical'; } return $class; } sub pick_service_class { my( $state ) = @_; my $class = 'unknown'; if ( $state == 0 ) { $class = 'ok'; } elsif ( $state == 1 ) { $class = 'warning'; } elsif ( $state == 2 ) { $class = 'critical'; } else { $class = 'unknown'; } return $class; } sub pretty_duration { my( $duration ) = @_; my $str = ''; if ( $duration > 86400 ) { $str .= sprintf "%dd ", int( $duration / 86400 ); $duration -= 86000 * int( $duration / 86400 ); } if ( $duration > 3600 ) { $str .= sprintf "%dh ", int( $duration / 3600 ); $duration -= 3600 * int( $duration / 3600 ); } if ( $duration > 60 ) { $str .= sprintf "%dm ", int( $duration / 60 ); $duration -= 60 * int( $duration / 60 ); } if ( $duration > 0 ) { $str .= sprintf "%ds ", $duration; } $str =~ s/\s+$//; return $str; } sub pretty_time { my( $time ) = @_; my( $i, $timestr ); my @nowfields = split( m/\s/, $nowstr ); my @timefields = split( m/\s/, scalar( localtime( $time ) ) ); for ( $i = 0; $i <= $#nowfields; $i++ ) { next if ( $nowfields[ $i ] =~ m/:/ ); if ( $nowfields[ $i ] eq $timefields[ $i ] ) { $timefields[ $i ] = ''; } } $timestr = join( ' ', @timefields ); return $timestr; } sub escape_url { my( $str ) = @_; $str =~ s/([^\w\d_-])/ sprintf( "%%%02x", ord( $1 ) ) /ieg; return $str; } sub move_page { my( $file ) = @_; my( $newfile ); $newfile = $file . ".new"; if ( -s $newfile < 100 ) { warn "$newfile is too small!\n"; return; } if ( $noop ) { print "noop set, not moving $newfile to $file\n"; return; } chmod 0644, $newfile; if ( ! rename( $newfile, $file ) ) { die "can't mv $newfile to $file: $!\n"; } }