#!/usr/bin/env perl

######################################################################################################
# NEPP - Nagios External Perfdata Parser
# This script is used as a wrapper to any Nagios plugin in order to seperatly collect and hand the 
# perf data off to an external application (like pnp4Nagios).
#
# Copyright (C) 2010  Frank Clements <fclements@inetu.net>
#   
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#   
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#  
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
#
#
#
# Usage:
# In order to use this simply run your Nagios checks via this wrapper in the command definitions.
#   Ex: $USER1$/neppc --local $USER1$/check_nrpe -H $HOSTADDRESS$ -c check_command -a <arguments>
# 
# The default mode is to spit out the performance data from the check to a file in /var/spool/nepp
# which can then be processed by some external performance trending system.
#
# Why you ask?
# At INetU we monitor more than 13,000 services and carry a requiremnt that all service checks 
# _must_ happen within a 5 minute window.  Having Nagios process any performance data with this
# many checks drops performance to about 500 checks/5 min.  With performance processing turned off
# in the nrpe.cfg performance climbs to > 10,000 checks 5 min on a single host.
#
# Having the trending data is required so the solution is to split it out and not ask Nagios to do
# anything with it.
#
######################################################################################################

use strict;
use warnings;
use Getopt::Long;
#use Time::HiRes qw(time);

## Variable & Constant Definitions
use constant VERSION      => qw(0.1);
use constant EXITSTATE	  => qw(OK WARNING CRITICAL UNKNOWN); 

my $optionLocal        = undef;    # Used for logging the perfdata on the local file system
my $optionRemote       = undef;    # Used for logging the perfdata to a remote NEPP server (not yet implemented)
my $optionCheckCommand = undef;    # The check command we are wrapping
my $optionCheckArgs    = undef;    # The check command's arguments
my $optionSpool        = undef;    # The location of the local spool directory
my $optionService      = undef;    # Performance results will be of type service
my $optionHost         = undef;    # Performance results will be of type host
my $optionVersion      = undef;    # Version display option
my $optionHelp         = undef;    # Help display option
my $optionCommand      = undef;    # The command and arguments to wrap - Needs to be quoted on the cli
my $perfType           = qw(service);   # Default type is service


## Subroutines

# Show version
sub displayVersion{print "Version: " . VERSION; exit 0};

# Display detailed help information
sub displayHelp{
   displayUsage();
print <<EOF;

--local
   Sends the perfdata to a local output file (default)
--spool
   The local spool dir for perfdata output (default: /var/spool/nepp)
--host
   Run against host performance data
--service
   Run against service performance data (default)
--command
   The quoted version of the command to be wrapped
--version
   Print the version of this program
--help
   Display this help screen

Summary:
In order to properly use this tool the administrator must define the 
template to use in the SVCTEMPLATE and HOSTTEMPLATE constants located
within this tool.  This can typically be found in the nagios.cfg file
within the config value 'host_perfdata_file_template' and 
'service_perfdata_file_template' values.

Once the templates are defined configure all your service checks which
you would like to process perfdata for externally (probably all) as 
follows:  \$USER1\$/nepp --local --spooldir=/var/spool/nepp --service \
          --command="<check_command> <check_command_args>"

Now that you have wrapped the checks turn off performance data processing
in the nagios.cfg file by setting process_perfdata=0.  Restart Nagios.

EOF
exit 0;
}

# Display simple usage information
sub displayUsage{
   print "Usage: $0 [--local] [--spool=<path_to_spool_dir>] [--service|--host] [--help] [--version] --command=\"<check_command> <check_args>\"\n";
}

# Process the command line arguments
sub processOptions {
    Getopt::Long::Configure ("bundling");
    GetOptions(
       'local'     => \$optionLocal,
       'spool:s'   => \$optionSpool,
       'host'      => \$optionHost,
       'service'   => \$optionService,
       'command:s' => \$optionCommand,
       'version'   => \$optionVersion,
       'help'      => \$optionHelp
    );

   displayVersion() if $optionVersion;                        # Display version info
   displayHelp() if $optionHelp || ! $optionCommand;          # Display help
   # Set te default spool directory if not specified on the cli
   $optionSpool = defined $optionSpool ? $optionSpool : qw(/var/spool/nepp);
   $perfType = qw(host) if( $optionHost );
}

# Create the data to be stuffed into the perfdata file
sub createPerfdataOutput {

   my $commandOutput = shift;
   my $exitCode      = shift;

   # Split the command output and the perfdata
   my ($output, $perfdata) = split(/\|/, $commandOutput);
  
   chomp($output);
   chomp($perfdata) if($perfdata);

   print $output;

   if( $perfType eq 'service' && $perfdata ) {
      my $servicePerfdata = "DATATYPE::SERVICEPERFDATA\tTIMET::".$ENV{'NAGIOS_TIMET'}."\tHOSTNAME::".$ENV{'NAGIOS_HOSTNAME'}."\tSERVICEDESC::".$ENV{'NAGIOS_SERVICEDESC'}."\tSERVICEPERFDATA::".$perfdata."\tSERVICECHECKCOMMAND::".$ENV{'NAGIOS_SERVICECHECKCOMMAND'}."\tHOSTSTATE::".$ENV{'NAGIOS_HOSTSTATE'}."\tHOSTSTATETYPE::".$ENV{'NAGIOS_HOSTSTATETYPE'}."\tSERVICESTATE::".(EXITSTATE)[$exitCode]."\tSERVICESTATETYPE::".$ENV{'NAGIOS_SERVICESTATETYPE'}."\tSERVICEOUTPUT::".$output."\n";

      return $servicePerfdata;
   }
   return 0;   # 1 evaluates to true which is not what we want if we get here.
}

# Write out the performance data file
sub writeFile {
    my $template  = shift;
    my $fileName  = $optionSpool."/".$perfType."-perfdata.".time();

    # Check if the file already exists - if it does append to it, otherwise create it
    if( -f $fileName ) {
       open(FILE, '>>', $fileName) || die("Unable to open $fileName: $!\n");
    } else {
       open(FILE, '>', $fileName) || die("Unable to open $fileName: $!\n");
    }

    print FILE $template;
    close(FILE);

}


## Main Section of tool
processOptions();      # Process command line arguments

my $result   = `$optionCommand`;     # Run and store the command output
my $ecode    = $?>>8;		   # Store the exit status from the command

# Check the exit status right away for UNKNOWN conditions
if( $ecode == 4 ) {
   print $result;
   exit $ecode;
}

my $perfData = createPerfdataOutput($result, $ecode);
writeFile($perfData) if($perfData); 

exit $ecode;    # Always leave with the exit status.

##
# TODO:
# 
# * Create a daemon which can accept bulk updates from multiple outside nepp clients so that something
#   like NFS or scp does not have to bused to aggregate the data.
