#! /usr/bin/perl -w
#
# check_cpu - nagios plugin
#
# Copyright (C) 2008 Guenther Mair,
# Derived from check_ifoperstatus by Christoph Kron.
#
# 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, USA.
#
#
# Report bugs to:  guenther.mair@hoslo.ch
#
# $Id: dfba67189024301934d436dc940e1721df7ac018 $
#

use POSIX;
use strict;
use lib "/usr/local/nagios/libexec";
use utils qw($TIMEOUT %ERRORS &print_revision &support);

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

my $PROGNAME = 'check_cpu';
my $REVISION = '$Id: dfba67189024301934d436dc940e1721df7ac018 $';
sub print_help ();
sub usage ();
sub process_arguments ();

my $DEBUG = 0;

my $timeout;
my $hostname;
my $session;
my $error;
my $response;
my $sub_response;
my $opt_h;
my $opt_V;
my $key;
my $lastc;
my $name;
my $status;
my $answer       = "";
my $snmpkey      = 0;
my $community    = "public";
my $snmp_version = 1;
my $maxmsgsize   = 1472;       # Net::SNMP default is 1472
my ($seclevel, $authproto, $secname, $authpass, $privpass, $auth_method, $auth_token, $priv_method, $priv_token);
my $port = 161;
my $warning;
my $warningAvg;
my $critical;
my $criticalAvg;
my @tmp;
my $count        = 0;
my $sum          = 0;
my $currentOID   = '';
my $state        = 'OK';
my $overallState = 'OK';

my $hrProcessorLoad = '1.3.6.1.2.1.25.3.3.1.2';

## Validate Arguments

process_arguments();

## Just in case of problems, let's not hang Nagios

$SIG{'ALRM'} = sub {
  print("ERROR: No snmp response from $hostname (alarm)\n");
  exit $ERRORS{"UNKNOWN"};
};

alarm($timeout);

## Main function

if (!defined($response = $session->get_table($hrProcessorLoad))) {
  $answer = $session->error;
  $session->close;
  $state = 'UNKNOWN';
  printf("$state: SNMP error with snmp version $snmp_version ($answer)\n");
  $session->close;
  exit $ERRORS{$state};
}

print "CPU load:";

foreach $key (keys %{$response}) {
  $count++;
  $sum += $response->{$key};

  if ($response->{$key} > $critical) {
    $state        = 'CRITICAL';
    $overallState = 'CRITICAL';
  } elsif ($response->{$key} > $warning) {
    $state        = 'WARNING';
    $overallState = 'WARNING' unless ($overallState eq 'CRITICAL');
  } else {
    $state = 'OK';
  }

  $key =~ /.*\.(\d+)$/;
  $snmpkey = $1;
  print " cpuID " . $snmpkey . " at " . $response->{$key} . "% (" . $state . ")";
}

# more than 1 cpu
$sum = $sum / $count;
if ($count > 1) {
  if ($sum > $criticalAvg) {
    $state        = 'CRITICAL';
    $overallState = 'CRITICAL';
  } elsif ($sum > $warningAvg) {
    $state        = 'WARNING';
    $overallState = 'WARNING' unless ($overallState eq 'CRITICAL');
  } else {
    $state = 'OK';
  }
  printf(" - average load at %.1f%% (%s)", $sum, $state);
}

printf("|load=%.0f\n", $sum);
exit $ERRORS{$overallState};

### subroutines

sub usage() {
  printf "\nMissing arguments!\n";
  printf "\n";
  printf "usage: \n";
  printf "check_cpu -w <MAXSINGLE>,<MAXAVG> -c <MAXSINGLE>,<MAXAVG> -H <HOSTNAME> [-C <community>]\n";
  printf "Copyright (C) 2008 Guenther Mair\n";
  printf "\n\n";
  exit $ERRORS{"UNKNOWN"};
}

sub print_help() {
  printf "check_cpu plugin for Nagios\n";
  printf "\nUsage:\n";
  printf "   -H (--hostname)   Hostname to query - (required)\n";
  printf "   -C (--community)  SNMP read community (defaults to public,\n";
  printf "                     used with SNMP v1 and v2c\n";
  printf "   -v (--snmp_version)  1 for SNMP v1 (default)\n";
  printf "                        2 for SNMP v2c\n";
  printf "                        SNMP v2c will use get_bulk for less overhead\n";
  printf "                        if monitoring with -d\n";
  printf "   -L (--seclevel)   choice of \"noAuthNoPriv\", \"authNoPriv\", or	\"authPriv\"\n";
  printf "   -U (--secname)    username for SNMPv3 context\n";
  printf "   -w (--warning)    single and average processor load that will cause warning\n";
  printf "   -c (--critical)   single and average processor load that will cause critical\n";
  printf "   -A (--authpass)   authentication password (cleartext ascii or localized key\n";
  printf "                     in hex with 0x prefix generated by using	\"snmpkey\" utility\n";
  printf "                     auth password and authEngineID\n";
  printf "   -a (--authproto)  Authentication protocol ( MD5 or SHA1)\n";
  printf "   -X (--privpass)   privacy password (cleartext ascii or localized key\n";
  printf "                     in hex with 0x prefix generated by using	\"snmpkey\" utility\n";
  printf "                     privacy password and authEngineID\n";
  printf "   -p (--port)       SNMP port (default 161)\n";
  printf "   -M (--maxmsgsize) Max message size - usefull only for v1 or v2c\n";
  printf "   -t (--timeout)    seconds before the plugin times out (default=$TIMEOUT)\n";
  printf "   -V (--version)    Plugin version\n";
  printf "   -h (--help)       usage help \n\n";
  printf " -c and -w must be specified\n\n";
  print_revision($PROGNAME, '$Id: dfba67189024301934d436dc940e1721df7ac018 $');

}

sub process_arguments() {
  $status = GetOptions(
    "V"              => \$opt_V,
    "version"        => \$opt_V,
    "h"              => \$opt_h,
    "help"           => \$opt_h,
    "v=i"            => \$snmp_version,
    "snmp_version=i" => \$snmp_version,
    "C=s"            => \$community,
    "community=s"    => \$community,
    "L=s"            => \$seclevel,
    "seclevel=s"     => \$seclevel,
    "a=s"            => \$authproto,
    "authproto=s"    => \$authproto,
    "U=s"            => \$secname,
    "secname=s"      => \$secname,
    "A=s"            => \$authpass,
    "authpass=s"     => \$authpass,
    "X=s"            => \$privpass,
    "privpass=s"     => \$privpass,
    "c=s"            => \$critical,
    "critical=s"     => \$critical,
    "w=s"            => \$warning,
    "warning=s"      => \$warning,
    "p=i"            => \$port,
    "port=i"         => \$port,
    "H=s"            => \$hostname,
    "hostname=s"     => \$hostname,
    "M=i"            => \$maxmsgsize,
    "maxmsgsize=i"   => \$maxmsgsize,
    "t=i"            => \$timeout,
    "timeout=i"      => \$timeout,
  );

  if ($status == 0) {
    print_help();
    exit $ERRORS{'OK'};
  }

  if ($opt_V) {
    print_revision($PROGNAME, '$Id: dfba67189024301934d436dc940e1721df7ac018 $');
    exit $ERRORS{'OK'};
  }

  if ($opt_h) {
    print_help();
    exit $ERRORS{'OK'};
  }

  if (!utils::is_hostname($hostname)) {
    usage();
    exit $ERRORS{"UNKNOWN"};
  }

  unless (defined $critical) {
    printf "Single and average processor load that will cause a critical status (-c).\n";
    usage();
    exit $ERRORS{"UNKNOWN"};
  }
  @tmp         = split(/,/, $critical);
  $critical    = $tmp[0];
  $criticalAvg = $tmp[1];

  unless (defined $warning) {
    printf "Single and average processor load that will cause a warning status (-w).\n";
    usage();
    exit $ERRORS{"UNKNOWN"};
  }
  @tmp        = split(/,/, $warning);
  $warning    = $tmp[0];
  $warningAvg = $tmp[1];

  unless (defined $timeout) {
    $timeout = $TIMEOUT;
  }

  if ($snmp_version =~ /3/) {

    # Must define a security level even though default is noAuthNoPriv
    # v3 requires a security username
    if (defined $seclevel && defined $secname) {

      # Must define a security level even though defualt is noAuthNoPriv
      unless (grep /^$seclevel$/, qw(noAuthNoPriv authNoPriv authPriv)) {
        usage();
        exit $ERRORS{"UNKNOWN"};
      }

      # Authentication wanted
      if ($seclevel eq 'authNoPriv' || $seclevel eq 'authPriv') {
        unless ($authproto eq 'MD5' || $authproto eq 'SHA1') {
          usage();
          exit $ERRORS{"UNKNOWN"};
        }

        if (!defined $authpass) {
          usage();
          exit $ERRORS{"UNKNOWN"};
        } else {
          if ($authpass =~ /^0x/) {
            $auth_method = "-authkey";
            $auth_token  = $authpass;
          } else {
            $auth_method = "-authpassword";
            $auth_token  = $authpass;
          }
        }
      }

      # Privacy (DES encryption) wanted
      if ($seclevel eq 'authPriv') {
        if (!defined $privpass) {
          usage();
          exit $ERRORS{"UNKNOWN"};
        } else {
          if ($authpass =~ /^0x/) {
            $priv_method = "-privkey";
            $priv_token  = $privpass;
          } else {
            $priv_method = "-privpassword";
            $priv_token  = $privpass;
          }
        }
      }

    } else {
      usage();
      exit $ERRORS{'UNKNOWN'};
    }
  }    # end snmpv3

  if ($snmp_version =~ /[12]/) {
    ($session, $error) = Net::SNMP->session(
      -hostname   => $hostname,
      -community  => $community,
      -port       => $port,
      -version    => $snmp_version,
      -maxmsgsize => $maxmsgsize
    );

    if (!defined($session)) {
      $state  = 'UNKNOWN';
      $answer = $error;
      print("$state: $answer");
      exit $ERRORS{$state};
    }

  } elsif ($snmp_version =~ /3/) {
    if ($seclevel eq 'noAuthNoPriv') {
      ($session, $error) = Net::SNMP->session(
        -hostname => $hostname,
        -port     => $port,
        -domain   => $domain,
        -version  => $snmpversion,
        -username => $secname
      );
    } elsif ($seclevel eq 'authNoPriv') {
      ($session, $error) = Net::SNMP->session(
        -hostname     => $hostname,
        -port         => $port,
        -domain       => $domain,
        -version      => $snmpversion,
        -username     => $secname,
        $auth_method  => $auth_token,
        -authprotocol => $authproto
      );
    } elsif ($seclevel eq 'authPriv') {
      ($session, $error) = Net::SNMP->session(
        -hostname     => $hostname,
        -port         => $port,
        -domain       => $domain,
        -version      => $snmpversion,
        -username     => $secname,
        $auth_method  => $auth_token,
        -authprotocol => $authproto,
        $priv_method  => $priv_token,
      );
    }

    if (!defined($session)) {
      $state  = 'UNKNOWN';
      $answer = $error;
      print("$state: $answer");
      exit $ERRORS{$state};
    }

  } else {
    $state = 'UNKNOWN';
    print("$state: No support for SNMP v$snmp_version yet\n");
    exit $ERRORS{$state};
  }
}
## End validation
