#! /usr/bin/perl -w
#
# check_dell - nagios plugin
#
# Copyright (C) 2019 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: 6e8ae1fdd7f7313330f5bf03dd6d7dd58bfcc344 $
#

my $libexec;

BEGIN {
  my @lib_folders = qw(/usr/local/nagios/libexec/ /usr/lib/nagios/plugins/ /usr/lib64/nagios/plugins/);
  foreach my $lib (@lib_folders) {
    if (-d $lib)  {
      $libexec = $lib;
      last;
    }
  }
}

use POSIX;
use strict;

use lib $libexec;
use utils qw($TIMEOUT %ERRORS &print_revision &support);
use Socket 1.94 qw(AF_INET AF_INET6 inet_ntop getaddrinfo sockaddr_in sockaddr_in6);

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


## function prototypes

sub in_array($@);
sub print_help();
sub usage();
sub process_arguments();


## module-specific variables

my $PROGNAME = "check_dell";
my $REVISION = '$Rev$';
my $debug;
my $exclude = '';
my $ignore = '';
my $warnings = '';
my $required = '';
my @all_exclude;
my @all_ignore;
my @all_warnings;
my @all_required;
my $countComponents = 0;
my $countSpareDisks = 0;
my $countDisks = 0;
my $state = 'OK';
my $answer = "";
my $result;


## variables for argument handling

my $opt_h;
my $opt_V;
my $status;


## snmp specific variables

my $timeout;
my $hostname;
my $domain = 'udp/ipv4';
my $session;
my $error;
my $response;
my $community = "public";
my $snmpversion = 1;
my $maxmsgsize = 1472; # Net::SNMP default is 1472
my ($seclevel, $authproto, $secname, $authpass, $privpass, $auth_method, $auth_token, $priv_method, $priv_token);
my $context = "";
my $port = 161;
my @snmpoids;

my $key;
my $value;


## Dell system information

my $dellSystemHealth = '1.3.6.1.4.1.674.10892.1.200.10.1';
my $dellSystemInfo = '1.3.6.1.4.1.674.10892.1.300.10.1';
my $dellStorage = '1.3.6.1.4.1.674.10893.1.20.130.4.1';

my $chassisModelName = '1.3.6.1.4.1.674.10892.1.300.10.1.9.1';
my $chassisServiceTagName = '1.3.6.1.4.1.674.10892.1.300.10.1.11.1';


## Dell system states
my %dellStatus = (
  '1', 'other',
  '2', 'unknown',
  '3', 'ok',
  '4', 'warning',
  '5', 'critical',
  '6', 'nonRecoverable',
  '7', 'absent');

my %dellStatusRedundancy = (
  '1', 'other',
  '2', 'unknown',
  '3', 'full',
  '4', 'degraded',
  '5', 'lost',
  '6', 'notRedundant',
  '7', 'redundancyOffline');

my %arrayDiskStates = (
  '1', 'ready',
  '2', 'failed',
  '3', 'online',
  '4', 'offline',
  '6', 'degraded',
  '7', 'recovering',
  '11', 'removed',
  '13', 'non-raid',
  '14', 'notReady',
  '15', 'resynching',
  '22', 'replacing',
  '23', 'spinningDown',
  '24', 'rebuild',
  '25', 'noMedia',
  '26', 'formatting',
  '28', 'diagnostics',
  '34', 'predictiveFailure',
  '35', 'initializing',
  '39', 'foreign',
  '40', 'clear',
  '41', 'unsupported',
  '53', 'incompatible',
  '56', 'readOnly');

my %arrayDiskSpareStates = (
  '1', 'memberVD',
  '2', 'memberDG',
  '3', 'globalHotSpare',
  '4', 'dedicatedHotSpare',
  '5', 'notASpare',
  '99', 'notApplicable');


## Dell system OIDs (ascending numeric order) and state-types

my $arrayDiskName = '1.3.6.1.4.1.674.10893.1.20.130.4.1.2';
my $arrayDiskVendor = '1.3.6.1.4.1.674.10893.1.20.130.4.1.3';
my $arrayDiskState = '1.3.6.1.4.1.674.10893.1.20.130.4.1.4';
my $arrayDiskSerialNo = '1.3.6.1.4.1.674.10893.1.20.130.4.1.7';
my $arrayDiskSpareState = '1.3.6.1.4.1.674.10893.1.20.130.4.1.22';
my $arrayDiskSmartAlertIndication = '1.3.6.1.4.1.674.10893.1.20.130.4.1.31';

my %dellComponents = (
   systemStateGlobalSystemStatus => {
     OID  => '1.3.6.1.4.1.674.10892.1.200.10.1.2.1',
     type => 'dellStatus',
   },
   systemStateChassisStatus => {
     OID  => '1.3.6.1.4.1.674.10892.1.200.10.1.4.1',
     type => 'dellStatus',
   },
   systemStatePowerUnitStatusRedundancy => {
     OID  => '1.3.6.1.4.1.674.10892.1.200.10.1.6.1',
     type => 'dellStatusRedundancy',
   },
   systemStatePowerSupplyStatusCombined => {
     OID  => '1.3.6.1.4.1.674.10892.1.200.10.1.9.1',
     type => 'dellStatus',
   },
   systemStateVoltageStatusCombined => {
     OID  => '1.3.6.1.4.1.674.10892.1.200.10.1.12.1',
     type => 'dellStatus',
   },
   systemStateAmperageStatusCombined => {
     OID  => '1.3.6.1.4.1.674.10892.1.200.10.1.15.1',
     type => 'dellStatus',
   },
   systemStateCoolingUnitStatusRedundancy => {
     OID  => '1.3.6.1.4.1.674.10892.1.200.10.1.18.1',
     type => 'dellStatusRedundancy',
   },
   systemStateCoolingDeviceStatusCombined => {
     OID  => '1.3.6.1.4.1.674.10892.1.200.10.1.21.1',
     type => 'dellStatus',
   },
   systemStateTemperatureStatusCombined => {
     OID  => '1.3.6.1.4.1.674.10892.1.200.10.1.24.1',
     type => 'dellStatus',
   },
   systemStateMemoryDeviceStatusCombined => {
     OID  => '1.3.6.1.4.1.674.10892.1.200.10.1.27.1',
     type => 'dellStatus',
   },
   systemStateChassisIntrusionStatusCombined => {
     OID  => '1.3.6.1.4.1.674.10892.1.200.10.1.30.1',
     type => 'dellStatus',
   },
   systemStateACPowerSwitchStatusRedundancy => {
     OID  => '1.3.6.1.4.1.674.10892.1.200.10.1.33.1',
     type => 'dellStatusRedundancy',
   },
   systemStateACPowerCordStatusCombined => {
     OID  => '1.3.6.1.4.1.674.10892.1.200.10.1.36.1',
     type => 'dellStatus',
   },
   systemStateRedundantMemoryUnitStatusRedundancy => {
     OID  => '1.3.6.1.4.1.674.10892.1.200.10.1.39.1',
     type => 'dellStatusRedundancy',
   },
   systemStateEventLogStatus => {
     OID  => '1.3.6.1.4.1.674.10892.1.200.10.1.41.1',
     type => 'dellStatus',
   },
   systemStatePowerUnitStatusCombined => {
     OID  => '1.3.6.1.4.1.674.10892.1.200.10.1.42.1',
     type => 'dellStatus',
   },
   systemStateCoolingUnitStatusCombined => {
     OID  => '1.3.6.1.4.1.674.10892.1.200.10.1.44.1',
     type => 'dellStatus',
   },
   systemStateACPowerSwitchStatusCombined => {
     OID  => '1.3.6.1.4.1.674.10892.1.200.10.1.46.1',
     type => 'dellStatus',
   },
   systemStateRedundantMemoryUnitStatusCombined => {
     OID  => '1.3.6.1.4.1.674.10892.1.200.10.1.48.1',
     type => 'dellStatus',
   },
   systemStateProcessorDeviceStatusCombined => {
     OID  => '1.3.6.1.4.1.674.10892.1.200.10.1.50.1',
     type => 'dellStatus',
   },
   systemStateBatteryStatusCombined => {
     OID  => '1.3.6.1.4.1.674.10892.1.200.10.1.52.1',
     type => 'dellStatus',
   },
   systemStateSDCardUnitStatusCombined => {
     OID  => '1.3.6.1.4.1.674.10892.1.200.10.1.54.1',
     type => 'dellStatus',
   },
   systemStateSDCardDeviceStatusCombined => {
     OID  => '1.3.6.1.4.1.674.10892.1.200.10.1.56.1',
     type => 'dellStatus',
   }
);


## validate arguments

process_arguments();


## just in case of problems, let's avoid blocking the calling process for too long

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

alarm($timeout);


## main function
print "Dell OMSA Agent Check: " if ( ! defined $debug);

## fetch and print system information
if (in_array('dellSystemInfo', @all_exclude)) {
  print "\ndellSystemInfo will not be checked.\n" if (defined $debug);
} else {
  # response strings
  my $si = "";    # system information (output string)
  my $model = ""; # chassisModelName
  my $tag = "";   # chassisServiceTagName

  printf("dellSystemInfo OID-tree (key-value pairs):\n") if (defined $debug);
  if ( ! defined ($response = $session->get_table($dellSystemInfo))) {
    printf(" - %-35s - OID-tree %s not found\n", "dellSystemInfo", $dellSystemInfo) if (defined $debug);
  } else {
    while (($key, $value) = each %{$response}) {
      printf(" * %-35s = %s\n", $key, $value) if (defined $debug);
      if    ($key =~ /^${chassisModelName}/)      { $model = rtrim($value); }
      elsif ($key =~ /^${chassisServiceTagName}/) { $tag = rtrim($value);   }
    }

    $si = ($model eq "") ? 'unknown model' : $model;
    if ($tag ne "") { $si .= ' ServiceTag ' . $tag; }
    $si .= ' -';

    print "\n" if (defined $debug);
    print $si;
    print "\n" if (defined $debug);
  }
}

## verify components
printf("dellSystemHealth OID-tree (key-value pairs):\n") if (defined $debug);
if ( ! defined ($response = $session->get_table($dellSystemHealth))) {
  printf(" - %-35s - OID-tree %s not found\n", "dellSystemHealth", $dellSystemHealth) if (defined $debug);
} else {
  print "\n" if (defined $debug);
  while (($key, $value) = each %{$response}) {
    printf(" * %-35s = %s\n", $key, $value) if (defined $debug);
    for my $component (keys %dellComponents) {
      # if the component doesn't match the current key, jump to the next element...
      if ($key !~ /^$dellComponents{$component}{OID}/) {
        next;
      }

      # if this component matches but is not to be checked, break the whole loop...
      if (in_array($component, @all_exclude)) {
        print " excluding '".$component."' from check\n" if (defined $debug);
        last;
      }

      # ok, this component needs to be considered...
      $countComponents++;

      # dellStatus ok(3) / dellStatusRedundancy full(3)
      if ($value != 3) {
        my $component_with_value = $component . '=' . $value;
        if (in_array($component, @all_ignore) || in_array($component_with_value, @all_ignore)) {
          printf("critical state ignored because of setting %s\n", $component_with_value) if (defined $debug);
          next;
        } elsif ((in_array($component, @all_warnings) || in_array($component_with_value, @all_warnings)) && $state ne 'CRITICAL') {
          printf("critical state reduced to warning because of setting %s\n", $component_with_value) if (defined $debug);
          $state = 'WARNING';
        } else {
          $state = 'CRITICAL';
        }

        printf(" %s(%d=%s)", $component, $value, eval("\$" . $dellComponents{$component}{type} . "{" . $value . "}"));
        print "\n" if (defined $debug);
      }

      # debug output
      printf("   +-> %s = %d\n", $component, $value) if (defined $debug);
    }
  }
}

## verify storage
printf("\n\ndellStorage OID-tree (key-value pairs):\n") if (defined $debug);
if ( ! defined ($response = $session->get_table($dellStorage))) {
  printf(" - %-35s - OID-tree %s not found\n", "dellStorage", $dellStorage) if (defined $debug);
} else {
  my %arrayDiskInfo = %{$response};
  my $device;
  my $deviceID;
  my $deviceState;
  my $deviceStateValue;
  my $deviceSpareState;
  my $deviceSpareStateValue;
  my $deviceSmartAlert;
  my $deviceSmartAlertValue;

  do {
    $countDisks++;
  } while (exists $arrayDiskInfo{$arrayDiskName . "." . $countDisks});
  $countDisks--;

  printf("\n - found %d disks\n", $countDisks) if (defined $debug);

  for (my $diskID = 1; $diskID <= $countDisks; $diskID++) {
    $deviceID = $arrayDiskInfo{$arrayDiskName . "." . $diskID};
    $device = $arrayDiskInfo{$arrayDiskVendor . "." . $diskID} . " " . $arrayDiskInfo{$arrayDiskSerialNo . "." . $diskID};
    $device =~ s/\t+/ /g;
    $device =~ s/ +/ /g;

    $deviceStateValue = $arrayDiskInfo{$arrayDiskState . "." . $diskID};
    $deviceState = eval("\$arrayDiskStates{" . $deviceStateValue . "}");
    $state = 'CRITICAL' if ($deviceStateValue != 3 && $deviceStateValue != 1);

    $deviceSpareStateValue = $arrayDiskInfo{$arrayDiskSpareState . "." . $diskID};
    $deviceSpareState = eval("\$arrayDiskSpareStates{" . $deviceSpareStateValue . "}");
    $countSpareDisks++ if ($deviceSpareStateValue != 3 && $deviceSpareStateValue != 4);

    $deviceSmartAlertValue = $arrayDiskInfo{$arrayDiskSmartAlertIndication . "." . $diskID};
    $deviceSmartAlert = ($deviceSmartAlertValue == 2) ? 'smart alert' : 'no smart alert';
    $state = 'WARNING' if ($deviceSmartAlertValue == 2 && $state ne 'CRITICAL');

    printf("   +-> arrayDisk %d:", $diskID) if (defined $debug);
    printf(" %s (%s", $device, $deviceID)   if (defined $debug || ($deviceStateValue != 3 && $deviceStateValue != 1) || $deviceSmartAlertValue == 2);
    printf(", %s", $deviceState)            if (defined $debug || ($deviceStateValue != 3 && $deviceStateValue != 1));
    printf(", %s", $deviceSmartAlert)       if (defined $debug || $deviceSmartAlertValue == 2);
    printf(")")                             if (defined $debug || ($deviceStateValue != 3 && $deviceStateValue != 1) || $deviceSmartAlertValue == 2);
    printf("\n")                            if (defined $debug);
  }
}


## set status information
if ($countComponents == 0) {
  $state = "UNKNOWN";
  $answer = " no Dell components found";
} elsif ( ! defined $debug && $state eq 'OK') {
  $answer = " overall system state OK (" . $countComponents . " components and " . $countDisks . " disks checked, ";
  if ($countSpareDisks == 0) {
    $answer .= "no spare disk present";
  } elsif ($countSpareDisks == 1) {
    $answer .= "one spare disk present";
  } else {
    $answer .= $countSpareDisks . " spare disks present";
  }
  $answer .= ").";
}

## terminate with exit code
printf("%s\n", $answer);
exit $ERRORS{$state};


## subroutines

sub ltrim { my $s = shift; $s =~ s/^\s+//;       return $s };
sub rtrim { my $s = shift; $s =~ s/\s+$//;       return $s };
sub  trim { my $s = shift; $s =~ s/^\s+|\s+$//g; return $s };

sub in_array($@) {
   my $needle = shift(@_);
   my %items = map {$_ => 1} @_;
   return (exists($items{$needle})) ? 1 : 0;
}

sub usage() {
  printf("\nMissing arguments!\n\n");
  printf("Usage:\n\n");
  printf("%s -H <HOSTNAME> [-C <community>] [-d] [-x excludecomponent1,excludecomponent2,...]\n", $PROGNAME);
  printf("Copyright (C) 2019 Guenther Mair\n\n");
  printf("See '%s --help' for details.\n", $PROGNAME);
  printf("\n\n");
  exit $ERRORS{"UNKNOWN"};
}

sub print_help() {
  printf("%s plugin\n", $PROGNAME);
  printf("\n+---------+---------+---------+---------+---------+---------+---------+---------\n\n");
  printf("Module specific parameters\n\n");
  printf("  -d (--debug)        debug / verbose mode (print checked details)\n");
  printf("  -x (--exclude)      exclude components from check (separate by commas, may\n");
  printf("                      also contain the special value 'dellSystemInfo')\n");
  printf("  -i (--ignore)       ignore NOT-OK-states for these components\n");
  printf("                      (separate by commas); specific values to be selected may\n");
  printf("                      be specified as in '-i systemStateChassisStatus=4' or\n");
  printf("                      '-i systemStateChassisStatus' (independent of value)\n");
  printf("  -w (--warnings)     use warnings instead of criticals for these components\n");
  printf("                      (separate by commas); specific values to be selected may\n");
  printf("                      be specified as in '-w systemStateChassisStatus=4' or\n");
  printf("                      '-w systemStateChassisStatus' (independent of value)\n");
  printf("  -r (--required)     report critical if any of these components is not found\n");
  printf("                      (separate by commas)\n");
  printf("\n+---------+---------+---------+---------+---------+---------+---------+---------\n\n");
  printf("Currently the module supports the following components\n\n");
  printf("  * dellSystemInfo,\n");
  printf("  * " . join(",\n  * ", keys %dellComponents) . "\n");
  printf("\n+---------+---------+---------+---------+---------+---------+---------+---------\n\n");
  printf("SNMP parameters\n\n");
  printf("  -H (--hostname)     hostname to query (required)\n");
  printf("  -C (--community)    SNMP read community (defaults to public, used with SNMP\n");
  printf("                      v1 and v2c)\n");
  printf("  -v (--snmpversion)  1 for SNMP v1 (default)\n");
  printf("                      2 for SNMP v2c\n");
  printf("                      SNMP v2c will use get_bulk for less overhead\n");
  printf("  -L (--seclevel)     choice of 'noAuthNoPriv', 'authNoPriv', or 'authPriv'\n");
  printf("  -U (--secname)      username for SNMPv3 context\n");
  printf("  -a (--authproto)    authentication protocol (MD5 or SHA1)\n");
  printf("  -A (--authpass)     authentication password (cleartext ASCII or localized key\n");
  printf("                      in hex with 0x prefix generated by using 'snmpkey'\n");
  printf("                      utility auth password and authEngineID)\n");
  printf("  -X (--privpass)     privacy password (cleartext ASCII or localized key\n");
  printf("                      in hex with 0x prefix generated by using 'snmpkey'\n");
  printf("                      utility 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=%s)\n", $TIMEOUT);
  printf("  -V (--version)      plugin version\n");
  printf("  -h (--help)         show this usage screen\n\n");
  print_revision($PROGNAME, '$Revision$');
}

sub process_arguments() {
  $status = GetOptions(
    "V"   => \$opt_V,        "version"        => \$opt_V,
    "h"   => \$opt_h,        "help"           => \$opt_h,
    "d"   => \$debug,        "debug"          => \$debug,
    "x=s" => \$exclude,      "exclude=s"      => \$exclude,
    "i=s" => \$ignore,       "ignore=s"       => \$ignore,
    "w=s" => \$warnings,     "warnings=s"     => \$warnings,
    "r=s" => \$required,     "required=s"     => \$required,
    "v=i" => \$snmpversion,  "snmpversion=i"  => \$snmpversion,
    "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,
    "p=i" => \$port,         "port=i"         => \$port,
    "H=s" => \$hostname,     "hostname=s"     => \$hostname,
    "M=i" => \$maxmsgsize,   "maxmsgsize=i"   => \$maxmsgsize,
    "t=i" => \$timeout,      "timeout=i"      => \$timeout,
  );

  @all_exclude = split(/,/, $exclude);
  @all_ignore = split(/,/, $ignore);
  @all_warnings = split(/,/, $warnings);
  @all_required = split(/,/, $required);

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

  if ($opt_V) {
    print_revision($PROGNAME,'$Revision$');
    exit $ERRORS{'OK'};
  }

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

  # verify validity of hostname
  my ($err, @addrs) = getaddrinfo($hostname, 0);
  if ($err != 0) {
    usage();
    exit $ERRORS{"UNKNOWN"};
  }

  # check IPv4/IPv6 for details see http://backreference.org/2014/12/11/some-common-networking-operations-in-perl/
  if ($addrs[0]->{family} == AF_INET) {
    # port is always 0 when resolving a hostname
    my ($port, $addr4) = sockaddr_in($addrs[0]->{addr});
    $domain = 'udp/ipv4';
    $hostname = inet_ntop(AF_INET, $addr4);
  } else {
    my ($port, $addr6, $scope_id, $flowinfo) = sockaddr_in6($addrs[0]->{addr});
    $domain = 'udp/ipv6';
    $hostname = inet_ntop(AF_INET6, $addr6);
  }
  printf("IPv4/IPv6 host address: %s\n", $hostname) if (defined $debug);

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

  if ($snmpversion =~ /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

  # start snmpv1 / snmpv2
  if ($snmpversion =~ /[12]/) {
    ($session, $error) = Net::SNMP->session(
      -hostname   => $hostname,
      -port       => $port,
      -domain     => $domain,
      -community  => $community,
      -version    => $snmpversion,
      -maxmsgsize => $maxmsgsize
    );

    if ( ! defined($session)) {
      $state = 'UNKNOWN';
      $answer = $error;
      printf("%s: %s\n", $state, $answer);
      exit $ERRORS{$state};
    }
  } elsif ($snmpversion =~ /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;
      printf("%s: %s\n", $state, $answer);
      exit $ERRORS{$state};
    }

  } else {
    $state = 'UNKNOWN';
    printf("%s: No support for SNMP v%s yet\n", $state, $snmpversion);
    exit $ERRORS{$state};
  }
}
