File check_sentry3 of Package monitoring-plugins-sentry3

#!/usr/bin/perl
#
# check_sentry3 - Nagios(r) network traffic monitor plugin
# Copyright (C) 2012 Peter Harrison / www.linuxhomenetworking.com
#
# This Nagios check monitors Servertech devices that use the Sentry3 MIB.
# It checks the following:
# 1) Environmental temperature (Auto-detects all sensors)
# 2) Environmental humidity (Auto-detects all sensors)
# 3) Input power (Auto-detects all input feeds)
#
# Prerequisite Perl modules:
#
#	Net::SNMP
#	Nagios::Plugin
#
# Though Net::SNMP may be installable using package systems such as yum or apt, 
# Nagios::Plugin may have to be done by hand like this:
#
# $ perl -MCPAN -e 'install Nagios::Plugin'
#
################################################################################
# Modification History
################################################################################
#
#	Put first modification description below here:
#
################################################################################
# Legal stuff...
################################################################################
#
# Send us bug reports, questions and comments about this plugin.
# Latest version of this software: http://www.nagiosexchange.org
#
#
# 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307
#
################################################################################
# Let the coding begin ...
################################################################################


   use Getopt::Long;
   &Getopt::Long::config('bundling');
   use strict;
   use Nagios::Plugin;
   use Net::SNMP;

   # Run the program
   &main();
   exit;


sub main{

   my $get_help		= 0;
   my $get_morehelp	= 0;
   my %cmd_flags	= ();

   # SNMP Specific
   my %snmp_vars                = ();
   my %snmp_results             = ();
   $snmp_vars{port}             = 161;
   $snmp_vars{version}          = 2;
   $snmp_vars{community}        = "public";

   # Define the OIDs we are interested in
   my %oids		= ();

   #
   # Important stuff below, do not erase as it is good information:
   #
   #$oids{temp_value}	= "Sentry3-MIB::tempHumidSensorTempValue";
   #$oids{humid_value}	= "Sentry3-MIB::tempHumidSensorHumidValue";
   #$oids{power_value}	= "Sentry3-MIB::infeedLoadValue";
   #
   # I originally used the MIB, but wanted after convertint to Net-SNMP
   # I had to use the oids. This is how I got them.
   #
   # /usr/bin/snmpwalk  -v 2c     -c public 66.214.208.171 enterprise > file1.txt
   # /usr/bin/snmpwalk  -v 2c -On -c public 66.214.208.171 enterprise > file2.txt

   $oids{temp_value}	= ".1.3.6.1.4.1.1718.3.2.5.1.6";
   $oids{humid_value}	= ".1.3.6.1.4.1.1718.3.2.5.1.10";
   $oids{power_value}	= ".1.3.6.1.4.1.1718.3.2.2.1.7";

   # Verify that we have the correct options listed
   GetOptions(
     "c|critical-level=s"	=> \$cmd_flags{crit_level},
     "w|warning-level=s"	=> \$cmd_flags{warn_level},
     "d|temperature"		=> \$cmd_flags{do_temp},
     "e|humidity"		=> \$cmd_flags{do_humid},
     "f|power"			=> \$cmd_flags{do_power},
     "b|celcius"		=> \$cmd_flags{celcius},
     "B|fahrenheit"		=> \$cmd_flags{fahrenheit},
     "h|help"			=> \$get_help,
     "m|morehelp"		=> \$get_morehelp,
     "H|hostname=s"		=> \$snmp_vars{hostname},
     "p|port=i"			=> \$snmp_vars{port},
     "C|community=s"		=> \$snmp_vars{community},
     "s|username=s"		=> \$snmp_vars{username},
     "v|version=i"		=> \$snmp_vars{version},
     "T|authpassword=s"		=> \$snmp_vars{authpassword},
     "t|authprotocol=s"		=> \$snmp_vars{authprotocol},
     "X|privpassword=s"		=> \$snmp_vars{privpassword},
     "x|privprotocol=s"		=> \$snmp_vars{privprotocol}
   );

   # Test for file validity and Help
   if($get_help){&usage();}
   if($get_morehelp){&moreusage();}
   if(!$snmp_vars{hostname}){&usage();}


   # usage notes
   if (
        ( ! defined $cmd_flags{crit_level} ) ||
        ( ! defined $cmd_flags{warn_level} )
      ) {&nagios_exit("Critical Level or Warning Level not specified on the command line","UNKNOWN");}

   # usage notes (Specific to temperature)
   if (
        (( ! defined $cmd_flags{celcius} ) &&
        ( ! defined $cmd_flags{fahrenheit} )) &&
	defined $cmd_flags{do_temp}
      ) {&nagios_exit("Temperature mode defined, but don't know whether to use Celcius or Fahrenheit mode","UNKNOWN");}

   # Define what to do
   if(defined $cmd_flags{do_temp}){
	$cmd_flags{do_power}	= 0;
	$cmd_flags{do_humid}	= 0;
   }
   elsif(defined $cmd_flags{do_humid}){
	$cmd_flags{do_temp}	= 0;
	$cmd_flags{do_power}	= 0;
   }
   elsif(defined $cmd_flags{do_power}){
	$cmd_flags{do_temp}	= 0;
	$cmd_flags{do_humid}	= 0;
   }

   &nagios_report(\%cmd_flags,\%snmp_vars,\%oids);

}

sub nagios_report {

   # Passing multiple arrays into subroutine
   my %cmd_flags	= %{$_[0]};
   my %snmp_vars	= %{$_[1]};
   my %oids		= %{$_[2]};

   my %poll_results	= ();
   my @nagios_value	= "";
   my $np		= 0;
   my $np_threshold	= 0;
   my $temp_uom		= "";
   my $count		= -1;
   my $message		= "";

   # Get all the OID values
   my ($poll_pointer) = &get_data(\%cmd_flags,\%snmp_vars,\%oids);

   # Get the results
   %poll_results = %$poll_pointer;

   # Create the object, we are going to be adding data to it multiple times
   if ($cmd_flags{do_temp}){
      $np = Nagios::Plugin->new(shortname => "SERVERTECH_TEMPERATURE");
   }
   elsif ($cmd_flags{do_humid}){
      $np = Nagios::Plugin->new(shortname => "SERVERTECH_HUMIDITY");
   }
   elsif ($cmd_flags{do_power}){
      $np = Nagios::Plugin->new(shortname => "SERVERTECH_AMPS");
   }

   foreach my $key (sort keys(%poll_results)){

     # Increment count
     $count +=1;

     # Define the values we are going to test against
     $nagios_value[$count] = sprintf("%.2f",$poll_results{$key});

     # Process the value
     if ($cmd_flags{do_temp}){
   
	# Set up the thresholds we are interested in
	$np_threshold = $np->set_thresholds( 
	   warning	=> $cmd_flags{warn_level} ,
	   critical	=> $cmd_flags{crit_level} );

	# Determine the Unit of Measure
	if ($cmd_flags{celcius}){$temp_uom = "C";}else{$temp_uom = "F";}

	# Define the performance data
	$np->add_perfdata(
	   label => "Temperature" . sprintf("%d",$count + 1),
	   value => $nagios_value[$count],
	   threshold => $np_threshold,
	   uom => $temp_uom
	);

	# Update $message
	$message .= "Temperature" . sprintf("%d @ ", $count + 1) . sprintf("%.2f",$nagios_value[$count]) . $temp_uom . ", ";
     }
     elsif ($cmd_flags{do_humid}){
   
	# Set up the thresholds we are interested in
	$np_threshold = $np->set_thresholds( 
	   warning	=> $cmd_flags{warn_level} ,
	   critical	=> $cmd_flags{crit_level} );

	# Define the performance data
	$np->add_perfdata(
	   label => "Humidity" . sprintf("%d",$count + 1),
	   value => $nagios_value[$count],
	   threshold => $np_threshold,
	   uom => "%"
	);

	# Update $message
	$message .= "Humidity" . sprintf("%d @ ", $count + 1) . sprintf("%.2f",$nagios_value[$count]) . "%, ";
     }
     elsif ($cmd_flags{do_power}){
   
	# Set up the thresholds we are interested in
	$np_threshold = $np->set_thresholds( 
	   warning	=> $cmd_flags{warn_level} ,
	   critical	=> $cmd_flags{crit_level} );

	# Define the performance data
	$np->add_perfdata(
	   label => "Input" . sprintf("%d",$count + 1),
	   value => $nagios_value[$count],
	   threshold => $np_threshold,
	   uom => "A"
	);

	# Update $message
	$message .= "Input" . sprintf("%d @ ", $count + 1) . sprintf("%.2f",$nagios_value[$count]) . "A, ";
     }
   }

   # Remove trailing comma from $message
   $message =~ s/\,+\s+$//g;

   # Process and exit
   $np->nagios_exit( 
	return_code => $np->check_threshold(check => \@nagios_value), message => $message
   );

}

sub get_data {

   # Passing multiple arrays into subroutine
   my %cmd_flags        = %{$_[0]};
   my %snmp_vars        = %{$_[1]};
   my %oids             = %{$_[2]};

   # Initialize variables
   my %walk_results	= ();
   my $walk_pointer	= undef;
   my $session		= undef;
   my $oid_to_walk	= ""; # Used for getting individual sensor statuses
   my $session;
   my $error;


   # Poll specific OID for a value
   if($cmd_flags{do_temp}){
        $oid_to_walk = $oids{temp_value};
   }
   elsif ($cmd_flags{do_humid}){
        $oid_to_walk = $oids{humid_value};
   }
   elsif ($cmd_flags{do_power}){
        $oid_to_walk = $oids{power_value};
   }

   ################################################################

   # Do SNMP bulk query and put results in %walk_results array
   # after running the analyse_walk routine

   # Get SNMP Session pointer
   $session = &get_snmp_session_blocking(\%snmp_vars);

   # Define what to do when the query is done
   my $result = $session->get_bulk_request(
      -varbindlist    => [ $oid_to_walk ],
      -callback       => [ \&analyse_walk, \%walk_results, $oid_to_walk ],
      -maxrepetitions => 10,
   );

   # If we get no result then exit and DIE!
   if (!defined $result) {
      &nagios_exit($session->error(),"UNKNOWN");
   }

   # Now initiate the SNMP message exchange.
   snmp_dispatcher();
   $session->close();

   ################################################################

   # Go through the results in sorted order and adjust values
   foreach my $key (sort keys(%walk_results)){

	# Temperatures are recorded in tenths of a degree
	if($cmd_flags{do_temp}){
           $walk_results{$key} = $walk_results{$key}/10;
	}
	# Current is recorded in hundredths of an amp
	elsif($cmd_flags{do_power}){
           $walk_results{$key} = $walk_results{$key}/100;
	}
   }

   # Return all the results
   return(\%walk_results);
}


sub analyse_walk{

   # Purpose: Analyses the results of the walk

   my ($session, $table, $oid_to_walk) = @_;

   # Define variables used in this routine
   my $next  = undef;

   # Define a pointer to the get hash reference for the VarBindList values (snmpget results)
   my $list = $session->var_bind_list();

   # If there is an error say so! Then DIE!
   if (!defined $session) {
      &nagios_exit($session->error(),"UNKNOWN");
   }

   # Get the array of the ObjectNames (OIDs) in the VarBindList
   my @names = $session->var_bind_names();

   # Loop through each of the OIDs in the response and assign
   # the key/value pairs to the reference that was passed with
   # the callback.  Make sure that we are still in the table
   # before assigning the key/values.

   foreach $next (@names) {
      if (!is_child_oid($oid_to_walk, $next)) {
            return; # Table is done.
      }
      $table->{$next} = $list->{$next};
   }

   # Table is not done, send another request, starting at the last
   # OBJECT IDENTIFIER in the response.  No need to include the
   # calback argument, the same callback that was specified for the
   # original request will be used.

   my $result = $session->get_bulk_request(
      -varbindlist    => [ $next ],
      -maxrepetitions => 10,
   );


   # If we get no result then exit and DIE!
   if (!defined $result) {
      &nagios_exit($session->error(),"UNKNOWN");
   }

   return;
}

sub is_child_oid{

   my ($oid_tree,$oid_child) = @_;

   my $count		= 0;
   my $return_ok	= 1;

   my @tree_array	= split('\.',$oid_tree);
   my @child_array 	= split('\.',$oid_child);

   # Compare $oid_tree and $oid_child. 
   # If any octet in $oid_tree doesn't match the same octet in $oid_child then exit with "0" (Fail)

   foreach my $temp (@tree_array){
	if ($tree_array[$count] != $child_array[$count]){$return_ok = 0; last;}
	$count +=1;
   }

   return ($return_ok);
}

sub get_snmp_session_blocking {

   # Passing multiple arrays into subroutine
   my %snmp_vars      = %{$_[0]};

   # Define the SNMP session variable
   my $session;
   my $error;

   #
   # Check for missing options
   #

   # Check missing host name
   if (!$snmp_vars{hostname}){
      &nagios_exit("Missing host address!","UNKNOWN");
   }

   # Make some variables lower case
   if ($snmp_vars{privprotocol}){$snmp_vars{privprotocol} = lc($snmp_vars{privprotocol});}
   if ($snmp_vars{authprotocol}){$snmp_vars{authprotocol} = lc($snmp_vars{authprotocol});}

   # Check SNMP version compatibility (Versions 1 & 2)
   if ( $snmp_vars{version} =~ /[12]/ ) {
      ( $session, $error ) = Net::SNMP->session(
        -hostname  	=> $snmp_vars{hostname},
        -community 	=> $snmp_vars{community},
        -port      	=> $snmp_vars{port},
        -nonblocking 	=> 1,
        -translate   	=> [-octetstring => 0],
        -version   	=> $snmp_vars{version}
        );

   } # / SNMP version 1 or 2

   # Check SNMP version compatibility (version 3)
   elsif ( $snmp_vars{version} =~ /3/ ) {

      # AuthPriv mode
      if($snmp_vars{privpassword}){

        ($session, $error) = Net::SNMP->session(
          -username             => $snmp_vars{username},
          -authpassword         => $snmp_vars{authpassword},
          -authprotocol         => $snmp_vars{authprotocol},
          -privpassword         => $snmp_vars{privpassword},
          -privprotocol         => $snmp_vars{privprotocol},
          -hostname             => $snmp_vars{hostname},
          -port                 => $snmp_vars{port},
          -nonblocking 		=> 1,
          -translate   		=> [-octetstring => 0],
          -version              => $snmp_vars{version}
          );

      } # /AuthPriv mode

      # AuthNoPriv options
      else{

        # AuthNoPriv mode
        if($snmp_vars{authprotocol}){

          ($session, $error) = Net::SNMP->session(
            -username           => $snmp_vars{username},
            -authpassword       => $snmp_vars{authpassword},
            -authprotocol       => $snmp_vars{authprotocol},
            -hostname           => $snmp_vars{hostname},
            -port               => $snmp_vars{port},
	    -nonblocking 	=> 1,
            -translate   	=> [-octetstring => 0],
            -version            => $snmp_vars{version}
            );

        } # /AuthNoPriv mode


        # noAuthNoPriv mode
        else{

          ($session, $error) = Net::SNMP->session(
            -username           => $snmp_vars{username},
            -authpassword       => $snmp_vars{authpassword},
            -hostname           => $snmp_vars{hostname},
            -port               => $snmp_vars{port},
	    -nonblocking 	=> 1,
            -translate   	=> [-octetstring => 0],
            -version            => $snmp_vars{version}
            );

        } # /noAuthNoPriv mode

      } # /AuthNoPriv options

   } # /SNMP version 3

   # Some other version of SNMP
   else {
      &nagios_exit("Unknown SNMP v" . $snmp_vars{version},"UNKNOWN");
   };

   # Things failed, so DIE!
   if ( !defined($session) ) {
      &nagios_exit($error,"UNKNOWN");
   };

   # Return the session pointer
   return($session);
}


sub nagios_exit {

   my ($message, $status) = @_;

   $status = uc($status);
   $message = $status . ": " . $message;

   if ($status eq "UNKNOWN"){
      my $np = Nagios::Plugin->new(shortname => "UNKNOWN");
      $np->nagios_exit(UNKNOWN, $message);
   }
   elsif ($status eq "WARNING"){
      my $np = Nagios::Plugin->new(shortname => "WARNING");
      $np->nagios_exit( WARNING, $message )
   }
   elsif ($status eq "CRITICAL"){
      my $np = Nagios::Plugin->new(shortname => "CRITICAL");
      $np->nagios_exit( CRITICAL, $message )
   }
}

sub usage {

print STDERR << "EOF";

    This program is a Nagios check the monitors servertech devices
    using the Sentry3.mib MIB

    It checks the following:
    1) Environmental temperature (Auto-detects all sensors)
    2) Environmental humidity (Auto-detects all sensors)
    3) Input power (Auto-detects all input feeds)

    Prerequisite Perl modules:

      Net::SNMP
      Nagios::Plugin

      Though Net::SNMP may be installable using package systems such as yum or apt,
      Nagios::Plugin may have to be done by hand like this:

      # perl -MCPAN -e 'install Nagios::Plugin'
      # perl -MCPAN -e 'install Net::SNMP'





    usage: $0 [--help]

    -h, --help
        This help message

    -m, --morehelp
        Detailed help on how to edit Nagios / Icinga configuration files to get this
	check working for you

    -H, --hostname STRING or IPADDRESS
        Host to query via SNMP

    -C, --community STRING
        SNMP Community (Default = "public")

    -v, --version INTEGER
        SNMP version (Default = 2)

    -p, --port INTEGER
        SNMP UDP port (Default = 161)

    -s, --username
        Set the securityName used for authenticated SNMPv3 communication.

    -T, --authpassword
        Set the SNMPv3 authentication pass phrase used for authenticated SNMPv3 communication.

    -t, --authprotocol
        Set  the authentication protocol (MD5 or SHA) used for authenticated SNMPv3
        communication.

    -X, --privpassword
        Set the privacy pass phrase used for encrypted SNMPv3 communication.

    -x, --privprotocol
        Set the privacy protocol (DES or AES) used for encrypted SNMPv3 communication.

    -e, --humidity
	Used if you are tracking humidity

    -f, --power
	Used if you are tracking power

    -d, --temperature
	Used if you are tracking temperature

    -B, --fahrenheit
	Used with --temperature

    -b, --celcius
	Used with --temperature

    -c, --critical-level STRING
	Critical level to use

    -w, --warning-level STRING
	Warning level to use

EOF

   exit();

}

sub moreusage {

print STDERR << "EOF";

   # Without PNP4Nagios ########################################################################    
   Sample SNMPv2 commands
   to add to your Nagios / Icinga command file
   #############################################################################################    

    define command{
        command_name    check_sentry3_amps
        command_line    \$USER1\$/check_sentry3 --hostname \$HOSTADDRESS\$ --power  --community \$ARG1\$ --warning \$ARG2\$ --critical \$ARG3\$
        }

    define command{
        command_name    check_sentry3_temp
        command_line    \$USER1\$/check_sentry3 --hostname \$HOSTADDRESS\$ --temp   --community \$ARG1\$ --warning \$ARG2\$ --critical \$ARG3\$ \$ARG4\$
        }

    define command{
        command_name    check_sentry3_humid
        command_line    \$USER1\$/check_sentry3 --hostname \$HOSTADDRESS\$ --humid  --community \$ARG1\$ --warning \$ARG2\$ --critical \$ARG3\$
        }

   # Without PNP4Nagios ########################################################################    
   Sample SNMPv2 checks 
   to add to the Nagios / Icinga file where your checks are located

   Note: Servertechs often have multiple sensors of the same type. With this check Nagios / 
   Icinga will alarm when any of the temperature, power or humidity thresholds is exceeded on
   any of the specified type of sensor. This check automatically detects the number of sensors.
   #############################################################################################    

    # Servertech (AMPS)
    define service{
        use                             local-service			; Name of service template to use
        host_name                       servertech-cdu
        service_description             AMPS
        check_command                   check_sentry3_amps!public!16!18
        }

    # Servertech (HUMIDITY)
    define service{
        use                             local-service			; Name of service template to use
        host_name                       servertech-cdu
        service_description             HUMIDITY
        check_command                   check_sentry3_humid!public!10:!75
        }

    # Servertech (TEMPERATURE)
    define service{
        use                             local-service			; Name of service template to use
        host_name                       servertech-cdu
        service_description             TEMPERATURE
        check_command                   check_sentry3_temp!public!35:!85!--fahrenheit
        }

   # With PNP4Nagios ###########################################################################    
   Sample SNMPv2 commands
   to add to your Nagios / Icinga command file
   #############################################################################################    

   define command{
        command_name    check_sentry3_amps
        command_line    \$USER1\$/check_sentry3 --hostname \$HOSTADDRESS\$ --power  --community \$ARG2\$ --warning \$ARG3\$ --critical \$ARG4\$
        }

   define command{
        command_name    check_sentry3_temp
        command_line    \$USER1\$/check_sentry3 --hostname \$HOSTADDRESS\$ --temp   --community \$ARG2\$ --warning \$ARG3\$ --critical \$ARG4\$ \$ARG5\$
        }

   define command{
        command_name    check_sentry3_humid
        command_line    \$USER1\$/check_sentry3 --hostname \$HOSTADDRESS\$ --humid  --community \$ARG2\$ --warning \$ARG3\$ --critical \$ARG4\$
        }

   # With PNP4Nagios ###########################################################################    
   Sample SNMPv2 checks 
   to add to the Nagios / Icinga file where your checks are located
   #############################################################################################    

   Note:
	Servertechs often have multiple sensors of the same type. With this check Nagios / 
	Icinga will alarm when any of the temperature, power or humidity thresholds is exceeded on
	any of the specified type of sensor. This check automatically detects the number of sensors.

   Note:
	You will have to create separate configuration files in the PNP4Nagios "etc/check_commands"
	directory. Use the sample files that exist there as a guide. Create these configuration files:

	1) check_sentry3_amps.cfg
	2) check_sentry3_humid.cfg
	3) check_sentry3_temp.cfg

	Make sure the following directives are applied in each file

	   CUSTOM_TEMPLATE = 1
	   DATATYPE = GAUGE
	   RRD_STORAGE_TYPE = SINGLE
   Note:
        PNP4Nagios chart template file gauge_single_rrd_one_chart_per_rra.php must be placed in 
        your "pnp4nagios/share/templates/" directory

   Note:
	If your charts are not displayed it could be due to the fact that PNP4Nagios did not
	create a correctly configured .rrd and .xml file (AMPS, HUMIDITY,TEMPERATURE) in the 
	"pnp4nagios/var/perfdata/\$HOSTNAME\$/ directory because you restarted Nagios / Icinga 
	before you created the .cfg files above. The .rrd and .xml file for your check may have 
	to be deleted so that PNP4Nagios can recreate it automatically.

   #############################################################################################    

   # Servertech test (AMPS)
   define service{
        use                             local-service,srv-pnp         ; Name of service template to use
        host_name                       servertech-cdu
        service_description             AMPS
        check_command                   check_sentry3_amps!default_single_rra_per_chart!public!16!18
        }

   # Servertech test (HUMIDITY)
   define service{
        use                             local-service,srv-pnp         ; Name of service template to use
        host_name                       servertech-cdu
        service_description             HUMIDITY
        check_command                   check_sentry3_humid!gauge_single_rrd_one_chart_per_rra!public!10:!75
        }

   # Servertech test (TEMPERATURE)
   define service{
        use                             local-service,srv-pnp         ; Name of service template to use
        host_name                       servertech-cdu
        service_description             TEMPERATURE
        check_command                   check_sentry3_temp!gauge_single_rrd_one_chart_per_rra!public!35:!85!--fahrenheit
        }

EOF

   exit();

}