#!/usr/local/bin/perl

=pod

=head1 NAME

check_snmp_cisco_temp.pl - Check temperature sensors on Cisco devices
that support temperature sensors.

=head1 DESCRIPTION

This script will check the temperatures of all temperature sensors on
a Cisco device.  Pass in warning and critical temperature thresholds
(in degrees Celcius) and the script will alert if one or more temperature
sensors on the device exceed the thresholds supplied to the script; the
highest alert level becomes the alert level for the script.

If the device does not support temperature sensors, UNKNOWN will be
returned.

You must specify which sensors to check by using the --sensor-regex
switch.  If you pass the word 'all' to the switch, every sensor will
be checked; if you specify a regular expression, only sensors matching
that expression will be matched.  For example, you could specify

--sensor-regex Inlet

to only check inlet sensors.  Regular expression matching is case
insensitive.

Please note that you may need to use the --snmp-max-msg-size switch to
increase the SNMP buffer size for devices with a large number of 
sensors.  50000 is a good place to start.

=cut

#
#  Descriptions of sensors
#  snmpwalk -c read-only -v 2c 192.168.3.1 1.3.6.1.2.1.47.1.1.1.1.2 
#
#  Type - 8 == celsius, 12 == truth value
#  snmpwalk -c read-only -v 2c 192.168.3.1 1.3.6.1.4.1.9.9.91.1.1.1.1.1
#
#  Scale (exponent) - 9 == units (10^0)
#  snmpwalk -c read-only -v 2c 192.168.3.1 1.3.6.1.4.1.9.9.91.1.1.1.1.2
#
#  Most recent measurements
#  snmpwalk -c read-only -v 2c 192.168.3.1 1.3.6.1.4.1.9.9.91.1.1.1.1.4
#
#  Status 1 == ok, 2 == cannot report, 3 == broken
#  snmpwalk -c read-only -v 2c 192.168.3.1 1.3.6.1.4.1.9.9.91.1.1.1.1.5

sub check_snmp_cisco_temp {

    use strict;

    use FindBin;
    use lib "$FindBin::Bin/lib";

    use Nagios::Plugin::SNMP;

    my $LABEL = 'SNMP-CISCO-TEMP';

    my $USAGE = <<EOF;
Usage: %s --sensor-regex 'Inlet' --warning 30 --critical 36
EOF

    my $plugin = Nagios::Plugin::SNMP->new(
        'shortname' => $LABEL,
        'usage' => $USAGE
    );

    $plugin->add_arg(
        'spec' => 'sensor-regex|S=s@',
        'help' => "-S, --sensor-regex\n" .
                  "   Regular expression to use to select sensors to\n" .
                  "   read.  For example, -S 'Inlet' would select only\n" .
                  "   inlet sensors on a device.  Use the word 'all' to\n" .
                  "   have the script check ALL sensors.",
        'required' => 1
    );

    $plugin->getopts;

    my $DEBUG = $plugin->opts->get('snmp-debug');

    #  Get the sensor index

    my $sensor_oid = '1.3.6.1.2.1.47.1.1.1.1.2';

    my $result = $plugin->walk($sensor_oid)->{$sensor_oid};
    
    debug("Retrieved sensor list");

    my %sensors;

    debug("Retrieved " . keys(%$result) . "sensor descriptions");

    my @idxs;

    my $sensor_regexes = $plugin->opts->get('sensor-regex');

    for my $var (keys %$result) {

        my $name = $result->{$var};

        if (lc($sensor_regexes->[0]) ne 'all') {

            #  Choose just ones that match our regexes

            my $matched = 0;

            for my $expr (@$sensor_regexes) {

                if ($name =~ m/$expr/i) {
                    debug("Matched sensor $name against $expr");
                    $matched = 1;
                    last;

                }

            }

            next unless $matched == 1;

        }

        $name =~ s/['"]//g;
        $name =~ s/temperature\s+sensor//ig;
        $name =~ s/\s+$//;
        $name =~ s/^\s+//;

        my $idx = ($var =~ m/(\d+)$/)[0];

        debug("Setting oid $var to name $name and index $idx");

        $sensors{$idx} = {
            'name'   => $name,
            'type'   => 0,
            'scale'  => 0,
            'temp'   => 0,
            'status' => 0
        };

    }

    debug("Checking " .  keys(%sensors) . " sensors");

    my $prefix = '.1.3.6.1.4.1.9.9.91.1.1.1.1';

    #  Check type, eliminate those that are not type 8 (celsius)
    for my $idx (keys %sensors) {

        my @oids = (
                "${prefix}.1.$idx", #  Type - 8 is temp
                "${prefix}.2.$idx", #  Scale - 9 == 10^0
                "${prefix}.4.$idx", #  Measurement
                "${prefix}.5.$idx"  #  Status - 1 ok, 2 cannot report, 3 broken
        );

        $result = $plugin->get(@oids);
        
        foreach my $p (keys %$result) {

            if ($p eq "${prefix}.1.$idx") {
                $sensors{$idx}->{'type'} = $result->{$p};
            }

            if ($p eq "${prefix}.2.$idx") {
                $sensors{$idx}->{'scale'} = $result->{$p};
            }

            if ($p eq "${prefix}.4.$idx") {
                $sensors{$idx}->{'temp'} = $result->{$p};
            }

            if ($p eq "${prefix}.5.$idx") {
                $sensors{$idx}->{'status'} = $result->{$p};
            }

        }

    }

    #  Close and destroy session
    $plugin->close();

    #  Now check sizes

    my $w = $plugin->opts->get('warning');
    my $c = $plugin->opts->get('critical');

    my @ok;
    my @warn;
    my @crit;

    my $level = OK;

    my $has_temperature_sensors = 0;

    my @perf_data;

    for my $idx (keys %sensors) {

        my $s = $sensors{$idx};

        #  Type 8 == temperature, ignore all others
        next if $s->{'type'} != 8;

        $has_temperature_sensors = 1;

        #  Status of 2 is non-measurable, 3 is broken
        next if $s->{'status'} == 2;

        push(@perf_data, perf_data($s->{'name'}, $s->{'temp'}, $w, $c));

        if ($s->{'status'} == 3) {
            $level = CRITICAL;
            push(@crit, "$s->{name} offline");
            next;
        }

        if ($s->{'temp'} >= $c) {
            $level = CRITICAL;
            push(@crit, "$s->{'name'} ($s->{'temp'}c >= ${c}c)");
        } elsif ($s->{'temp'} >= $w) {
            $level = WARNING unless $level == CRITICAL;
            push(@warn, "$s->{'name'} ($s->{'temp'}c >= ${w}c)");
        } else {
            push(@ok, "$s->{'name'} $s->{'temp'}c");
        }
    }

    if (! $has_temperature_sensors) {
        $plugin->nagios_die("Device does not have temperature sensors");
    }

    print "$LABEL ";

    if (scalar(@crit) > 0) {
        print "CRITICAL - " . join(', ', @crit) . ' ';
    }

    if (scalar(@warn) > 0) {
        print "WARNING - " . join(', ', @warn) . ' ';
    }

    if (scalar(@ok) > 0) {
        print "OK - " . join(', ', @ok) . ' ';
    }

    print " |" . join(' ', @perf_data) . "\n";

    return $level;

    sub perf_data {

        my $module = shift;
        my $temp = shift;
        my $warn = shift;
        my $crit = shift;

        $module =~ s/\s+/_/g;
        $module = ucfirst($module);

        return "'${module}'=$temp;$warn;$crit;;";
    }

    sub debug {

        return unless $DEBUG == 1;

        my $msg = shift;

        print STDERR scalar(localtime()) . ": $msg\n";

    }
}

exit check_snmp_cisco_temp();
