#! /usr/bin/perl -w

# Monitor swapspace usage via SNMP.
# Plugin uses UCD SNMP MIB (1.3.6.1.4.1.2021).
# Used in net-snmp packages on linux.
#
# Copyright (C) 2007 by Herbert Stadler
# email: hestadler@gmx.at

# 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/>. 
#
#

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


use POSIX;
use strict;
use Getopt::Long;

use lib ".";
use lib "/usr/lib/nagios/plugins";
use lib "/usr/lib64/nagios/plugins";
use lib "/usr/local/nagios/libexec";

use utils qw(%ERRORS);

use Net::SNMP qw(oid_lex_sort oid_base_match);

my ($opt_version,$opt_help,$opt_verbose);
my ($opt_timeout,$opt_license);
my ($opt_hostname,$opt_community,$opt_port,$opt_snmpvers);
my ($opt_username,$opt_authpasswd,$opt_authproto);
my ($opt_privpasswd,$opt_privproto);
my ($opt_warn,$opt_crit);
my ($PROGNAME,$REVISION);
my ($state,$msg);

use constant DEFAULT_TIMEOUT		=>15;
use constant DEFAULT_PORT    		=>161;
use constant DEFAULT_COMMUNITY  	=>"public";
use constant DEFAULT_SNMPVERS 		=>"2";
use constant DEFAULT_PRIVPROTO		=>"DES";
use constant DEFAULT_AUTHPROTO		=>"MD5";
use constant DEFAULT_WARN      		=>20;
use constant DEFAULT_CRIT      		=>30;

#  UCD SNMP MIB 
my $memIndex     			="1.3.6.1.4.1.2021.4.1.0";
my $memErrorName     			="1.3.6.1.4.1.2021.4.2.0";
my $memTotalSwap     			="1.3.6.1.4.1.2021.4.3.0";
my $memAvailSwap     			="1.3.6.1.4.1.2021.4.4.0";
my $memTotalReal     			="1.3.6.1.4.1.2021.4.5.0";
my $memAvailReal     			="1.3.6.1.4.1.2021.4.6.0";
my $memTotalSwapTXT    			="1.3.6.1.4.1.2021.4.7.0";
my $memAvailSwapTXT    			="1.3.6.1.4.1.2021.4.8.0";
my $memTotalRealTXT    			="1.3.6.1.4.1.2021.4.9.0";
my $memAvailRealTXT    			="1.3.6.1.4.1.2021.4.10.0";
my $memTotalFree    			="1.3.6.1.4.1.2021.4.11.0";
my $memMinimumSwap    			="1.3.6.1.4.1.2021.4.12.0";
my $memShared    			="1.3.6.1.4.1.2021.4.13.0";
my $memBuffer    			="1.3.6.1.4.1.2021.4.14.0";
my $memCached    			="1.3.6.1.4.1.2021.4.15.0";
my $memSwapError    			="1.3.6.1.4.1.2021.4.100.0";
my $memSwapErrorMsg    			="1.3.6.1.4.1.2021.4.101.0";


$ENV{'PATH'}='';
$ENV{'BASH_ENV'}=''; 
$ENV{'ENV'}='';
$PROGNAME = "check_swapspace_ucd";
$REVISION = "1.3";

# checking commandline arguments
my $arg_status = check_args();
if ($arg_status){
  print "ERROR: some arguments wrong\n";
  exit $ERRORS{"UNKNOWN"};
}

# set alarmhandler for timeout handling
$SIG{'ALRM'} = sub {
  print ("ERROR: plugin timed out after $opt_timeout seconds \n");
  exit $ERRORS{"UNKNOWN"};
};

alarm($opt_timeout);

# let's see if the server wants to speak with us
my ($snmp_session,$snmp_error)=open_snmp_session($opt_hostname);
if ( ! defined ($snmp_session)) {
  print "ERROR: Could not open connection: $snmp_error \n";
  exit $ERRORS{'UNKNOWN'};
}

$snmp_session->translate(['-endofmibview'=>0,'-nosuchobject'=>0,'-nosuchinstance'=>0]);

#  Reading necessary OIDs
my $oids=build_oid_table();
my $p_memoids=get_request ($oids);

$snmp_session->close;

if ( $opt_verbose ) {
  print_MemoryValues ();
}

my $l_swapConf =$p_memoids->{$memTotalSwap};
my $l_swapAvail=$p_memoids->{$memAvailSwap};
my $l_swapUsed =$l_swapConf - $l_swapAvail;

my $l_pct_used = $l_swapUsed * 100 / $l_swapConf;

if ( $l_pct_used < $opt_warn ){
  $msg = sprintf("SWAP OK - No Problems found (Usage=%d%%)",$l_pct_used);
  $state = $ERRORS{'OK'};
}elsif ( $l_pct_used < $opt_crit ){
  $msg = sprintf("SWAP WARNING - Usage=%d%%",$l_pct_used);
  $state = $ERRORS{'WARNING'};
}else{
  $msg = sprintf("SWAP CRITICAL - Usage=%d%%",$l_pct_used);
  $state = $ERRORS{'CRITICAL'};
}

# and now "over and out"

print "$msg\n";
exit $state;




#--------------------------------------------------------------------------#
# S U B R O U T I N E S                                                    #
#--------------------------------------------------------------------------#

sub open_snmp_session {
  my ($l_host)=@_;

  my ($snmp_session,$snmp_error);

  # open SNMP Session to Server
  if ( $opt_snmpvers eq "3" ) {
    if ( defined ($opt_authpasswd)) {
      if ( defined ($opt_privpasswd)) {
	($snmp_session,$snmp_error)=Net::SNMP->session(
	    -hostname 		=> 	$l_host,
	    -port		=>	$opt_port || 161,
	    -timeout		=>	2,
	    -retries		=>	2,
	    -maxmsgsize		=>	16384,
	    -version		=>	$opt_snmpvers,
	    -username		=> 	$opt_username,
	    -authpassword	=> 	$opt_authpasswd,
	    -authprotocol	=> 	$opt_authproto,
	    -privpassword	=> 	$opt_privpasswd,
	    -privprotocol	=> 	$opt_privproto,
	    );
      } else {
	($snmp_session,$snmp_error)=Net::SNMP->session(
	    -hostname 		=> 	$l_host,
	    -port		=>	$opt_port || 161,
	    -timeout		=>	2,
	    -retries		=>	2,
	    -maxmsgsize		=>	16384,
	    -version		=>	$opt_snmpvers,
	    -username		=> 	$opt_username,
	    -authpassword	=> 	$opt_authpasswd,
	    -authprotocol	=> 	$opt_authproto,
	    );
      } 
    } else {
	($snmp_session,$snmp_error)=Net::SNMP->session(
	    -hostname 		=> 	$l_host,
	    -port		=>	$opt_port || 161,
	    -timeout		=>	2,
	    -retries		=>	2,
	    -maxmsgsize		=>	16384,
	    -version		=>	$opt_snmpvers,
	    -username		=> 	$opt_username,
	    );
    }
  } else {
    ($snmp_session,$snmp_error)=Net::SNMP->session(
    	-hostname 	=> 	$l_host,
	-community 	=> 	$opt_community || 'public',
	-port		=>	$opt_port || 161,
	-timeout	=>	2,
	-retries	=>	2,
	-maxmsgsize	=>	16384,
	-version	=>	$opt_snmpvers,
	);
  }
  return ($snmp_session,$snmp_error);
}

sub get_request {
  my ($l_oid)=@_;

  my $l_snmp_result=$snmp_session->get_request(
 	-varbindlist	=>	$l_oid,
  	);

  #if ( ! defined ($l_snmp_result)) {
  if ($snmp_session->error_status != 0) {
    print "ERROR %d get_request: ",$snmp_session->error_status,$snmp_session->error,"\n";
    $snmp_session->close;
    exit $ERRORS{'UNKNOWN'};
  }
  return $l_snmp_result;
}

sub build_oid_table {
  my @l_oids;

  push @l_oids,$memIndex;
  push @l_oids,$memErrorName;
  push @l_oids,$memTotalSwap;
  push @l_oids,$memAvailSwap;
  push @l_oids,$memTotalReal;
  push @l_oids,$memAvailReal;
  #push @l_oids,$memTotalSwapTXT;
  #push @l_oids,$memAvailSwapTXT;
  #push @l_oids,$memTotalRealTXT;
  #push @l_oids,$memAvailRealTXT;
  push @l_oids,$memTotalFree;
  push @l_oids,$memMinimumSwap;
  push @l_oids,$memShared;
  push @l_oids,$memBuffer;
  push @l_oids,$memCached;
  push @l_oids,$memSwapError;
  push @l_oids,$memSwapErrorMsg;

  return \@l_oids;
}

sub check_args {
  Getopt::Long::Configure('bundling');
  GetOptions
	("V"   			=> \$opt_version,
	 "version"   		=> \$opt_version,
	 "L"   			=> \$opt_license, 
	 "license"   		=> \$opt_license, 
	 "v"   			=> \$opt_verbose, 
	 "verbose"   		=> \$opt_verbose, 
	 "h|?" 			=> \$opt_help,
	 "help"   		=> \$opt_help,
	 "t=i" 			=> \$opt_timeout, 
	 "timeout=i" 		=> \$opt_timeout, 
	 "H=s" 			=> \$opt_hostname, 
	 "hostname=s" 		=> \$opt_hostname, 
	 "C=s" 			=> \$opt_community, 
	 "community=s" 		=> \$opt_community, 
	 "p=i" 			=> \$opt_port, 
	 "port=i" 		=> \$opt_port, 
	 "s=s" 			=> \$opt_snmpvers, 
	 "snmpvers=s" 		=> \$opt_snmpvers, 
         "u=s"       		=> \$opt_username,
         "username=s"       	=> \$opt_username,
         "o=s"   		=> \$opt_authpasswd,
         "authpass=s"   	=> \$opt_authpasswd,
         "r=s"   		=> \$opt_authproto,
         "authprot=s"   	=> \$opt_authproto,
         "O=s"   		=> \$opt_privpasswd,
         "privpass=s"   	=> \$opt_privpasswd,
         "R=s"   		=> \$opt_privproto,
         "privprot=s"   	=> \$opt_privproto,
	 "w=i" 			=> \$opt_warn, 
	 "warn=i" 		=> \$opt_warn, 
	 "c=i" 			=> \$opt_crit, 
	 "crit=i" 		=> \$opt_crit, 
	 );

  if ($opt_license) {
    print_gpl($PROGNAME,$REVISION);
    exit $ERRORS{'OK'};
  }

  if ($opt_version) {
    print_revision($PROGNAME,$REVISION);
    exit $ERRORS{'OK'};
  }

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

  if ( ! defined($opt_hostname)){
    print "\nERROR: Hostname not defined\n\n";
    print_usage();
    exit $ERRORS{'UNKNOWN'};
  }

  unless (defined $opt_snmpvers) {
    $opt_snmpvers = DEFAULT_SNMPVERS;
  }
  if (($opt_snmpvers ne "1") && ($opt_snmpvers ne "2") && ($opt_snmpvers ne "3")) {
    printf ("\nERROR: SNMP Version %s unknown\n",$opt_snmpvers);
    print_usage();
    exit $ERRORS{'UNKNOWN'};
  }

  unless (defined $opt_timeout) {
    $opt_timeout = DEFAULT_TIMEOUT;
  }

  unless (defined $opt_port) {
    $opt_port = DEFAULT_PORT;
  }

  unless (defined $opt_community) {
    $opt_community = DEFAULT_COMMUNITY;
  }

  if (defined $opt_privpasswd) {
    unless (defined $opt_privproto) {
      $opt_privproto = DEFAULT_PRIVPROTO;
    }
  }

  if (defined $opt_authpasswd) {
    unless (defined $opt_authproto) {
      $opt_authproto = DEFAULT_AUTHPROTO;
    }
  }

  if ($opt_snmpvers eq 3) {
    unless (defined $opt_username) {
      printf ("\nERROR: SNMP Version %s: please define username\n",$opt_snmpvers);
      print_usage();
      exit $ERRORS{'UNKNOWN'};
    }
  }

  unless (defined $opt_warn) {
    $opt_warn = DEFAULT_WARN;
  }

  unless (defined $opt_crit) {
    $opt_crit = DEFAULT_CRIT;
  }

  if ( $opt_warn >= $opt_crit ) {
    printf ("\nERROR: parameter -w greater or equal -c, w=%d%% / c=%d%%\n",$opt_warn,$opt_crit);
    exit $ERRORS{'UNKNOWN'};
  }

  return $ERRORS{'OK'};
}

sub print_usage {
  print "Usage: $PROGNAME [-h] [-L] [-t timeout] [-v] [-V] [-C community] [-p port] [-s 1|2|3] [-w warningpct] [-c criticalpct] -H hostname  \n\n";
  print "SNMP version 3 specific: [-u username] [-o authpass] [-r authprot] [-O privpass] [-R privprot]\n";
}

sub print_help {
  print_revision($PROGNAME,$REVISION);
  print "\n";
  print_usage();
  print "\n";
  print "   Monitor swapspace usage via SNMP\n";
  print "   e.g: used on linux in net-snmp agent.\n\n";
  print "-t (--timeout)      Timeout in seconds (default=",DEFAULT_TIMEOUT,")\n";
  print "-H (--hostname)     Host to monitor\n";
  print "-s (--snmpvers)     SNMP Version [1|2|3] (default=",DEFAULT_SNMPVERS,")\n";
  print "-C (--community)    SNMP Community (default=",DEFAULT_COMMUNITY,")\n";
  print "-p (--port)         SNMP Port (default=",DEFAULT_PORT,")\n";
  print "-w (--warn)         Warning percentage of used swapspace (default=",DEFAULT_WARN,")\n";
  print "-c (--crit)         Critical percentage of used swapspace (default=",DEFAULT_CRIT,")\n";
  print "-h (--help)         Help\n";
  print "-V (--version)      Programm version\n";
  print "-v (--verbose)      Print some useful information\n";
  print "-L (--license)      Print license information\n";
  print "\nSNMP version 3 specific arguments:\n";
  print "-u (--username)     Security Name\n";
  print "-o (--authpassword) Authentication password\n";
  print "-r (--authprotocol) Authentication protocol [md5|sha]\n";
  print "-O (--privpassword) Privacy password\n";
  print "-R (--privprotocol) Privacy protocol [des|aes|3des]\n";
  print "\n";
}

sub print_MemoryValues {
  printhead  ("UCD Memory Values");
  print      ("=================\n");
  #printscalar("Bogus Index",                   $p_memoids->{$memIndex});
  #printscalar("Bogus Name ",                   $p_memoids->{$memErrorName});
  printscalar("Total Swap configured",         $p_memoids->{$memTotalSwap});
  printscalar("Available Swap",                $p_memoids->{$memAvailSwap});
  printscalar("Total Real Memory",             $p_memoids->{$memTotalReal});
  printscalar("Avail Real Memory",             $p_memoids->{$memAvailReal});
  #printscalar("Total virtual memory used by text", $p_memoids->{$memTotalSwapTXT});
  #printscalar("Active virtual memory used by text",$p_memoids->{$memAvailSwapTXT});
  #printscalar("Total Real Memory used by text", $p_memoids->{$memTotalRealTXT});
  #printscalar("Active Real Memory used by text",$p_memoids->{$memAvailRealTXT});
  printscalar("Total Available Memory",         $p_memoids->{$memTotalFree});
  printscalar("Minimum Swap required",          $p_memoids->{$memMinimumSwap});
  printscalar("Total Shared Memory",            $p_memoids->{$memShared});
  printscalar("Total Bufferd Memory",           $p_memoids->{$memBuffer});
  printscalar("Total Cached Memory",            $p_memoids->{$memCached});
  printscalar("Swap Error Flag",                $p_memoids->{$memSwapError});
  printscalar("Swap Error Message",             $p_memoids->{$memSwapErrorMsg});
  print ("\n");
}

sub printhead {
  my ($l_head)=@_;

  printf ("\n%-40s\n",$l_head);
}

sub printtable {
  my ($l_head)=@_;

  printf ("%-40s\n",$l_head);
}

sub printscalar {
  my ($l_arg,$l_oid)=@_;

  printf ("%-35s: %-30s\n",$l_arg,$l_oid);
}

sub printtabular {
  my ($l_arg,$l_oid)=@_;

  printf ("%-25s: %-30s\n",$l_arg,$l_oid);
}


sub print_gpl {
  print <<EOD;

  Copyright (C) 2007 by Herbert Stadler
  email: hestadler\@gmx.at

  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/>. 

EOD

}

sub print_revision {
  my ($l_prog,$l_revision)=@_;

  print <<EOD

$l_prog $l_revision, Copyright (C) 2007 Herbert Stadler

This program comes with ABSOLUTELY NO WARRANTY; 
for details type "$l_prog -L".
EOD
}


#__END__


=head1 NAME

 check_swapspace_ucd

=head1 DESCRIPTION

 Checking swapspace usage via SNMP
 e.g: used on linux in net-snmp agent

 Plugin created for Nagios Monitoring.

=head1 SYNOPSIS

 check_swapspace_ucd -H <hostname> 

 for more information concerning this plugin call:
     check_swapspace_ucd -h
     perldoc check_swapspace_ucd

 more information concerning the configuration of the UCD SNMP Package:
     man snmpd.conf


=head1 AUTHOR

 Herbert Stadler, Austria (hestadler@gmx.at)
 December 2007

 This plugin is a contribution to the nagios community.

=head1 REQUIRED SOFTWARE

 from search.cpan.org
   Net::SNMP Package   	e.g: Net-SNMP-5.2.0.tar.gz

=head1 HOW TO CHECK THE SERVER FUNCTIONALITY

 Example:
   snmpwalk 172.29.130.201 -v2c -c public enterprises.2021.4.3.0
   snmpwalk 172.29.130.201 -v2c -c public enterprises.2021.4.4.0

 should return some lines like these:

  UCD-SNMP-MIB::memTotalSwap.0 = INTEGER: 262136
  UCD-SNMP-MIB::memAvailSwap.0 = INTEGER: 262136


=head1 CONFIGURATION IN NAGIOS

 Copy this plugin to the nagios plugin installation directory 
 e.g.: /usr/lib(64)/nagios/plugin

 COMMAND DEFINITION:

 # "check_swapspace_ucd" command definition
 define command{
    command_name    check_swapspace_ucd
    command_line    $USER1$/check_swapspace_ucd -H $HOSTADDRESS$
    }


=head1 PLUGIN HISTORY

 Version 1.0 - 2007-12-15	first release
 Version 1.1 - 2007-12-19       fixed problem with **ePN
                                (Missing right curly or square ...)
 Version 1.2 - 2009-02-17       some new "use lib .." statements
 Version 1.3 - 2010-03-24       check error_status of snmp call

=head1 COPYRIGHT AND DISCLAIMER

 Copyright (C) 2007 by Herbert Stadler
 email: hestadler@gmx.at

 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/>. 
 

=cut



