#!/usr/bin/perl -w

#######################################################################
#
# Copyright (c) 2011 Mike bofh@norgie.net
#
# License Information:
# 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 3 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, see <http://www.gnu.org/licenses/>.
#
# Home Site: http://www.norgie.net/tools/check_snmp_disks/
# #####################################################################

use strict;
use Getopt::Long qw(:config no_ignore_case);
use lib "/usr/lib/nagios/plugins/";
use Net::SNMP;
use Time::HiRes qw(gettimeofday ualarm);
use utils qw(%ERRORS);

my $PROGNAME = "check_snmp_disks.pl";
my $VERSION = '$Revision: 1.0 $';

my $config = "/etc/check_snmp_disks.conf";
my $oid_disk = "1.3.6.1.4.1.2021.9.1.2";
my $oid_avail = "1.3.6.1.4.1.2021.9.1.7";
my $oid_used = "1.3.6.1.4.1.2021.9.1.8";
my $oid_percent = "1.3.6.1.4.1.2021.9.1.9";
my $measure_default = "p";
my $warn_default = 80;
my $critical_default = 90;

my $opt_c = '';
my $opt_C = '';
my $opt_h = '';
my $opt_H = '';
my $opt_p = '';
my $opt_t = '';
my $opt_v = 0;
my $opt_V = '';

my $glob_measure = '';
my $glob_warn = '';
my $glob_critical = '';
my $host_measure = '';
my $host_warn = '';
my $host_critical = '';
my %limits;
my %excludes;

local $SIG{ALRM} = sub {
  print "UNKNOWN: Timeout while connecting to snmp daemon\n";
  exit $ERRORS{"UNKNOWN"};
};

GetOptions
  ("C=s"  =>  \$opt_C, "community=s" => \$opt_C,
   "h"  =>  \$opt_h, "help" => \$opt_h,
   "H=s" => \$opt_H, "host=s"  => \$opt_H,
   "p=i" => \$opt_p, "port=i" => \$opt_p,
   "t=i" => \$opt_t, "timeout=i" => \$opt_t,
   "v+" => \$opt_v, "verbose" => \$opt_v
  ) or exit $ERRORS{'UNKNOWN'};

if ($opt_H eq "" || $opt_C eq "") {
  &print_short_help();
}

if ($opt_t eq '') {
  $opt_t = 10;
}

$opt_t = $opt_t * 1000000;

if ($opt_h eq "1") {
  &print_long_help();
}

if ($opt_v eq "") {
  $opt_v = 0;
}

# Let us start by reading in the config file
debug($opt_v, 3, "Parsing config file");
open(FD, "<$config") || die("Cannot open config file");
while(<FD>) {
  my @line = split(/:/, $_);
  if ($line[1] eq "global") {
    $glob_measure = $line[3];
    $glob_warn = $line[4];
    $glob_critical = $line[5];
    debug($opt_v, 3, "Read global threasholds: Meausre: $glob_measure Warn: $glob_warn Critical: $glob_critical");
  } elsif ($line[2] eq "global" && $line[1] eq $opt_H) {
    $host_measure = $line[3];
    $host_warn = $line[4];
    $host_critical = $line[5];
    debug($opt_v, 3, "Read host threasholds: Meausre: $host_measure Warn: $host_warn Critical: $host_critical");
  } elsif ($line[0] eq "e" && $line[1] eq $opt_H) {
    $excludes{$line[2]} = 1;
    debug($opt_v, 3, "Read exlucde for $line[2]");
  } elsif ($line[0] eq "i" && $line[1] eq $opt_H) {
    $limits{$line[2]}->[0] = $line[3];
    $limits{$line[2]}->[1] = $line[4];
    $limits{$line[2]}->[2] = $line[5];
    debug($opt_v, 3, "Read limits fo $line[2]: Meausre: $line[3] Warn: $line[4] Critical: $line[5]");
  }
}
close(FD);
debug($opt_v, 3, "Finished reading config file");

alarm($opt_t);

debug($opt_v, 3, "Connecting to SNMP server");
my ($session, $error) = Net::SNMP->session(
      -hostname  => $opt_H,
      -community => $opt_C,
      -version   => 'snmpv2c',
      -maxmsgsize=> 65535,
);

if (!defined $session) {
      debug($opt_v, 3, "Failed to connect to SNMP server: $error");
      print "UNKNOWN: Could not connect to SNMP server\n";
      exit $ERRORS{"UNKNOWN"};
}

my $snmp_value = '';
my @disks;
my $count = 1;

# Walk the system's SNMP MIB collecting stats for each partition
until($snmp_value eq "noSuchInstance") {
  my $query_disk = $oid_disk . "." . $count;
  my $query_avail = $oid_avail . "." . $count;
  my $query_used = $oid_used . "." . $count;
  my $query_percent = $oid_percent . "." . $count;
  my $result = $session->get_request(-varbindlist => [ $query_disk, $query_avail, $query_used, $query_percent ],);

  if (!defined $result) {
     print "UNKNOWN: Unable to query SNMP server\n";
     debug($opt_v, 3, "SNMP query failed: $session->error()");
     $session->close();
     exit $ERRORS{"UNKNOWN"};
  }

  $snmp_value = $result->{$query_disk};
  unless($snmp_value eq "noSuchInstance") {
    $disks[$count - 1]->[0] = $result->{$query_disk};
    $disks[$count - 1]->[1] = $result->{$query_avail};
    $disks[$count - 1]->[2] = $result->{$query_used};
    $disks[$count - 1]->[3] = $result->{$query_percent};
    debug($opt_v, 3, "Read from SNMP server: Disk: $result->{$query_disk} Available: $result->{$query_avail} Used: $result->{$query_used} Percent: $result->{$query_percent}");
  }

  $count++;
}

$session->close();
debug($opt_v, 3, "Disconnected from SNMP server");

ualarm(0);

my $dok = 0;
my $dwarn = 0;
my $dcritical = 0;
my (@critical_disks, @warn_disks);

# Compare each of the partitons read from SNMP against parameters defined in the config file or failling back to defaults
foreach(@disks) {
  my $disk = $_->[0];
  my $avail = $_->[1];
  my $used = $_->[2];
  my $percent = $_->[3];

  my ($measure, $warn, $critical);
  unless(defined $excludes{$disk}) {
    if (defined $limits{$disk}) {
      $measure = $limits{$disk}->[0];
      $warn = $limits{$disk}->[1];
      $critical = $limits{$disk}->[2];
      debug($opt_v, 2, "Using partition limit measure: $measure warn:$warn critical: $critical");
    } elsif ($host_measure ne "") {
      $measure = $host_measure;
      $warn = $host_warn;
      $critical = $host_critical;
      debug($opt_v, 2, "Using host limit measure: $measure warn: $warn critical: $critical");
    } elsif ($glob_measure ne "") {
      $measure = $glob_measure;
      $warn = $glob_warn;
      $critical = $glob_critical;
      debug($opt_v, 2, "Using global limit measure: $measure warn: $warn critical: $critical");
    } else {
      $measure = $measure_default;
      $warn = $warn_default;
      $critical = $critical_default;
      debug($opt_v, 2, "Using default limit measure: $measure warn: $warn critical: $critical");
    }
    if ($measure eq "b") {
      if ($avail <= $critical) {
        debug($opt_v, 2, "$disk is CRITICAL, $avail block free is <= $critical critical limit");
        push(@critical_disks, $disk);
        $dcritical++;
      } elsif ($avail <= $warn) {
        debug($opt_v, 2, "$disk is WARNING, $avail blocks free is <= $warn warning limit");
        push(@warn_disks, $disk);
        $dwarn++;
      } else {
        debug($opt_v, 2, "$disk is OK, $avail block free is > $warn warning limit");
        $dok++;
      }
    } else {
      if ($percent >= $critical) {
        debug($opt_v, 2, "$disk is CRITICAL, $percent%  is >= $critical% critical limit");
        push(@critical_disks, $disk);
        $dcritical++;
      } elsif ($percent >= $warn) {
        debug($opt_v, 2, "$disk is WARNING, $percent% is >= $warn% warning limit");
        push(@warn_disks, $disk);
        $dwarn++;
      } else {
        debug($opt_v, 2, "$disk is OK, $percent% is < $warn% warning limit");
        $dok++;
      }
    }
  }
}

my $state;

# Set our alert state
if ($dcritical >= 1) {
  $state = "CRITICAL";
} elsif ($dwarn >= 1) {
  $state = "WARNING";
} else {
  $state = "OK";
}

# And finally, we report our fiindings
if ($opt_v == 0) {
  print "$state: $dcritical disks are CRITICAL, $dwarn disks are WARNING & $dok disks are OK\n";
} else {
  my $summary = "C" . $dcritical . "W" . $dwarn . "O" . $dok;
  print "$state: $summary";
  foreach (@critical_disks) {
    print " C$_";
  }
  foreach (@warn_disks) {
    print " W$_";
  }
  print "\n";
}

exit $ERRORS{$state};

sub print_short_help() {
  print "check_snmp_disks.pl: Usage: check_snmp_disks.pl [-hv] -H host -C community name [-p port] [-t timeout]\n";
  exit $ERRORS{"OK"};
}

sub print_long_help() {
  print "check_snmp_disks.pl: Usage: check_snmp_disks.pl [-hv] -H host -C community name [-p port] [-t timeout]\n";
  print "Written by BOfH\n";
  print "Options:\n";
  print "-C --community: Specify the SNMP community name\n";
  print "-h --help: Print this help message\n";
  print "-H --hostname: Specify the host to query\n";
  print "-p --port: Specify a port to use (default is 161)\n";
  print "-t --timeout: Specify the timeout in seconds\n";
  exit $ERRORS{"OK"};
}

sub debug {
  my $debug_level = shift;
  my $mesg_level = shift;
  my $mesg = shift;

  if ($mesg_level <= $debug_level) {
    print "$mesg\n";
  }
}
