#! /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: 36e94da23770f65bee20af62ccb5868e84c1c3a3 $
#

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 = '$Id: 36e94da23770f65bee20af62ccb5868e84c1c3a3 $';
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 $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);

    $deviceSpareStateValue = $arrayDiskInfo{ $arrayDiskSpareState . "." . $diskID };
    $deviceSpareState      = eval("\$arrayDiskSpareStates{" . $deviceSpareStateValue . "}");
    $state                 = 'CRITICAL' if ($deviceSpareStateValue != 5);

    $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 || $deviceSpareStateValue != 5 || $deviceSmartAlertValue == 2);
    printf(", %s", $deviceState)      if (defined $debug || $deviceStateValue != 3);
    printf(", %s", $deviceSpareState) if (defined $debug || $deviceSpareStateValue != 5);
    printf(", %s", $deviceSmartAlert) if (defined $debug || $deviceSmartAlertValue == 2);
    printf(")") if (defined $debug || $deviceStateValue != 3 || $deviceSpareStateValue != 5 || $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).";
}

## 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, '$Id: 36e94da23770f65bee20af62ccb5868e84c1c3a3 $');
}

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, '$Id: 36e94da23770f65bee20af62ccb5868e84c1c3a3 $');
    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};
  }
}
