File check_temp_raritan of Package monitoring-plugins-temp_raritan

#!/usr/bin/perl -w
# nagios: -epn
#
# check_temp_raritan - nagios plugin
#
# Copyright (C) 2015, SUSE Linux GmbH
# Author: Lars Vogdt
#
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice, this
#   list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright notice,
#   this list of conditions and the following disclaimer in the documentation
#   and/or other materials provided with the distribution.
#
# * Neither the name of the Novell nor the names of its contributors may be
#   used to endorse or promote products derived from this software without
#   specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
use warnings;
use strict;
use Carp;
use Config::IniFiles;
use English qw( -no_match_vars );
use Getopt::Long;
use Net::SNMP;
use Pod::Usage;

#######################################################################
## Init Area
#######################################################################
$ENV{'PATH'}     = '/bin:/usr/bin:/sbin:/usr/sbin:';
$ENV{'BASH_ENV'} = '';
$ENV{'ENV'}      = '';
our $conf={
    VERSION  => '0.2.0',
    PROGNAME => 'check_temp_raritan',
    community => 'public',
    debug    => 0,
    hostname => 'localhost',
    timeout  => 120,
    logfile  => '/var/log/check_temp_raritan.log',
    loglevel => 1,
};
our $ERRORS={
    'OK'        => 0,
    'WARNING'   => 1,
    'CRITICAL'  => 2,
    'UNKNOWN'   => 3,
    'DEPENDENT' => 4,
};
our %nagios_result=(
    output   => 'OK',
    perfdata => '',
    error    => 0,
);
our @config_options=qw(
    ip
    community
    logfile
    loglevel
);
our @needed=qw(
    ip 
    community
);

our $temp_sensor_count	=	'.1.3.6.1.4.1.13742.4.2.1.0';
our $tempSensorLabel	=	'.1.3.6.1.4.1.13742.4.2.2.1.2';
our $temperature	=	'.1.3.6.1.4.1.13742.4.2.2.1.3';
our $tempLowerWarning	=	'.1.3.6.1.4.1.13742.4.2.2.1.4';
our $tempUpperWarning	=	'.1.3.6.1.4.1.13742.4.2.2.1.5';
our $tempLowerCritical	=	'.1.3.6.1.4.1.13742.4.2.2.1.6';
our $tempUpperCritical	=	'.1.3.6.1.4.1.13742.4.2.2.1.7';

my $print_help=0;

#######################################################################
## Function Area
#######################################################################
sub logfile {
    my ($action,$config_ref)=@_;
    if (!defined($config_ref->{'loglevel'}) || $config_ref->{'loglevel'} eq ""){
        $config_ref->{'loglevel'}=0;
    }
    if (("$action" eq "open" ) || ("$action" eq "new" )) {
        open (my $fh, ">>$config_ref->{'logfile'}") or croak "Couldn't open ".$config_ref->{'logfile'}." !\n";
        flock($fh,2) or croak "Can't get lock for $config_ref->{'logfile'} !\n";
        return $fh;
    } else {
        if (defined($config_ref->{'logfile_fh'})){
            close $config_ref->{'logfile_fh'} or croak("Could not close filehandle $config_ref->{'logfile_fh'}\n");
        }
    }
}

sub LOG {
    my ($message,$level,$config_ref)=@_;
    my $time = localtime(time);
    my $fh=$config_ref->{'logfile_fh'};
    if ($level <= $config_ref->{'loglevel'}){
        print $fh "$time [".$config_ref->{'PROGNAME'}."] : $message\n" or croak("Could not write to logfile\n");
    }
    print STDERR "DEBUG: $message\n" if ($config_ref->{'debug'});
}

sub check_community($){
    my ($string)=@_;
    my $stringQuestion = "N";
    if ("$string" eq ""){
            printf("The Communitystring is empty. Are you sure?\n [y/N]]:");
            $stringQuestion = <STDIN>;
            chop($stringQuestion);
            if ($stringQuestion ne "y" && $stringQuestion ne "Y"){
                    die("Communitystring is empty. Abort..\n")
            }
    }
    return scalar "$stringQuestion";
}

sub check_ini_section($$$){
    my ($iniref,$name,$needed_ref)=@_;
    foreach my $needed (@$needed_ref){
        if (!defined($iniref->val("$name","$needed"))){
            return "$needed";
        }
    }
    return 0
}

sub set_errorcode($$){
    my ($status,$result)=@_;
    if ( $result->{'error'} < $status){
        return $status;
    }
    else {
        return $result->{'error'};
    }
}

sub cleanup_and_exit($$){
    my ($conf,$exitcode)=@_;
    logfile('close',$conf);
    exit $exitcode;
}

sub check_raritan($){
    my ($conf)=@_;
    my $error;
    my %session;
    ($session{'snmp'}, $error)=Net::SNMP->session(
                                                    -hostname  	=> $conf->{'hostname'},
                                                    -community 	=> $conf->{'community'},
                                                 );
    if (!defined $session{'snmp'}) {
      LOG("ERROR opening SNMP session to $conf->{'hostname'}: $error",1,$conf);
      $session{'snmp'}->close();
      next;
    }

    # how many sensors should we query?
    my $result=$session{'snmp'}->get_request( -varbindlist => ["$temp_sensor_count" ]);
    if (!defined $result) {
      LOG("ERROR: no result for temp sensor count: $session{'snmp'}->error()",1,$conf);
      $session{'snmp'}->close();
      next;
    }
    my $count=$result->{"$temp_sensor_count"};

    # get temperatures
    for (my $x=1; $x <= $count; $x++){
	# get label
    	my $result=$session{'snmp'}->get_request( -varbindlist => ["$tempSensorLabel.$x"] );
    	$session{$x}{'label'}=$result->{"$tempSensorLabel.$x"};
	# get current temperature
	$result=$session{'snmp'}->get_request( -varbindlist => ["$temperature.$x"]);
	$session{$x}{'temp'}=$result->{"$temperature.$x"};
	$session{$x}{'real_temp'}=$session{$x}{'temp'}/10;
	# get lower warning level
	$result=$session{'snmp'}->get_request( -varbindlist => ["$tempLowerWarning.$x"]);
	$session{$x}{'lowerWarning'}=$result->{"$tempLowerWarning.$x"};
	# get upper warning level
	$result=$session{'snmp'}->get_request( -varbindlist => ["$tempUpperWarning.$x"]);
	$session{$x}{'upperWarning'}=$result->{"$tempUpperWarning.$x"};
	# get lower critical level
	$result=$session{'snmp'}->get_request( -varbindlist => ["$tempLowerCritical.$x"]);
	$session{$x}{'lowerCritical'}=$result->{"$tempLowerCritical.$x"};
	# get upper critical level
	$result=$session{'snmp'}->get_request( -varbindlist => ["$tempUpperCritical.$x"]);
	$session{$x}{'upperCritical'}=$result->{"$tempUpperCritical.$x"};
        #
        # prepare output
        #
	$nagios_result{'perfdata'}="'".$session{$x}{'label'}."'=".$session{$x}{'real_temp'}.
                                   ";".$session{$x}{'lowerWarning'}.":".$session{$x}{'upperWarning'}.
                                   ";".$session{$x}{'lowerCritical'}.":".$session{$x}{'upperCritical'}.";";

	$nagios_result{'output'}="OK: ".$session{$x}{'real_temp'}.
                                 " degrees on '".$session{$x}{'label'}.
                                 "' (".$conf->{'hostname'}.") is inside the tolerance";

	if ($session{$x}{'real_temp'} > $session{$x}{'upperWarning'}){
		$nagios_result{'output'}="WARNING: ".$session{$x}{'real_temp'}.
                                         " degrees on '".$session{$x}{'label'}.
                                         "' (".$conf->{'hostname'}.") is above warning level (".
                                         $session{$x}{'upperWarning'}.")";
		$nagios_result{'error'}=set_errorcode($ERRORS->{'WARNING'},\%nagios_result);
	}
	if ($session{$x}{'real_temp'} < $session{$x}{'lowerWarning'}){
		$nagios_result{'output'}="WARNING: ".$session{$x}{'real_temp'}.
                                         " degrees on '".$session{$x}{'label'}.
                                         "' (".$conf->{'hostname'}.") is below warning level (".
                                         $session{$x}{'lowerWarning'}.")";
		$nagios_result{'error'}=set_errorcode($ERRORS->{'WARNING'},\%nagios_result);
	}
	if ($session{$x}{'real_temp'} > $session{$x}{'upperCritical'}){
		$nagios_result{'output'}="CRITICAL: ".$session{$x}{'real_temp'}.
                                         " degrees on '".$session{$x}{'label'}.
                                         "' (".$conf->{'hostname'}.") is above critical level (".
                                         $session{$x}{'upperCritical'}.")";
		$nagios_result{'error'}=set_errorcode($ERRORS->{'CRITICAL'},\%nagios_result);
	}
        if ($session{$x}{'real_temp'} < $session{$x}{'lowerCritical'}){
		$nagios_result{'output'}="CRITICAL: ".$session{$x}{'real_temp'}.
                                         " degrees on '".$session{$x}{'label'}.
                                         "' (".$conf->{'hostname'}.") exceeds ciritcal level (".
                                         $session{$x}{'lowerCritical'}.")";
		$nagios_result{'error'}=set_errorcode($ERRORS->{'CRITICAL'},\%nagios_result);

	}

    }
    if ($conf->{'debug'}){
        print STDERR "Session: \n".Data::Dumper->Dump([\%session])."\n";
    }
    $session{'snmp'}->close();
  return %nagios_result;
}


#######################################################################
## Main Area
#######################################################################
Getopt::Long::Configure('bundling');
GetOptions( "h|help"         => \$print_help,
            "c|config=s"     => \$conf->{'config'},
            "d|debug"        => \$conf->{'debug'},
            "logfile=s"      => \$conf->{'logfile'},
            "loglevel=i"     => \$conf->{'loglevel'},
            "H|host=s"       => \$conf->{'hostname'},
            "C|community=s"  => \$conf->{'community'},
            "t|timeout=i"    => \$conf->{'timeout'},
) or pod2usage(2);

pod2usage(  -exitstatus => 0,
            -verbose => 1,  # 2 to print full pod
         ) if $print_help;

# Just in case of problems, let's not hang Nagios
$SIG{'ALRM'} = sub {
    print "UNKNOWN -timeout reached (".$conf->{'timeout'}." sec)\n";
    exit 3;
};
alarm($conf->{'timeout'});

# use an existing config file, if exists
if (defined($conf->{'config'}) && -r $conf->{'config'}){
	my $ini = new Config::IniFiles( -file => "$conf->{'config'}",
					-default => "global",
					-allowcontinue => 1);
	if( ! $ini ){
	    croak("Couldn't open $conf->{'config'} : $!\n");
	}
	$conf->{'logfile'}=$ini->val('global','logfile') if $ini->val('global','logfile');
	$conf->{'loglevel'}=$ini->val('global','loglevel') if $ini->val('global','loglevel');
	my $logfile_ref=logfile('open',$conf);
	$conf->{'logfile_fh'}=$logfile_ref;
	if ($conf->{'debug'}){
	    use Data::Dumper;
	    print STDERR "Config:\n".Data::Dumper->Dump([$conf])."\n";
	    print STDERR "Ini:\n".Data::Dumper->Dump([$ini])."\n";
	}
	foreach my $section ( $ini->Sections() ){
	    next if ( $section eq "global" );
	    my $res=check_ini_section($ini,$section,\@needed);
	    if ($res){
		LOG("Needed element $res not found in $section from $conf->{'config'} - skipping",1,$conf);
		next;
	    }
	    $conf->{'hostname'}=$ini->val($section, 'ip');
	    $conf->{'community'}=$ini->val($section, 'community');
            %nagios_result=check_raritan($conf);
            print $nagios_result{'output'}." |".$nagios_result{'perfdata'}."\n";
            LOG("$nagios_result{'output'} |$nagios_result{'perfdata'}",2,$conf);
	}
} else {
    %nagios_result=check_raritan($conf);
    print $nagios_result{'output'}." |".$nagios_result{'perfdata'}."\n";
    LOG("$nagios_result{'output'} |$nagios_result{'perfdata'}",2,$conf);
}

LOG("Exit code of last run: $nagios_result{'error'}",2,$conf);
cleanup_and_exit($conf,$nagios_result{'error'});

__END__

=head1 Get temperatures from Raritan Powerswitches via SNMP

SNMP commandline interface for getting temperature information from the sensors of Raritan  power switches.

=head1 SYNOPSIS

./check_temp_raritan <OPTIONS>

Options:

    -c <file>      | --config <file>

    -H <host>      | --host <host>

    -C <community> | --community <community>

=head1 OPTIONS

=over 8

=item B<--config> F<file>

Use configfile F<file> instead of commandline args.

The content of the file should be as follows:

=over 8

[global]

    logfile=/var/log/check_temp_raritan.log

    loglevel=1

[hostname]

    ip=hostname.example.com

    community=public



=back

=item B<--host> F<host>

Name of the host to query.

=item B<--community> F<community>

SNMP community string (default: public).

=item B<--logfile> F<file>

Use logfile F<file> instead of the default logfile configured in config.

=back

=head1 DESCRIPTION

Raritan power switches can be equipped with external temperature sensors. This script tries to 
get the sensor information via SNMP.

Choose between using a configuration file (together with option -c) or commandline arguments 
(options -C and -H). 

=head1 AUTHORS

=over 8

=item Lars Vogdt <lars@linux-schulserver.de>

=back

=cut
openSUSE Build Service is sponsored by