#! /usr/bin/perl -w

package Nagios::MiniPlugin;

use strict;
use Getopt::Long qw(:config no_ignore_case bundling);

our @STATUS_CODES = qw(OK WARNING CRITICAL UNKNOWN DEPENDENT);

require Exporter;
our @ISA = qw(Exporter);
our @EXPORT = (@STATUS_CODES, qw(nagios_exit nagios_die check_messages));
our @EXPORT_OK = qw(%ERRORS);

use constant OK         => 0;
use constant WARNING    => 1;
use constant CRITICAL   => 2;
use constant UNKNOWN    => 3;
use constant DEPENDENT  => 4;

our %ERRORS = (
    'OK'        => OK,
    'WARNING'   => WARNING,
    'CRITICAL'  => CRITICAL,
    'UNKNOWN'   => UNKNOWN,
    'DEPENDENT' => DEPENDENT,
);

our %STATUS_TEXT = reverse %ERRORS;

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
       perfdata => [],
       messages => {
         ok => [],
         warning => [],
         critical => [],
         unknown => [],
       },
       args => [],
       opts => Nagios::MiniPlugin::Getopt->new(%params),
  };
  foreach (qw(shortname usage version url plugin blurb extra
      license timeout)) {
    $self->{$_} = $params{$_};
  }
  bless $self, $class;
}

sub add_arg {
  my $self = shift;
  $self->{opts}->add_arg(@_);
}

sub getopts { 
       my $self = shift;
       $self->{opts}->getopts();
}

sub override_opt { 
   my $self = shift;
   $self->{opts}->override_opt(@_);
}

sub opts {
       my $self = shift;
       return $self->{opts};
}

sub add_message {
  my $self = shift;
  my ($code, @messages) = @_;
  $code = (qw(ok warning critical unknown))[$code] if $code =~ /^\d+$/;
  $code = lc $code;
  push @{$self->{messages}->{$code}}, @messages;
}

sub add_perfdata {
  my ($self, %args) = @_;
  #if ($args{label} =~ /\s/) {
    $args{label} = '\''.$args{label}.'\'';
  #}
  if (! exists $args{places}) {
    $args{places} = 2;
  }
  my $format = '%d';
  if ($args{value} =~ /\./) {
    $format = '%.'.$args{places}.'f';
  }
  my $str = $args{label}.'='.sprintf $format, $args{value};
  if ($args{uom}) {
    $str .= $args{uom};
  }
  if ($args{warning}) {
    $str .= ';'.$args{warning};
  }
  if ($args{critical}) {
    $str .= ';'.$args{critical};
  }
  push @{$self->{perfdata}}, $str;
}

sub clear_messages {
  my $self = shift;
  my $code = shift;
  $code = (qw(ok warning critical unknown))[$code] if $code =~ /^\d+$/;
  $code = lc $code;
  $self->{messages}->{$code} = [];
}

sub check_messages {
  my $self = shift;
  my %args = @_;

  # Add object messages to any passed in as args
  for my $code (qw(critical warning unknown ok)) {
    my $messages = $self->{messages}->{$code} || [];
    if ($args{$code}) {
      unless (ref $args{$code} eq 'ARRAY') {
        if ($code eq 'ok') {
          $args{$code} = [ $args{$code} ];
        }
      }
      push @{$args{$code}}, @$messages;
    } else {
      $args{$code} = $messages;
    }
  }
  my %arg = %args;
  $arg{join} = ' ' unless defined $arg{join};

  # Decide $code
  my $code = OK;
  $code ||= CRITICAL  if @{$arg{critical}};
  $code ||= WARNING   if @{$arg{warning}};
  $code ||= UNKNOWN   if @{$arg{unknown}};
  return $code unless wantarray;

  # Compose message
  my $message = '';
  if ($arg{join_all}) {
      $message = join( $arg{join_all},
          map { @$_ ? join( $arg{'join'}, @$_) : () }
              $arg{critical},
              $arg{warning},
              $arg{unknown},
              $arg{ok} ? (ref $arg{ok} ? $arg{ok} : [ $arg{ok} ]) : []
      );
  }

  else {
      $message ||= join( $arg{'join'}, @{$arg{critical}} )
          if $code == CRITICAL;
      $message ||= join( $arg{'join'}, @{$arg{warning}} )
          if $code == WARNING;
      $message ||= join( $arg{'join'}, @{$arg{unknown}} )
          if $code == UNKNOWN;
      $message ||= ref $arg{ok} ? join( $arg{'join'}, @{$arg{ok}} ) : $arg{ok}
          if $arg{ok};
  }

  return ($code, $message);
}

sub nagios_exit {
  my $self = shift;
  my ($code, $message, $arg) = @_;
  $code = $ERRORS{$code} if defined $code && exists $ERRORS{$code};
  $code = UNKNOWN unless defined $code && exists $STATUS_TEXT{$code};
  $message = '' unless defined $message;
  if (ref $message && ref $message eq 'ARRAY') {
      $message = join(' ', map { chomp; $_ } @$message);
  } else {
      chomp $message;
  }
  my $output = "$STATUS_TEXT{$code}";
  $output .= " - $message" if defined $message && $message ne '';
  if (scalar (@{$self->{perfdata}})) {
    $output .= " | ".join(" ", @{$self->{perfdata}});
  }
  $output .= "\n";
  print $output;
  exit $code;
}

sub set_thresholds {
  my $self = shift;
  my %params = @_;
  $self->{mywarning} = $self->opts->warning || $params{warning} || 0;
  $self->{mycritical} = $self->opts->critical || $params{critical} || 0;
}

sub get_thresholds {
  my $self = shift;
  return ($self->{mywarning}, $self->{mycritical});
}

sub check_thresholds {
  my $self = shift;
  my @params = @_;
  my $level = $ERRORS{OK};
  my $warningrange;
  my $criticalrange;
  my $value;
  if (scalar(@params) > 1) {
    my %params = @params;
    $value = $params{check};
    $warningrange = (defined $params{warning}) ?
        $params{warning} : $self->{mywarning};
    $criticalrange = (defined $params{critical}) ?
        $params{critical} : $self->{mycritical};
  } else {
    $value = $params[0];
    $warningrange = $self->{mywarning};
    $criticalrange = $self->{mycritical};
  }
  if ($warningrange =~ /^(\d+)$/) {
    # warning = 10, warn if > 10 or < 0
    $level = $ERRORS{WARNING}
        if ($value > $1 || $value < 0);
  } elsif ($warningrange =~ /^(\d+):$/) {
    # warning = 10:, warn if < 10
    $level = $ERRORS{WARNING}
        if ($value < $1);
  } elsif ($warningrange =~ /^~:(\d+)$/) {
    # warning = ~:10, warn if > 10
    $level = $ERRORS{WARNING}
        if ($value > $1);
  } elsif ($warningrange =~ /^(\d+):(\d+)$/) {
    # warning = 10:20, warn if < 10 or > 20
    $level = $ERRORS{WARNING}
        if ($value < $1 || $value > $2);
  } elsif ($warningrange =~ /^@(\d+):(\d+)$/) {
    # warning = @10:20, warn if >= 10 and <= 20
    $level = $ERRORS{WARNING}
        if ($value >= $1 && $value <= $2);
  }
  if ($criticalrange =~ /^(\d+)$/) {
    # critical = 10, crit if > 10 or < 0
    $level = $ERRORS{CRITICAL}
        if ($value > $1 || $value < 0);
  } elsif ($criticalrange =~ /^(\d+):$/) {
    # critical = 10:, crit if < 10
    $level = $ERRORS{CRITICAL}
        if ($value < $1);
  } elsif ($criticalrange =~ /^~:(\d+)$/) {
    # critical = ~:10, crit if > 10
    $level = $ERRORS{CRITICAL}
        if ($value > $1);
  } elsif ($criticalrange =~ /^(\d+):(\d+)$/) {
    # critical = 10:20, crit if < 10 or > 20
    $level = $ERRORS{CRITICAL}
        if ($value < $1 || $value > $2);
  } elsif ($criticalrange =~ /^@(\d+):(\d+)$/) {
    # critical = @10:20, crit if >= 10 and <= 20
    $level = $ERRORS{CRITICAL}
        if ($value >= $1 && $value <= $2);
  }
  return $level;
}


package Nagios::MiniPlugin::Getopt;

use strict;
use File::Basename;
use Data::Dumper;
use Getopt::Long qw(:config no_ignore_case bundling);

# Standard defaults
my %DEFAULT = (
  timeout => 15,
  verbose => 0,
  license =>
"This nagios plugin is free software, and comes with ABSOLUTELY NO WARRANTY.
It may be used, redistributed and/or modified under the terms of the GNU
General Public Licence (see http://www.fsf.org/licensing/licenses/gpl.txt).",
);
# Standard arguments
my @ARGS = ({
    spec => 'usage|?',
    help => "-?, --usage\n   Print usage information",
  }, {
    spec => 'help|h',
    help => "-h, --help\n   Print detailed help screen",
  }, {
    spec => 'version|V',
    help => "-V, --version\n   Print version information",
  }, {
    #spec => 'extra-opts:s@',
    #help => "--extra-opts=[<section>[@<config_file>]]\n   Section and/or config_file from which to load extra options (may repeat)",
  }, {
    spec => 'timeout|t=i',
    help => sprintf("-t, --timeout=INTEGER\n   Seconds before plugin times out (default: %s)", $DEFAULT{timeout}),
    default => $DEFAULT{timeout},
  }, {
    spec => 'verbose|v+',
    help => "-v, --verbose\n   Show details for command-line debugging (can repeat up to 3 times)",
    default => $DEFAULT{verbose},
  },
);
# Standard arguments we traditionally display last in the help output
my %DEFER_ARGS = map { $_ => 1 } qw(timeout verbose);

sub _init
{
  my $self = shift;
  my %params = @_;
  # Check params
  my $plugin = basename($ENV{NAGIOS_PLUGIN} || $0);
  #my %attr = validate( @_, {
  my %attr = (
    usage => 1,
    version => 0,
    url => 0,
    plugin => { default => $plugin },
    blurb => 0,
    extra => 0,
    'extra-opts' => 0,
    license => { default => $DEFAULT{license} },
    timeout => { default => $DEFAULT{timeout} },
  );

  # Add attr to private _attr hash (except timeout)
  $self->{timeout} = delete $attr{timeout};
  $self->{_attr} = { %attr };
  foreach (keys %{$self->{_attr}}) {
    if (exists $params{$_}) {
      $self->{_attr}->{$_} = $params{$_};
    } else {
      $self->{_attr}->{$_} = $self->{_attr}->{$_}->{default} 
          if ref ($self->{_attr}->{$_}) eq 'HASH' &&
              exists $self->{_attr}->{$_}->{default};
    }
  }
  # Chomp _attr values
  chomp foreach values %{$self->{_attr}};

  # Setup initial args list
  $self->{_args} = [ grep { exists $_->{spec} } @ARGS ];

  $self
}

sub new
{
  my $class = shift;
  my $self = bless {}, $class;
  $self->_init(@_);
}

sub add_arg {
       my $self = shift;
       my %arg = @_;
       push (@{$self->{_args}}, \%arg);
}

sub getopts { 
  my $self = shift;
  my %commandline = ();
  my @params = map { $_->{spec} } @{$self->{_args}};
  if (! GetOptions(\%commandline, @params)) {
    $self->print_help();
    exit 0;
  } else {
    no strict 'refs';
    do { $self->print_help(); exit 0; } if $commandline{help};
    do { $self->print_version(); exit 0 } if $commandline{version};
    do { $self->print_usage(); exit 0 } if $commandline{usage};
    foreach (map { $_->{spec} =~ /^([\w\-]+)/; $1; } @{$self->{_args}}) {
      my $field = $_;
      *{"$field"} = sub {
        return $self->{opts}->{$field};
      };
    }
    foreach (grep { exists $_->{default} } @{$self->{_args}}) {
      $_->{spec} =~ /^([\w\-]+)/;
      my $spec = $1;
      $self->{opts}->{$spec} = $_->{default};
    }
    foreach (keys %commandline) {
      $self->{opts}->{$_} = $commandline{$_};
    }
  }
}

sub override_opt {
  my $self = shift;
  my $key = shift;
  my $value = shift;
  $self->{opts}->{$key} = $value;
}

sub get {
       my $self = shift;
       my $opt = shift;
       return $self->{opts}->{$opt};
}

sub print_help {
  my $self = shift;
  $self->print_version();
  printf "\n%s\n", $self->{_attr}->{license};
  printf "\n%s\n\n", $self->{_attr}->{blurb};
  $self->print_usage();
  foreach (@{$self->{_args}}) {
    printf " %s\n", $_->{help};
  }
  exit 0;
}

sub print_usage {
  my $self = shift;
  printf $self->{_attr}->{usage}, $self->{_attr}->{plugin};
  print "\n";
}

sub print_version {
  my $self = shift;
  printf "%s %s", $self->{_attr}->{plugin}, $self->{_attr}->{version};
  printf " [%s]", $self->{_attr}->{url} if $self->{_attr}->{url};
  print "\n";
}

sub print_license {
  my $self = shift;
  printf "%s\n", $self->{_attr}->{license};
  print "\n";
}


package Server::Linux;

use strict;

use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

our @ISA = qw(NWC::Device);

sub init {
  my $self = shift;
  $self->{components} = {
      interface_subsystem => undef,
  };
  $self->{serial} = 'unknown';
  $self->{product} = 'unknown';
  $self->{romversion} = 'unknown';
  if (! $self->check_messages()) {
    if ($self->mode =~ /device::interfaces/) {
      $self->analyze_interface_subsystem();
      $self->check_interface_subsystem();
    }
  }
}

sub analyze_interface_subsystem {
  my $self = shift;
  $self->{components}->{interface_subsystem} =
      Server::Linux::Component::InterfaceSubsystem->new();
}

sub check_interface_subsystem {
  my $self = shift;
  $self->{components}->{interface_subsystem}->check();
  $self->{components}->{interface_subsystem}->dump()
      if $self->opts->verbose >= 2;
}

package Server::Linux::Component::InterfaceSubsystem;
our @ISA = qw(Server::Linux);

use strict;
use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    interfaces => [],
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  bless $self, $class;
  $self->init(%params);
  return $self;
}

sub init {
  my $self = shift;
  my %params = @_;
  if ($self->mode =~ /device::interfaces::list/) {
    foreach (glob "/sys/class/net/*") {
      my $name = $_;
      $name =~ s/.*\///g;
      my $tmpif = {
        ifDescr => $name,
      };
      push(@{$self->{interfaces}},
        Server::Linux::Component::InterfaceSubsystem::Interface->new(%{$tmpif}));
    }
  } else {
    foreach (glob "/sys/class/net/*") {
      my $name = $_;
      $name =~ s/.*\///g;
      if ($self->opts->name) {
        if ($self->opts->regexp) {
          my $pattern = $self->opts->name;
          if ($name !~ /$pattern/i) {
            next;
          }
        } elsif (lc $name ne lc $self->opts->name) {
          next;
        }
      }
      my $tmpif = {
        ifDescr => $name,
        ifSpeed => (-f "/sys/class/net/$name/speed" ? do { local (@ARGV, $/) = "/sys/class/net/$name/speed"; my $x = <>; close ARGV; $x} * 1024*1024 : 1024*1024*1024),
        ifInOctets => do { local (@ARGV, $/) = "/sys/class/net/$name/statistics/rx_bytes"; my $x = <>; close ARGV; $x},
        ifInDiscards => do { local (@ARGV, $/) = "/sys/class/net/$name/statistics/rx_dropped"; my $x = <>; close ARGV; $x},
        ifInErrors => do { local (@ARGV, $/) = "/sys/class/net/$name/statistics/rx_errors"; my $x = <>; close ARGV; $x},
        ifOutOctets => do { local (@ARGV, $/) = "/sys/class/net/$name/statistics/tx_bytes"; my $x = <>; close ARGV; $x},
        ifOutDiscards => do { local (@ARGV, $/) = "/sys/class/net/$name/statistics/tx_dropped"; my $x = <>; close ARGV; $x},
        ifOutErrors => do { local (@ARGV, $/) = "/sys/class/net/$name/statistics/tx_errors"; my $x = <>; close ARGV; $x},
      };
      foreach (keys %{$tmpif}) {
        chomp($tmpif->{$_});
      }
      push(@{$self->{interfaces}},
        Server::Linux::Component::InterfaceSubsystem::Interface->new(%{$tmpif}));
    }
  }
}

sub check {
  my $self = shift;
  my $errorfound = 0;
  $self->add_info('checking interfaces');
  $self->blacklist('ff', '');
  if (scalar(@{$self->{interfaces}}) == 0) {
    $self->add_message(UNKNOWN, 'no interfaces');
    return;
  }
  if ($self->mode =~ /device::interfaces::list/) {
    foreach (sort {$a->{ifDescr} cmp $b->{ifDescr}} @{$self->{interfaces}}) {
      $_->list();
    }
  } else {
    if (scalar (@{$self->{interfaces}}) == 0) {
    } else {
      foreach (@{$self->{interfaces}}) {
        $_->check();
      }
    }
  }
}

sub dump {
  my $self = shift;
  foreach (@{$self->{interfaces}}) {
    $_->dump();
  }
}


package Server::Linux::Component::InterfaceSubsystem::Interface;
our @ISA = qw(Server::Linux::Component::InterfaceSubsystem);

use strict;
use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    ifDescr => $params{ifDescr},
    ifSpeed => $params{ifSpeed},
    ifInOctets => $params{ifInOctets},
    ifInDiscards => $params{ifInDiscards},
    ifInErrors => $params{ifInErrors},
    ifOutOctets => $params{ifOutOctets},
    ifOutDiscards => $params{ifOutDiscards},
    ifOutErrors => $params{ifOutErrors},
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  foreach my $key (keys %params) {
    $self->{$key} = 0 if ! defined $params{$key};
  }
  bless $self, $class;
  $self->init();
  return $self;
}

sub init {
  my $self = shift;
  if ($self->mode =~ /device::interfaces::traffic/) {
    $self->valdiff({name => $self->{ifDescr}}, qw(ifInOctets ifInDiscards ifInErrors ifOutOctets ifOutDiscards ifOutErrors));
  } elsif ($self->mode =~ /device::interfaces::usage/) {
    $self->valdiff({name => $self->{ifDescr}}, qw(ifInOctets ifOutOctets));
    if ($self->{ifSpeed} == 0) {
      # vlan graffl
      $self->{inputUtilization} = 0;
      $self->{outputUtilization} = 0;
    } else {
      $self->{inputUtilization} = $self->{delta_ifInOctets} * 8 * 100 /
          ($self->{delta_timestamp} * $self->{ifSpeed});
      $self->{outputUtilization} = $self->{delta_ifOutOctets} * 8 * 100 /
          ($self->{delta_timestamp} * $self->{ifSpeed});
    }
    $self->{inputRate} = $self->{delta_ifInOctets} / $self->{delta_timestamp};
    $self->{outputRate} = $self->{delta_ifOutOctets} / $self->{delta_timestamp};
    my $factor = 1/8; # default Bits
    if ($self->opts->units) {
      if ($self->opts->units eq "GB") {
        $factor = 1024 * 1024 * 1024;
      } elsif ($self->opts->units eq "MB") {
        $factor = 1024 * 1024;
      } elsif ($self->opts->units eq "KB") {
        $factor = 1024;
      } elsif ($self->opts->units eq "GBi") {
        $factor = 1024 * 1024 * 1024 / 8;
      } elsif ($self->opts->units eq "MBi") {
        $factor = 1024 * 1024 / 8;
      } elsif ($self->opts->units eq "KBi") {
        $factor = 1024 / 8;
      } elsif ($self->opts->units eq "B") {
        $factor = 1;
      } elsif ($self->opts->units eq "Bit") {
        $factor = 1/8;
      }
    }
    $self->{inputRate} /= $factor;
    $self->{outputRate} /= $factor;
  } elsif ($self->mode =~ /device::interfaces::errors/) {
    $self->valdiff({name => $self->{ifDescr}}, qw(ifInErrors ifOutErrors ifInDiscards ifOutDiscards));
    $self->{inputErrorRate} = $self->{delta_ifInErrors} 
        / $self->{delta_timestamp};
    $self->{outputErrorRate} = $self->{delta_ifOutErrors} 
        / $self->{delta_timestamp};
    $self->{inputDiscardRate} = $self->{delta_ifInDiscards} 
        / $self->{delta_timestamp};
    $self->{outputDiscardRate} = $self->{delta_ifOutDiscards} 
        / $self->{delta_timestamp};
    $self->{inputRate} = ($self->{delta_ifInErrors} + $self->{delta_ifInDiscards}) 
        / $self->{delta_timestamp};
    $self->{outputRate} = ($self->{delta_ifOutErrors} + $self->{delta_ifOutDiscards}) 
        / $self->{delta_timestamp};
  } elsif ($self->mode =~ /device::interfaces::operstatus/) {
  }
  return $self;
}

sub check {
  my $self = shift;
  $self->blacklist('if', $self->{ifDescr});
  if ($self->mode =~ /device::interfaces::traffic/) {
  } elsif ($self->mode =~ /device::interfaces::usage/) {
    my $info = sprintf 'interface %s usage is in:%.2f%% (%s) out:%.2f%% (%s)',
        $self->{ifDescr}, 
        $self->{inputUtilization}, 
        sprintf("%.2f%s/s", $self->{inputRate},
            ($self->opts->units ? $self->opts->units : 'Bits')),
        $self->{outputUtilization},
        sprintf("%.2f%s/s", $self->{outputRate},
            ($self->opts->units ? $self->opts->units : 'Bits'));
    $self->add_info($info);
    $self->set_thresholds(warning => 80, critical => 90);
    my $in = $self->check_thresholds($self->{inputUtilization});
    my $out = $self->check_thresholds($self->{outputUtilization});
    my $level = ($in > $out) ? $in : ($out > $in) ? $out : $in;
    $self->add_message($level, $info);
    $self->add_perfdata(
        label => $self->{ifDescr}.'_usage_in',
        value => $self->{inputUtilization},
        uom => '%',
        warning => $self->{warning},
        critical => $self->{critical},
    );
    $self->add_perfdata(
        label => $self->{ifDescr}.'_usage_out',
        value => $self->{outputUtilization},
        uom => '%',
        warning => $self->{warning},
        critical => $self->{critical},
    );
    $self->add_perfdata(
        label => $self->{ifDescr}.'_traffic_in',
        value => $self->{inputRate},
        uom => $self->opts->units,
    );
    $self->add_perfdata(
        label => $self->{ifDescr}.'_traffic_out',
        value => $self->{outputRate},
        uom => $self->opts->units,
    );
  } elsif ($self->mode =~ /device::interfaces::errors/) {
    my $info = sprintf 'interface %s errors in:%.2f/s out:%.2f/s '.
        'discards in:%.2f/s out:%.2f/s',
        $self->{ifDescr},
        $self->{inputErrorRate} , $self->{outputErrorRate},
        $self->{inputDiscardRate} , $self->{outputDiscardRate};
    $self->add_info($info);
    $self->set_thresholds(warning => 1, critical => 10);
    my $in = $self->check_thresholds($self->{inputRate});
    my $out = $self->check_thresholds($self->{outputRate});
    my $level = ($in > $out) ? $in : ($out > $in) ? $out : $in;
    $self->add_message($level, $info);
    $self->add_perfdata(
        label => $self->{ifDescr}.'_errors_in',
        value => $self->{inputErrorRate},
        warning => $self->{warning},
        critical => $self->{critical},
    );
    $self->add_perfdata(
        label => $self->{ifDescr}.'_errors_out',
        value => $self->{outputErrorRate},
        warning => $self->{warning},
        critical => $self->{critical},
    );
    $self->add_perfdata(
        label => $self->{ifDescr}.'_discards_in',
        value => $self->{inputDiscardRate},
        warning => $self->{warning},
        critical => $self->{critical},
    );
    $self->add_perfdata(
        label => $self->{ifDescr}.'_discards_out',
        value => $self->{outputDiscardRate},
        warning => $self->{warning},
        critical => $self->{critical},
    );
  }
}

sub list {
  my $self = shift;
  printf "%s\n", $self->{ifDescr};
}

sub dump {
  my $self = shift;
  printf "[IF_%s]\n", $self->{ifDescr};
  foreach (qw(ifDescr ifSpeed ifInOctets ifInDiscards ifInErrors ifOutOctets ifOutDiscards ifOutErrors)) {
    printf "%s: %s\n", $_, defined $self->{$_} ? $self->{$_} : 'undefined';
  }
#  printf "info: %s\n", $self->{info};
  printf "\n";
}

package NWC::CiscoIOS::Component::CpuSubsystem;
our @ISA = qw(NWC::CiscoIOS);

use strict;
use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    cpus => [],
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  bless $self, $class;
  $self->init(%params);
  return $self;
}

sub init {
  my $self = shift;
  my %params = @_;
  my $type = 0;
  foreach ($self->get_snmp_table_objects(
      'CISCO-PROCESS-MIB', 'cpmCPUTotalTable')) {
    $_->{cpmCPUTotalIndex} ||= $type++;
    push(@{$self->{cpus}},
        NWC::CiscoIOS::Component::CpuSubsystem::Cpu->new(%{$_}));
  }
  if (scalar(@{$self->{cpus}}) == 0) {
    # maybe too old. i fake a cpu. be careful. this is a really bad hack
    my $response = $self->get_request(
        -varbindlist => [
            $NWC::Device::mibs_and_oids->{'OLD-CISCO-CPU-MIB'}->{avgBusy1},
            $NWC::Device::mibs_and_oids->{'OLD-CISCO-CPU-MIB'}->{avgBusy5},
            $NWC::Device::mibs_and_oids->{'OLD-CISCO-CPU-MIB'}->{busyPer},
        ]
    );
    if (exists $response->{$NWC::Device::mibs_and_oids->{'OLD-CISCO-CPU-MIB'}->{avgBusy1}}) {
      push(@{$self->{cpus}},
          NWC::CiscoIOS::Component::CpuSubsystem::Cpu->new(
              cpmCPUTotalPhysicalIndex => 0, #fake
              cpmCPUTotalIndex => 0, #fake
              cpmCPUTotal5sec => 0, #fake
              cpmCPUTotal5secRev => 0, #fake
              cpmCPUTotal1min => $response->{$NWC::Device::mibs_and_oids->{'OLD-CISCO-CPU-MIB'}->{avgBusy1}},
              cpmCPUTotal1minRev => $response->{$NWC::Device::mibs_and_oids->{'OLD-CISCO-CPU-MIB'}->{avgBusy1}},
              cpmCPUTotal5min => $response->{$NWC::Device::mibs_and_oids->{'OLD-CISCO-CPU-MIB'}->{avgBusy5}},
              cpmCPUTotal5minRev => $response->{$NWC::Device::mibs_and_oids->{'OLD-CISCO-CPU-MIB'}->{avgBusy5}},
              cpmCPUMonInterval => 0, #fake
              cpmCPUTotalMonIntervalValue => 0, #fake
              cpmCPUInterruptMonIntervalValue => 0, #fake
      ));
    }
  }
}

sub check {
  my $self = shift;
  my $errorfound = 0;
  $self->add_info('checking cpus');
  $self->blacklist('ff', '');
  if (scalar (@{$self->{cpus}}) == 0) {
  } else {
    foreach (@{$self->{cpus}}) {
      $_->check();
    }
  }
}


sub dump {
  my $self = shift;
  foreach (@{$self->{cpus}}) {
    $_->dump();
  }
}


package NWC::CiscoIOS::Component::CpuSubsystem::Cpu;
our @ISA = qw(NWC::CiscoIOS::Component::CpuSubsystem);

use strict;
use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  foreach my $param (qw(cpmCPUTotalIndex cpmCPUTotalPhysicalIndex
      cpmCPUTotal5sec cpmCPUTotal1min cpmCPUTotal5min
      cpmCPUTotal5secRev cpmCPUTotal1minRev cpmCPUTotal5minRev
      cpmCPUMonInterval cpmCPUTotalMonIntervalValue
      cpmCPUInterruptMonIntervalValue)) {
    if (exists $params{$param}) {
      $self->{$param} = $params{$param};
    }
  }
  bless $self, $class;
  if (exists $params{cpmCPUTotal5minRev}) {
    $self->{usage} = $params{cpmCPUTotal5minRev};
  } else {
    $self->{usage} = $params{cpmCPUTotal5min};
  }
  if ($self->{cpmCPUTotalPhysicalIndex}) {
    my $entPhysicalName = '1.3.6.1.2.1.47.1.1.1.1.7';
    $self->{entPhysicalName} = $self->get_request(
        -varbindlist => [$entPhysicalName.'.'.$self->{cpmCPUTotalPhysicalIndex}]
    );
    $self->{entPhysicalName} = $self->{entPhysicalName}->{$entPhysicalName.'.'.$self->{cpmCPUTotalPhysicalIndex}};
  } else {
    $self->{entPhysicalName} = $self->{cpmCPUTotalIndex};
  }
  return $self;
}

sub check {
  my $self = shift;
  $self->blacklist('c', $self->{cpmCPUTotalPhysicalIndex});
  my $info = sprintf 'cpu %s usage (5 min avg.) is %.2f%%',
      $self->{entPhysicalName}, $self->{usage};
  $self->add_info($info);
  $self->set_thresholds(warning => 80, critical => 90);
  $self->add_message($self->check_thresholds($self->{usage}), $info);
  $self->add_perfdata(
      label => 'cpu_'.$self->{entPhysicalName}.'_usage',
      value => $self->{usage},
      uom => '%',
      warning => $self->{warning},
      critical => $self->{critical},
  );
}

sub dump {
  my $self = shift;
  printf "[CPU_%s]\n", $self->{cpmCPUTotalPhysicalIndex};
  foreach (qw(cpmCPUTotalIndex cpmCPUTotalPhysicalIndex cpmCPUTotal5sec cpmCPUTotal1min cpmCPUTotal5min cpmCPUTotal5secRev cpmCPUTotal1minRev cpmCPUTotal5minRev cpmCPUMonInterval cpmCPUTotalMonIntervalValue cpmCPUInterruptMonIntervalValue)) {
    if (exists $self->{$_}) {
      printf "%s: %s\n", $_, $self->{$_};
    }
  }
  printf "info: %s\n", $self->{info};
  printf "\n";
}

package NWC::CiscoIOS::Component::MemSubsystem;
our @ISA = qw(NWC::CiscoIOS);

use strict;
use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    runtime => $params{runtime},
    rawdata => $params{rawdata},
    mems => [],
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  bless $self, $class;
  $self->init(%params);
  return $self;
}

sub init {
  my $self = shift;
  my %params = @_;
  my $snmpwalk = $params{rawdata};
  my $ignore_redundancy = $params{ignore_redundancy};
  my $type = 0;
  foreach ($self->get_snmp_table_objects(
     'CISCO-MEMORY-POOL-MIB', 'ciscoMemoryPoolTable')) {
    $_->{ciscoMemoryPoolType} ||= $type++;
    push(@{$self->{mems}},
        NWC::CiscoIOS::Component::MemSubsystem::Mem->new(%{$_}));
  }
}

sub check {
  my $self = shift;
  my $errorfound = 0;
  $self->add_info('checking mems');
  $self->blacklist('ff', '');
  if (scalar (@{$self->{mems}}) == 0) {
  } else {
    foreach (@{$self->{mems}}) {
      $_->check();
    }
  }
}


sub dump {
  my $self = shift;
  foreach (@{$self->{mems}}) {
    $_->dump();
  }
}


package NWC::CiscoIOS::Component::MemSubsystem::Mem;
our @ISA = qw(NWC::CiscoIOS::Component::MemSubsystem);

use strict;
use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  foreach my $param (qw(ciscoMemoryPoolTable ciscoMemoryPoolEntry
      ciscoMemoryPoolType ciscoMemoryPoolName ciscoMemoryPoolAlternate
      ciscoMemoryPoolValid ciscoMemoryPoolUsed ciscoMemoryPoolFree
      ciscoMemoryPoolLargestFree)) {
    $self->{$param} = $params{$param};
  }
  bless $self, $class;
  $self->{usage} = 100 * $params{ciscoMemoryPoolUsed} /
      ($params{ciscoMemoryPoolFree} + $params{ciscoMemoryPoolUsed});
  return $self;
}

sub check {
  my $self = shift;
  $self->blacklist('m', $self->{ciscoMemoryPoolType});
  my $info = sprintf 'mempool %s usage is %.2f%%',
      $self->{ciscoMemoryPoolName}, $self->{usage};
  $self->add_info($info);
  $self->set_thresholds(warning => 80, critical => 90);
  $self->add_message($self->check_thresholds($self->{usage}), $info);

  $self->add_perfdata(
      label => $self->{ciscoMemoryPoolName}.'_usage',
      value => $self->{usage},
      uom => '%',
      warning => $self->{warning},
      critical => $self->{critical}
  );
}

sub dump {
  my $self = shift;
  printf "[MEMPOOL_%s]\n", $self->{ciscoMemoryPoolType};
  foreach (qw(ciscoMemoryPoolType ciscoMemoryPoolName ciscoMemoryPoolAlternate ciscoMemoryPoolValid ciscoMemoryPoolUsed ciscoMemoryPoolFree ciscoMemoryPoolLargestFree)) {
    printf "%s: %s\n", $_, $self->{$_};
  }
  printf "info: %s\n", $self->{info};
  printf "\n";
}

package NWC::CiscoIOS::Component::EnvironmentalSubsystem;
our @ISA = qw(NWC::CiscoIOS);

use strict;
use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    runtime => $params{runtime},
    rawdata => $params{rawdata},
    method => $params{method},
    condition => $params{condition},
    status => $params{status},
    fan_subsystem => undef,
    temperature_subsystem => undef,
    powersupply_subsystem => undef,
    voltage_subsystem => undef,
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  bless $self, $class;
  $self->init(%params);
  return $self;
}

sub init {
  my $self = shift;
  my %params = @_;
  #
  # 1.3.6.1.4.1.9.9.13.1.1.0 ciscoEnvMonPresent (irgendein typ of envmon)
  # 
  $self->{fan_subsystem} =
      NWC::CiscoIOS::Component::FanSubsystem->new(%params);
  $self->{temperature_subsystem} =
      NWC::CiscoIOS::Component::TemperatureSubsystem->new(%params);
  $self->{powersupply_subsystem} = 
      NWC::CiscoIOS::Component::SupplySubsystem->new(%params);
  $self->{voltage_subsystem} =
      NWC::CiscoIOS::Component::VoltageSubsystem->new(%params);
}

sub check {
  my $self = shift;
  my $errorfound = 0;
  $self->{fan_subsystem}->check();
  $self->{temperature_subsystem}->check();
  $self->{voltage_subsystem}->check();
  $self->{powersupply_subsystem}->check();
  if (! $self->check_messages()) {
    $self->add_message(OK, "environmental hardware working fine");
  }
}

sub dump {
  my $self = shift;
  $self->{fan_subsystem}->dump();
  $self->{temperature_subsystem}->dump();
  $self->{voltage_subsystem}->dump();
  $self->{powersupply_subsystem}->dump();
}


package NWC::CiscoIOS::Component::TemperatureSubsystem;
our @ISA = qw(NWC::CiscoIOS::Component::EnvironmentalSubsystem);

use strict;
use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    temperatures => [],
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  bless $self, $class;
  $self->init(%params);
  return $self;
}

sub init {
  my $self = shift;
  my $tempcnt = 0;
  foreach ($self->get_snmp_table_objects(
      'CISCO-ENVMON-MIB', 'ciscoEnvMonTemperatureStatusTable')) {
    $_->{ciscoEnvMonTemperatureStatusIndex} = $tempcnt++ if (! exists $_->{ciscoEnvMonTemperatureStatusIndex});
    push(@{$self->{temperatures}},
        NWC::CiscoIOS::Component::TemperatureSubsystem::Temperature->new(%{$_}));
  }
}

sub check {
  my $self = shift;
  my $errorfound = 0;
  $self->add_info('checking temperatures');
  $self->blacklist('t', '');
  if (scalar (@{$self->{temperatures}}) == 0) {
  } else {
    foreach (@{$self->{temperatures}}) {
      $_->check();
    }
  }
}


sub dump {
  my $self = shift;
  foreach (@{$self->{temperatures}}) {
    $_->dump();
  }
}


package NWC::CiscoIOS::Component::TemperatureSubsystem::Temperature;
our @ISA = qw(NWC::CiscoIOS::Component::TemperatureSubsystem);

use strict;
use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  foreach my $param (qw(ciscoEnvMonTemperatureStatusIndex
      ciscoEnvMonTemperatureStatusDescr ciscoEnvMonTemperatureStatusValue
      ciscoEnvMonTemperatureThreshold ciscoEnvMonTemperatureLastShutdown
      ciscoEnvMonTemperatureState)) {
    $self->{$param} = $params{$param};
  }
  $self->{ciscoEnvMonTemperatureStatusIndex} ||= 0;
  $self->{ciscoEnvMonTemperatureLastShutdown} ||= 0;
  if ($self->{ciscoEnvMonTemperatureStatusValue}) {
    bless $self, $class;
  } else {
    bless $self, $class.'::Simple';
  }
  return $self;
}

sub check {
  my $self = shift;
  $self->blacklist('t', $self->{ciscoEnvMonTemperatureStatusIndex});
  if ($self->{ciscoEnvMonTemperatureStatusValue} >
      $self->{ciscoEnvMonTemperatureThreshold}) {
    $self->add_info(sprintf 'temperature %d %s is too high (%d of %d max = %s)',
        $self->{ciscoEnvMonTemperatureStatusIndex},
        $self->{ciscoEnvMonTemperatureStatusDescr},
        $self->{ciscoEnvMonTemperatureStatusValue},
        $self->{ciscoEnvMonTemperatureThreshold},
        $self->{ciscoEnvMonTemperatureState});
    if ($self->{ciscoEnvMonTemperatureState} eq 'warning') {
      $self->add_message(WARNING, $self->{info});
    } elsif ($self->{ciscoEnvMonTemperatureState} eq 'critical') {
      $self->add_message(CRITICAL, $self->{info});
    }
  } else {
    $self->add_info(sprintf 'temperature %d %s is %d (of %d max = normal)',
        $self->{ciscoEnvMonTemperatureStatusIndex},
        $self->{ciscoEnvMonTemperatureStatusDescr},
        $self->{ciscoEnvMonTemperatureStatusValue},
        $self->{ciscoEnvMonTemperatureThreshold},
        $self->{ciscoEnvMonTemperatureState});
  }
  $self->add_perfdata(
      label => sprintf('temp_%s', $self->{ciscoEnvMonTemperatureStatusIndex}),
      value => $self->{ciscoEnvMonTemperatureStatusValue},
      warning => $self->{ciscoEnvMonTemperatureThreshold},
      critical => undef,
  );
}

sub dump {
  my $self = shift;
  printf "[TEMP_%s]\n", $self->{ciscoEnvMonTemperatureStatusIndex};
  foreach (qw(ciscoEnvMonTemperatureStatusIndex ciscoEnvMonTemperatureStatusDescr ciscoEnvMonTemperatureStatusValue ciscoEnvMonTemperatureThreshold ciscoEnvMonTemperatureLastShutdown ciscoEnvMonTemperatureState)) {
    printf "%s: %s\n", $_, $self->{$_};
  }
  printf "info: %s\n", $self->{info};
  printf "\n";
}

package NWC::CiscoIOS::Component::TemperatureSubsystem::Temperature::Simple;
our @ISA = qw(NWC::CiscoIOS::Component::TemperatureSubsystem::Temperature);

use strict;
use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    runtime => $params{runtime},
    ciscoEnvMonTemperatureStatusIndex => $params{ciscoEnvMonTemperatureStatusIndex} || 0,
    ciscoEnvMonTemperatureStatusDescr => $params{ciscoEnvMonTemperatureStatusDescr},
    ciscoEnvMonTemperatureState => $params{ciscoEnvMonTemperatureState},
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  bless $self, $class;
  return $self;
}

sub check {
  my $self = shift;
  $self->blacklist('t', $self->{ciscoEnvMonTemperatureStatusIndex});
  $self->add_info(sprintf 'temperature %d %s is %s',
      $self->{ciscoEnvMonTemperatureStatusIndex},
      $self->{ciscoEnvMonTemperatureStatusDescr},
      $self->{ciscoEnvMonTemperatureState});
  if ($self->{ciscoEnvMonTemperatureState} ne 'normal') {
    if ($self->{ciscoEnvMonTemperatureState} eq 'warning') {
      $self->add_message(WARNING, $self->{info});
    } elsif ($self->{ciscoEnvMonTemperatureState} eq 'critical') {
      $self->add_message(CRITICAL, $self->{info});
    }
  } else {
  }
}

sub dump {
  my $self = shift;
  printf "[TEMP_%s]\n", $self->{ciscoEnvMonTemperatureStatusIndex};
  foreach (qw(ciscoEnvMonTemperatureStatusIndex ciscoEnvMonTemperatureStatusDescr ciscoEnvMonTemperatureState)) {
    printf "%s: %s\n", $_, $self->{$_};
  }
  printf "info: %s\n", $self->{info};
  printf "\n";
}


package NWC::CiscoIOS::Component::SupplySubsystem;
our @ISA = qw(NWC::CiscoIOS::Component::EnvironmentalSubsystem);

use strict;
use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    supplies => [],
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  bless $self, $class;
  $self->init(%params);
  return $self;
}

sub init {
  my $self = shift;
  foreach ($self->get_snmp_table_objects(
      'CISCO-ENVMON-MIB', 'ciscoEnvMonSupplyStatusTable')) {
    push(@{$self->{supplies}},
        NWC::CiscoIOS::Component::SupplySubsystem::Supply->new(%{$_}));
  }
}

sub check {
  my $self = shift;
  my $errorfound = 0;
  $self->add_info('checking supplies');
  $self->blacklist('ps', '');
  if (scalar (@{$self->{supplies}}) == 0) {
  } else {
    foreach (@{$self->{supplies}}) {
      $_->check();
    }
  }
}


sub dump {
  my $self = shift;
  foreach (@{$self->{supplies}}) {
    $_->dump();
  }
}


package NWC::CiscoIOS::Component::SupplySubsystem::Supply;
our @ISA = qw(NWC::CiscoIOS::Component::SupplySubsystem);

use strict;
use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  foreach my $param (qw(ciscoEnvMonSupplyStatusIndex
      ciscoEnvMonSupplyStatusDescr ciscoEnvMonSupplyState
      ciscoEnvMonSupplySource)) {
    $self->{$param} = $params{$param};
  }
  $self->{ciscoEnvMonSupplyStatusIndex} ||= 0;
  bless $self, $class;
  return $self;
}

sub check {
  my $self = shift;
  $self->blacklist('f', $self->{ciscoEnvMonSupplyStatusIndex});
  $self->add_info(sprintf 'powersupply %d (%s) is %s',
      $self->{ciscoEnvMonSupplyStatusIndex},
      $self->{ciscoEnvMonSupplyStatusDescr},
      $self->{ciscoEnvMonSupplyState});
  if ($self->{ciscoEnvMonSupplyState} eq 'notPresent') {
  } elsif ($self->{ciscoEnvMonSupplyState} ne 'normal') {
    $self->add_message(CRITICAL, $self->{info});
  }
}

sub dump {
  my $self = shift;
  printf "[PS_%s]\n", $self->{ciscoEnvMonSupplyStatusIndex};
  foreach (qw(ciscoEnvMonSupplyStatusIndex ciscoEnvMonSupplyStatusDescr ciscoEnvMonSupplyState ciscoEnvMonSupplySource)) {
    printf "%s: %s\n", $_, $self->{$_};
  }
  printf "info: %s\n", $self->{info};
  printf "\n";
}

package NWC::CiscoIOS::Component::VoltageSubsystem;
our @ISA = qw(NWC::CiscoIOS::Component::EnvironmentalSubsystem);

use strict;
use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    voltages => [],
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  bless $self, $class;
  $self->init(%params);
  return $self;
}

sub init {
  my $self = shift;
  my $index = 0;
  foreach ($self->get_snmp_table_objects(
      'CISCO-ENVMON-MIB', 'ciscoEnvMonVoltageStatusTable')) {
    $_->{ciscoEnvMonVoltageStatusIndex} ||= $index++;
    push(@{$self->{voltages}},
        NWC::CiscoIOS::Component::VoltageSubsystem::Voltage->new(%{$_}));
  }
}

sub check {
  my $self = shift;
  my $errorfound = 0;
  $self->add_info('checking voltages');
  $self->blacklist('ff', '');
  if (scalar (@{$self->{voltages}}) == 0) {
  } else {
    foreach (@{$self->{voltages}}) {
      $_->check();
    }
  }
}


sub dump {
  my $self = shift;
  foreach (@{$self->{voltages}}) {
    $_->dump();
  }
}


package NWC::CiscoIOS::Component::VoltageSubsystem::Voltage;
our @ISA = qw(NWC::CiscoIOS::Component::VoltageSubsystem);

use strict;
use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  foreach my $param (qw(ciscoEnvMonVoltageStatusTable
      ciscoEnvMonVoltageStatusEntry ciscoEnvMonVoltageStatusIndex
      ciscoEnvMonVoltageStatusDescr ciscoEnvMonVoltageStatusValue
      ciscoEnvMonVoltageThresholdLow ciscoEnvMonVoltageThresholdHigh
      ciscoEnvMonVoltageLastShutdown ciscoEnvMonVoltageState)) {
    $self->{$param} = $params{$param};
  }
  bless $self, $class;
  return $self;
}

sub check {
  my $self = shift;
  $self->blacklist('v', $self->{ciscoEnvMonVoltageStatusIndex});
  $self->add_info(sprintf 'voltage %d (%s) is %s',
      $self->{ciscoEnvMonVoltageStatusIndex},
      $self->{ciscoEnvMonVoltageStatusDescr},
      $self->{ciscoEnvMonVoltageState});
  if ($self->{ciscoEnvMonVoltageState} eq 'notPresent') {
  } elsif ($self->{ciscoEnvMonVoltageState} ne 'normal') {
    $self->add_message(CRITICAL, $self->{info});
  }
  $self->add_perfdata(
      label => sprintf('mvolt_%s', $self->{ciscoEnvMonVoltageStatusIndex}),
      value => $self->{ciscoEnvMonVoltageStatusValue},
      warning => $self->{ciscoEnvMonVoltageThresholdLow},
      critical => $self->{ciscoEnvMonVoltageThresholdHigh},
  );
}

sub dump {
  my $self = shift;
  printf "[VOLTAGE_%s]\n", $self->{ciscoEnvMonVoltageStatusIndex};
  foreach (qw(ciscoEnvMonVoltageStatusTable ciscoEnvMonVoltageStatusEntry ciscoEnvMonVoltageStatusIndex ciscoEnvMonVoltageStatusDescr ciscoEnvMonVoltageStatusValue ciscoEnvMonVoltageThresholdLow ciscoEnvMonVoltageThresholdHigh ciscoEnvMonVoltageLastShutdown ciscoEnvMonVoltageState)) {
    printf "%s: %s\n", $_, $self->{$_};
  }
  printf "info: %s\n", $self->{info};
  printf "\n";
}

package NWC::CiscoIOS::Component::FanSubsystem;
our @ISA = qw(NWC::CiscoIOS::Component::EnvironmentalSubsystem);

use strict;
use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    fans => [],
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  bless $self, $class;
  $self->init(%params);
  return $self;
}

sub init {
  my $self = shift;
  foreach ($self->get_snmp_table_objects(
      'CISCO-ENVMON-MIB', 'ciscoEnvMonFanStatusTable')) {
    push(@{$self->{fans}},
        NWC::CiscoIOS::Component::FanSubsystem::Fan->new(%{$_}));
  }
}

sub check {
  my $self = shift;
  my $errorfound = 0;
  $self->add_info('checking fans');
  $self->blacklist('ff', '');
  if (scalar (@{$self->{fans}}) == 0) {
  } else {
    foreach (@{$self->{fans}}) {
      $_->check();
    }
  }
}


sub dump {
  my $self = shift;
  foreach (@{$self->{fans}}) {
    $_->dump();
  }
}


package NWC::CiscoIOS::Component::FanSubsystem::Fan;
our @ISA = qw(NWC::CiscoIOS::Component::FanSubsystem);

use strict;
use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  foreach my $param (qw(ciscoEnvMonFanStatusIndex
      ciscoEnvMonFanStatusDescr ciscoEnvMonFanState)) {
    $self->{$param} = $params{$param};
  }
  $self->{ciscoEnvMonFanStatusIndex} ||= 0;
  bless $self, $class;
  return $self;
}

sub check {
  my $self = shift;
  $self->blacklist('f', $self->{ciscoEnvMonFanStatusIndex});
  $self->add_info(sprintf 'fan %d (%s) is %s',
      $self->{ciscoEnvMonFanStatusIndex},
      $self->{ciscoEnvMonFanStatusDescr},
      $self->{ciscoEnvMonFanState});
  if ($self->{ciscoEnvMonFanState} eq 'notPresent') {
  } elsif ($self->{ciscoEnvMonFanState} ne 'normal') {
    $self->add_message(CRITICAL, $self->{info});
  }
}

sub dump {
  my $self = shift;
  printf "[FAN_%s]\n", $self->{ciscoEnvMonFanStatusIndex};
  foreach (qw(ciscoEnvMonFanStatusIndex ciscoEnvMonFanStatusDescr 
      ciscoEnvMonFanState)) {
    printf "%s: %s\n", $_, $self->{$_};
  }
  printf "info: %s\n", $self->{info};
  printf "\n";
}

package NWC::CiscoIOS;

use strict;

use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

our @ISA = qw(NWC::Cisco);

sub init {
  my $self = shift;
  $self->{components} = {
      powersupply_subsystem => undef,
      fan_subsystem => undef,
      temperature_subsystem => undef,
      cpu_subsystem => undef,
      memory_subsystem => undef,
      disk_subsystem => undef,
      environmental_subsystem => undef,
  };
  $self->{serial} = 'unknown';
  $self->{product} = 'unknown';
  $self->{romversion} = 'unknown';
  # serial is 1.3.6.1.2.1.47.1.1.1.1.11.1
  #$self->collect();
  if (! $self->check_messages()) {
    ##$self->set_serial();
    if ($self->mode =~ /device::hardware::health/) {
      $self->analyze_environmental_subsystem();
      #$self->auto_blacklist();
      $self->check_environmental_subsystem();
    } elsif ($self->mode =~ /device::hardware::load/) {
      $self->analyze_cpu_subsystem();
      #$self->auto_blacklist();
      $self->check_cpu_subsystem();
    } elsif ($self->mode =~ /device::hardware::memory/) {
      $self->analyze_mem_subsystem();
      #$self->auto_blacklist();
      $self->check_mem_subsystem();
    } elsif ($self->mode =~ /device::interfaces/) {
      $self->analyze_interface_subsystem();
      $self->check_interface_subsystem();
    } elsif ($self->mode =~ /device::shinken::interface/) {
      $self->analyze_interface_subsystem();
      $self->shinken_interface_subsystem();
    } elsif ($self->mode =~ /device::hsrp/) {
      $self->analyze_hsrp_subsystem();
      $self->check_hsrp_subsystem();
    }
  }
}

sub analyze_hsrp_subsystem {
  my $self = shift;
  $self->{components}->{hsrp} =
      NWC::HSRP::Component::HSRPSubsystem->new();
}

sub analyze_environmental_subsystem {
  my $self = shift;
  $self->{components}->{environmental_subsystem} =
      NWC::CiscoIOS::Component::EnvironmentalSubsystem->new();
}

sub analyze_interface_subsystem {
  my $self = shift;
  $self->{components}->{interface_subsystem} =
      NWC::IFMIB::Component::InterfaceSubsystem->new();
}

sub analyze_cpu_subsystem {
  my $self = shift;
  $self->{components}->{cpu_subsystem} =
      NWC::CiscoIOS::Component::CpuSubsystem->new();
}

sub analyze_mem_subsystem {
  my $self = shift;
  $self->{components}->{mem_subsystem} =
      NWC::CiscoIOS::Component::MemSubsystem->new();
}

sub check_hsrp_subsystem {
  my $self = shift;
  $self->{components}->{hsrp}->check();
  $self->{components}->{hsrp}->dump()
      if $self->opts->verbose >= 2;
}

sub check_environmental_subsystem {
  my $self = shift;
  $self->{components}->{environmental_subsystem}->check();
  $self->{components}->{environmental_subsystem}->dump()
      if $self->opts->verbose >= 2;
}

sub check_interface_subsystem {
  my $self = shift;
  $self->{components}->{interface_subsystem}->check();
  $self->{components}->{interface_subsystem}->dump()
      if $self->opts->verbose >= 2;
}

sub check_cpu_subsystem {
  my $self = shift;
  $self->{components}->{cpu_subsystem}->check();
  $self->{components}->{cpu_subsystem}->dump()
      if $self->opts->verbose >= 2;
}

sub check_mem_subsystem {
  my $self = shift;
  $self->{components}->{mem_subsystem}->check();
  $self->{components}->{mem_subsystem}->dump()
      if $self->opts->verbose >= 2;
}

sub shinken_interface_subsystem {
  my $self = shift;
  my $attr = sprintf "%s", join(',', map {
      sprintf '%s$(%s)$$()$', $_->{ifDescr}, $_->{ifIndex}
  } @{$self->{components}->{interface_subsystem}->{interfaces}});
  printf <<'EOEO', $self->opts->hostname(), $self->opts->hostname(), $attr;
define host {
  host_name                     %s
  address                       %s
  use                           default-host
  _interfaces                   %s

}
EOEO
  printf <<'EOEO', $self->opts->hostname();
define service {
  host_name                     %s
  service_description           net_cpu
  check_command                 check_nwc_health!cpu-load!80%%!90%%
}
EOEO
  printf <<'EOEO', $self->opts->hostname();
define service {
  host_name                     %s
  service_description           net_mem
  check_command                 check_nwc_health!memory-usage!80%%!90%%
}
EOEO
  printf <<'EOEO', $self->opts->hostname();
define service {
  host_name                     %s
  service_description           net_ifusage_$KEY$
  check_command                 check_nwc_health!interface-usage!$VALUE1$!$VALUE2$
  duplicate_foreach             _interfaces
  default_value                 80%%|90%%
}
EOEO
}


package NWC::CiscoNXOS::Component::CpuSubsystem;
our @ISA = qw(NWC::CiscoNXOS);

use strict;
use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    cpus => [],
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  bless $self, $class;
  $self->init(%params);
  return $self;
}

sub init {
  my $self = shift;
  my %params = @_;
  my $type = 0;
  foreach ($self->get_snmp_table_objects(
      'CISCO-PROCESS-MIB', 'cpmCPUTotalTable')) {
    $_->{cpmCPUTotalIndex} ||= $type++;
    push(@{$self->{cpus}},
        NWC::CiscoNXOS::Component::CpuSubsystem::Cpu->new(%{$_}));
  }
  if (scalar(@{$self->{cpus}}) == 0) {
    # maybe too old. i fake a cpu. be careful. this is a really bad hack
    my $response = $self->get_request(
        -varbindlist => [
            $NWC::Device::mibs_and_oids->{'OLD-CISCO-CPU-MIB'}->{avgBusy1},
            $NWC::Device::mibs_and_oids->{'OLD-CISCO-CPU-MIB'}->{avgBusy5},
            $NWC::Device::mibs_and_oids->{'OLD-CISCO-CPU-MIB'}->{busyPer},
        ]
    );
    if (exists $response->{$NWC::Device::mibs_and_oids->{'OLD-CISCO-CPU-MIB'}->{avgBusy1}}) {
      push(@{$self->{cpus}},
          NWC::CiscoNXOS::Component::CpuSubsystem::Cpu->new(
              cpmCPUTotalPhysicalIndex => 0, #fake
              cpmCPUTotalIndex => 0, #fake
              cpmCPUTotal5sec => 0, #fake
              cpmCPUTotal5secRev => 0, #fake
              cpmCPUTotal1min => $response->{$NWC::Device::mibs_and_oids->{'OLD-CISCO-CPU-MIB'}->{avgBusy1}},
              cpmCPUTotal1minRev => $response->{$NWC::Device::mibs_and_oids->{'OLD-CISCO-CPU-MIB'}->{avgBusy1}},
              cpmCPUTotal5min => $response->{$NWC::Device::mibs_and_oids->{'OLD-CISCO-CPU-MIB'}->{avgBusy5}},
              cpmCPUTotal5minRev => $response->{$NWC::Device::mibs_and_oids->{'OLD-CISCO-CPU-MIB'}->{avgBusy5}},
              cpmCPUMonInterval => 0, #fake
              cpmCPUTotalMonIntervalValue => 0, #fake
              cpmCPUInterruptMonIntervalValue => 0, #fake
      ));
    }
  }
}

sub check {
  my $self = shift;
  my $errorfound = 0;
  $self->add_info('checking cpus');
  $self->blacklist('ff', '');
  if (scalar (@{$self->{cpus}}) == 0) {
  } else {
    foreach (@{$self->{cpus}}) {
      $_->check();
    }
  }
}


sub dump {
  my $self = shift;
  foreach (@{$self->{cpus}}) {
    $_->dump();
  }
}


package NWC::CiscoNXOS::Component::CpuSubsystem::Cpu;
our @ISA = qw(NWC::CiscoNXOS::Component::CpuSubsystem);

use strict;
use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  foreach my $param (qw(cpmCPUTotalIndex cpmCPUTotalPhysicalIndex
      cpmCPUTotal5sec cpmCPUTotal1min cpmCPUTotal5min
      cpmCPUTotal5secRev cpmCPUTotal1minRev cpmCPUTotal5minRev
      cpmCPUMonInterval cpmCPUTotalMonIntervalValue
      cpmCPUInterruptMonIntervalValue)) {
    $self->{$param} = $params{$param};
  }
  bless $self, $class;
  $self->{usage} = $params{cpmCPUTotal5minRev};
  if ($self->{cpmCPUTotalPhysicalIndex}) {
    my $entPhysicalName = '1.3.6.1.2.1.47.1.1.1.1.7';
    $self->{entPhysicalName} = $self->get_request(
        -varbindlist => [$entPhysicalName.'.'.$self->{cpmCPUTotalPhysicalIndex}]
    );
    $self->{entPhysicalName} = $self->{entPhysicalName}->{$entPhysicalName.'.'.$self->{cpmCPUTotalPhysicalIndex}};
  } else {
    $self->{entPhysicalName} = $self->{cpmCPUTotalIndex};
  }
  return $self;
}

sub check {
  my $self = shift;
  $self->blacklist('c', $self->{cpmCPUTotalPhysicalIndex});
  my $info = sprintf 'cpu %s usage (5 min avg.) is %.2f%%',
      $self->{entPhysicalName}, $self->{usage};
  $self->add_info($info);
  $self->set_thresholds(warning => 80, critical => 90);
  $self->add_message($self->check_thresholds($self->{usage}), $info);
  $self->add_perfdata(
      label => 'cpu_'.$self->{entPhysicalName}.'_usage',
      value => $self->{usage},
      uom => '%',
      warning => $self->{warning},
      critical => $self->{critical},
  );
}

sub dump {
  my $self = shift;
  printf "[CPU_%s]\n", $self->{cpmCPUTotalPhysicalIndex};
  foreach (qw(cpmCPUTotalIndex cpmCPUTotalPhysicalIndex cpmCPUTotal5sec cpmCPUTotal1min cpmCPUTotal5min cpmCPUTotal5secRev cpmCPUTotal1minRev cpmCPUTotal5minRev cpmCPUMonInterval cpmCPUTotalMonIntervalValue cpmCPUInterruptMonIntervalValue)) {
    printf "%s: %s\n", $_, $self->{$_};
  }
  printf "info: %s\n", $self->{info};
  printf "\n";
}

package NWC::CiscoNXOS::Component::MemSubsystem;
our @ISA = qw(NWC::CiscoNXOS);

use strict;
use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    runtime => $params{runtime},
    rawdata => $params{rawdata},
    mems => [],
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  bless $self, $class;
  $self->init(%params);
  return $self;
}

sub init {
  my $self = shift;
  my %params = @_;
  my $snmpwalk = $params{rawdata};
  my $ignore_redundancy = $params{ignore_redundancy};
  my $type = 0;
  $self->{cseSysMemoryUtilization} = $self->get_snmp_object('CISCO-SYSTEM-EXT-MIB', 'cseSysMemoryUtilization');
}

sub check {
  my $self = shift;
  my $errorfound = 0;
  $self->add_info('checking memory');
  $self->blacklist('m', '');
  if (defined $self->{cseSysMemoryUtilization}) {
    my $info = sprintf 'memory usage is %.2f%%',
        $self->{cseSysMemoryUtilization};
    $self->add_info($info);
    $self->set_thresholds(warning => 80, critical => 90);
    $self->add_message($self->check_thresholds($self->{cseSysMemoryUtilization}), $info);
    $self->add_perfdata(
        label => 'memory_usage',
        value => $self->{cseSysMemoryUtilization},
        uom => '%',
        warning => $self->{warning},
        critical => $self->{critical}
    );
  } else {
    $self->add_message(UNKNOWN, 'cannot aquire momory usage');
  }
}

sub dump {
  my $self = shift;
  printf "[MEMORY]\n";
  foreach (qw(cseSysMemoryUtilization)) {
    printf "%s: %s\n", $_, $self->{$_};
  }
  printf "info: %s\n", $self->{info};
  printf "\n";
}

package NWC::CiscoNXOS::Component::EnvironmentalSubsystem;
our @ISA = qw(NWC::CiscoNXOS);

use strict;
use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    runtime => $params{runtime},
    rawdata => $params{rawdata},
    method => $params{method},
    condition => $params{condition},
    status => $params{status},
    sensor_subsystem => undef,
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  bless $self, $class;
  $self->init(%params);
  return $self;
}

sub init {
  my $self = shift;
  my %params = @_;
  $self->{sensor_subsystem} =
      NWC::CiscoNXOS::Component::SensorSubsystem->new(%params);
}

sub check {
  my $self = shift;
  my $errorfound = 0;
  $self->{sensor_subsystem}->check();
  if (! $self->check_messages()) {
    $self->add_message(OK, "environmental hardware working fine");
  }
}

sub dump {
  my $self = shift;
  $self->{sensor_subsystem}->dump();
}


package NWC::CiscoNXOS::Component::SensorSubsystem;
our @ISA = qw(NWC::CiscoNXOS::Component::EnvironmentalSubsystem);

use strict;
use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    sensors => [],
    sensorthresholds => [],
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  bless $self, $class;
  $self->init(%params);
  return $self;
}

sub init {
  my $self = shift;
  my $sensors = {};
  foreach ($self->get_snmp_table_objects(
      'CISCO-ENTITY-SENSOR-MIB', 'entSensorValueTable')) {
    my $sensor = NWC::CiscoNXOS::Component::SensorSubsystem::Sensor->new(%{$_});
    $sensors->{$sensor->{entPhysicalIndex}} = $sensor;
  }
  foreach ($self->get_snmp_table_objects(
      'CISCO-ENTITY-SENSOR-MIB', 'entSensorThresholdTable')) {
    my $threshold = NWC::CiscoNXOS::Component::SensorSubsystem::SensorThreshold->new(%{$_});
    if (exists $sensors->{$threshold->{entPhysicalIndex}}) {
      push(@{$sensors->{$threshold->{entPhysicalIndex}}->{thresholds}},
          $threshold);
    } else {
      printf STDERR "sensorthreshold without sensor\n";
    }
  }
#printf "%s\n", Data::Dumper::Dumper($sensors);
  foreach my $sensorid (sort {$a <=> $b} keys %{$sensors}) {
    push(@{$self->{sensors}}, $sensors->{$sensorid});
  }
}

sub check {
  my $self = shift;
  my $errorfound = 0;
  $self->add_info('checking sensors');
  $self->blacklist('t', '');
  if (scalar (@{$self->{sensors}}) == 0) {
  } else {
    foreach (@{$self->{sensors}}) {
      $_->check();
    }
  }
}


sub dump {
  my $self = shift;
  foreach (@{$self->{sensors}}) {
    $_->dump();
  }
}


package NWC::CiscoNXOS::Component::SensorSubsystem::Sensor;
our @ISA = qw(NWC::CiscoNXOS::Component::SensorSubsystem);

use strict;
use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  foreach my $param (qw(entSensorType entSensorScale entSensorPrecision
      entSensorValue entSensorStatus entSensorMeasuredEntity indices)) {
    $self->{$param} = $params{$param};
  }
  $self->{entPhysicalIndex} = $params{indices}[0];
  # www.thaiadmin.org%2Fboard%2Findex.php%3Faction%3Ddlattach%3Btopic%3D45832.0%3Battach%3D23494&ei=kV9zT7GHJ87EsgbEvpX6DQ&usg=AFQjCNHuHiS2MR9TIpYtu7C8bvgzuqxgMQ&cad=rja
  # zu klaeren. entPhysicalIndex entspricht dem entPhysicalindex der ENTITY-MIB.
  # In der stehen alle moeglichen Powersupplies etc.
  # Was bedeutet aber dann entSensorMeasuredEntity? gibt's eh nicht in meinen
  # Beispiel-walks
  $self->{thresholds} = [];
  $self->{entSensorMeasuredEntity} ||= 'undef';
  bless $self, $class;
  return $self;
}

sub check {
  my $self = shift;
  $self->blacklist('se', $self->{entPhysicalIndex});
  $self->add_info(sprintf '%s sensor %s is %s',
      $self->{entSensorType},
      $self->{entPhysicalIndex},
      $self->{entSensorStatus});
  if ($self->{entSensorStatus} eq "nonoperational") {
    $self->add_message(CRITICAL, $self->{info});
  } elsif ($self->{entSensorStatus} eq "unavailable") {
  } elsif (scalar(grep { $_->{entSensorThresholdEvaluation} eq "true" }
        @{$self->{thresholds}})) {
    $self->add_message(CRITICAL,
        sprintf "%s sensor %s threshold evaluation is true", 
        $self->{entSensorType},
        $self->{entPhysicalIndex});
  } else {
  }
  if (scalar(@{$self->{thresholds}} == 2)) {
    my $warning = (map { $_->{entSensorThresholdValue} } 
        grep { $_->{entSensorThresholdSeverity} eq "minor" }
        @{$self->{thresholds}})[0];
    my $critical = (map { $_->{entSensorThresholdValue} } 
        grep { $_->{entSensorThresholdSeverity} eq "major" }
        @{$self->{thresholds}})[0];
    $self->add_perfdata(
        label => sprintf('sens_%s_%s', $self->{entSensorType}, $self->{entPhysicalIndex}),
        value => $self->{entSensorValue},
        warning => $warning,
        critical => $critical,
    );
  } else {
    $self->add_perfdata(
        label => sprintf('sens_%s_%s', $self->{entSensorType}, $self->{entPhysicalIndex}),
        value => $self->{entSensorValue},
        warning => $self->{ciscoEnvMonSensorThreshold},
        critical => undef,
    );
  }
}

sub dump {
  my $self = shift;
  printf "[SENSOR_%s]\n", $self->{entPhysicalIndex};
  foreach (qw(entSensorType entSensorScale entSensorPrecision
      entSensorValue entSensorStatus entSensorMeasuredEntity)) {
    printf "%s: %s\n", $_, $self->{$_};
  }
  printf "info: %s\n", $self->{info};
  foreach my $threshold (@{$self->{thresholds}}) {
    $threshold->dump();
  }
  printf "\n";
}

package NWC::CiscoNXOS::Component::SensorSubsystem::SensorThreshold;
our @ISA = qw(NWC::CiscoNXOS::Component::SensorSubsystem);

use strict;
use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  foreach my $param (qw(entSensorThresholdRelation entSensorThresholdValue
      entSensorThresholdSeverity entSensorThresholdNotificationEnable
      entSensorThresholdEvaluation indices)) {
    $self->{$param} = $params{$param};
  }
  $self->{entPhysicalIndex} = $params{indices}[0];
  $self->{entSensorThresholdIndex} = $params{indices}[1];
  bless $self, $class;
  return $self;
}

sub dump {
  my $self = shift;
  printf "[SENSOR_THRESHOLD_%s_%s]\n", 
      $self->{entPhysicalIndex}, $self->{entSensorThresholdIndex};
  foreach (qw(entSensorThresholdRelation entSensorThresholdValue
      entSensorThresholdSeverity entSensorThresholdNotificationEnable
      entSensorThresholdEvaluation)) {
    printf "%s: %s\n", $_, $self->{$_};
  }
}


package NWC::CiscoNXOS;

use strict;

use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

our @ISA = qw(NWC::Cisco);

sub init {
  my $self = shift;
  $self->{components} = {
      powersupply_subsystem => undef,
      fan_subsystem => undef,
      temperature_subsystem => undef,
      cpu_subsystem => undef,
      memory_subsystem => undef,
      disk_subsystem => undef,
      environmental_subsystem => undef,
  };
  $self->{serial} = 'unknown';
  $self->{product} = 'unknown';
  $self->{romversion} = 'unknown';
  # serial is 1.3.6.1.2.1.47.1.1.1.1.11.1
  #$self->collect();
  if (! $self->check_messages()) {
    ##$self->set_serial();
    if ($self->mode =~ /device::hardware::health/) {
      $self->analyze_environmental_subsystem();
      #$self->auto_blacklist();
      $self->check_environmental_subsystem();
    } elsif ($self->mode =~ /device::hardware::load/) {
      $self->analyze_cpu_subsystem();
      #$self->auto_blacklist();
      $self->check_cpu_subsystem();
    } elsif ($self->mode =~ /device::hardware::memory/) {
      $self->analyze_mem_subsystem();
      #$self->auto_blacklist();
      $self->check_mem_subsystem();
    } elsif ($self->mode =~ /device::interfaces/) {
      $self->analyze_interface_subsystem();
      $self->check_interface_subsystem();
    } elsif ($self->mode =~ /device::shinken::interface/) {
      $self->analyze_interface_subsystem();
      $self->shinken_interface_subsystem();
    }
  }
}

sub analyze_environmental_subsystem {
  my $self = shift;
  $self->{components}->{environmental_subsystem} =
      NWC::CiscoNXOS::Component::EnvironmentalSubsystem->new();
}

sub analyze_interface_subsystem {
  my $self = shift;
  $self->{components}->{interface_subsystem} =
      NWC::IFMIB::Component::InterfaceSubsystem->new();
}

sub analyze_cpu_subsystem {
  my $self = shift;
  $self->{components}->{cpu_subsystem} =
      NWC::CiscoNXOS::Component::CpuSubsystem->new();
}

sub analyze_mem_subsystem {
  my $self = shift;
  $self->{components}->{mem_subsystem} =
      NWC::CiscoNXOS::Component::MemSubsystem->new();
}

sub check_environmental_subsystem {
  my $self = shift;
  $self->{components}->{environmental_subsystem}->check();
  $self->{components}->{environmental_subsystem}->dump()
      if $self->opts->verbose >= 2;
}

sub check_interface_subsystem {
  my $self = shift;
  $self->{components}->{interface_subsystem}->check();
  $self->{components}->{interface_subsystem}->dump()
      if $self->opts->verbose >= 2;
}

sub check_cpu_subsystem {
  my $self = shift;
  $self->{components}->{cpu_subsystem}->check();
  $self->{components}->{cpu_subsystem}->dump()
      if $self->opts->verbose >= 2;
}

sub check_mem_subsystem {
  my $self = shift;
  $self->{components}->{mem_subsystem}->check();
  $self->{components}->{mem_subsystem}->dump()
      if $self->opts->verbose >= 2;
}

sub shinken_interface_subsystem {
  my $self = shift;
  my $attr = sprintf "%s", join(',', map {
      sprintf '%s$(%s)$$()$', $_->{ifDescr}, $_->{ifIndex}
  } @{$self->{components}->{interface_subsystem}->{interfaces}});
  printf <<'EOEO', $self->opts->hostname(), $self->opts->hostname(), $attr;
define host {
  host_name                     %s
  address                       %s
  use                           default-host
  _interfaces                   %s

}
EOEO
  printf <<'EOEO', $self->opts->hostname();
define service {
  host_name                     %s
  service_description           net_cpu
  check_command                 check_nwc_health!cpu-load!80%%!90%%
}
EOEO
  printf <<'EOEO', $self->opts->hostname();
define service {
  host_name                     %s
  service_description           net_mem
  check_command                 check_nwc_health!memory-usage!80%%!90%%
}
EOEO
  printf <<'EOEO', $self->opts->hostname();
define service {
  host_name                     %s
  service_description           net_ifusage_$KEY$
  check_command                 check_nwc_health!interface-usage!$VALUE1$!$VALUE2$
  duplicate_foreach             _interfaces
  default_value                 80%%|90%%
}
EOEO
}


package NWC::Cisco;

use strict;

use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

our @ISA = qw(NWC::Device);

use constant trees => (
  '1.3.6.1.2.1',        # mib-2
  '1.3.6.1.4.1.9',      # cisco
  '1.3.6.1.4.1.9.1',      # ciscoProducts
  '1.3.6.1.4.1.9.2',      # local
  '1.3.6.1.4.1.9.3',      # temporary
  '1.3.6.1.4.1.9.4',      # pakmon
  '1.3.6.1.4.1.9.5',      # workgroup
  '1.3.6.1.4.1.9.6',      # otherEnterprises
  '1.3.6.1.4.1.9.7',      # ciscoAgentCapability
  '1.3.6.1.4.1.9.8',      # ciscoConfig
  '1.3.6.1.4.1.9.9',      # ciscoMgmt
  '1.3.6.1.4.1.9.10',      # ciscoExperiment
  '1.3.6.1.4.1.9.11',      # ciscoAdmin
  '1.3.6.1.4.1.9.12',      # ciscoModules
  '1.3.6.1.4.1.9.13',      # lightstream
  '1.3.6.1.4.1.9.14',      # ciscoworks
  '1.3.6.1.4.1.9.15',      # newport
  '1.3.6.1.4.1.9.16',      # ciscoPartnerProducts
  '1.3.6.1.4.1.9.17',      # ciscoPolicy
  '1.3.6.1.4.1.9.18',      # ciscoPolicyAuto
  '1.3.6.1.4.1.9.19',      # ciscoDomains
);

sub init {
  my $self = shift;
  if ($self->{productname} =~ /Cisco NX-OS/i) {
    bless $self, 'NWC::CiscoNXOS';
    $self->debug('using NWC::CiscoNXOS');
  } elsif ($self->{productname} =~ /Cisco/i) {
    bless $self, 'NWC::CiscoIOS';
    $self->debug('using NWC::CiscoIOS');
  }
  $self->init();
}

package NWC::Nortel;

use strict;

use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

our @ISA = qw(NWC::Device);

sub init {
  my $self = shift;
  $self->{components} = {
      powersupply_subsystem => undef,
      fan_subsystem => undef,
      temperature_subsystem => undef,
      cpu_subsystem => undef,
      memory_subsystem => undef,
      disk_subsystem => undef,
      environmental_subsystem => undef,
  };
  $self->{serial} = 'unknown';
  $self->{product} = 'unknown';
  $self->{romversion} = 'unknown';
  # serial is 1.3.6.1.2.1.47.1.1.1.1.11.1
  #$self->collect();
  if (! $self->check_messages()) {
    ##$self->set_serial();
    if ($self->mode =~ /device::hardware::health/) {
      $self->analyze_environmental_subsystem();
      #$self->auto_blacklist();
      $self->check_environmental_subsystem();
    } elsif ($self->mode =~ /device::hardware::load/) {
      $self->analyze_cpu_subsystem();
      #$self->auto_blacklist();
      $self->check_cpu_subsystem();
    } elsif ($self->mode =~ /device::hardware::memory/) {
      $self->analyze_mem_subsystem();
      #$self->auto_blacklist();
      $self->check_mem_subsystem();
    } elsif ($self->mode =~ /device::interfaces/) {
      $self->analyze_interface_subsystem();
      $self->check_interface_subsystem();
    } elsif ($self->mode =~ /device::shinken::interface/) {
      $self->analyze_interface_subsystem();
      $self->shinken_interface_subsystem();
    } elsif ($self->mode =~ /device::hsrp/) {
      $self->analyze_hsrp_subsystem();
      $self->check_interface_subsystem();
    }
  }
}

sub analyze_hsrp_subsystem {
  my $self = shift;
  $self->{components}->{hsrp} =
      NWC::HSRP::Component::HSRPSubsystem->new();
}

sub analyze_environmental_subsystem {
  my $self = shift;
  $self->{components}->{environmental_subsystem} =
      NWC::Nortel::Component::EnvironmentalSubsystem->new();
}

sub analyze_interface_subsystem {
  my $self = shift;
  $self->{components}->{interface_subsystem} =
      NWC::IFMIB::Component::InterfaceSubsystem->new();
}

sub analyze_cpu_subsystem {
  my $self = shift;
  $self->{components}->{cpu_subsystem} =
      NWC::Nortel::Component::CpuSubsystem->new();
}

sub analyze_mem_subsystem {
  my $self = shift;
  $self->{components}->{mem_subsystem} =
      NWC::Nortel::Component::MemSubsystem->new();
}

sub check_environmental_subsystem {
  my $self = shift;
  $self->{components}->{environmental_subsystem}->check();
  $self->{components}->{environmental_subsystem}->dump()
      if $self->opts->verbose >= 2;
}

sub check_interface_subsystem {
  my $self = shift;
  $self->{components}->{interface_subsystem}->check();
  $self->{components}->{interface_subsystem}->dump()
      if $self->opts->verbose >= 2;
}

sub check_cpu_subsystem {
  my $self = shift;
  $self->{components}->{cpu_subsystem}->check();
  $self->{components}->{cpu_subsystem}->dump()
      if $self->opts->verbose >= 2;
}

sub check_mem_subsystem {
  my $self = shift;
  $self->{components}->{mem_subsystem}->check();
  $self->{components}->{mem_subsystem}->dump()
      if $self->opts->verbose >= 2;
}

sub shinken_interface_subsystem {
  my $self = shift;
  my $attr = sprintf "%s", join(',', map {
      sprintf '%s$(%s)$$()$', $_->{ifDescr}, $_->{ifIndex}
  } @{$self->{components}->{interface_subsystem}->{interfaces}});
  printf <<'EOEO', $self->opts->hostname(), $self->opts->hostname(), $attr;
define host {
  host_name                     %s
  address                       %s
  use                           default-host
  _interfaces                   %s

}
EOEO
  printf <<'EOEO', $self->opts->hostname();
define service {
  host_name                     %s
  service_description           net_cpu
  check_command                 check_nwc_health!cpu-load!80%%!90%%
}
EOEO
  printf <<'EOEO', $self->opts->hostname();
define service {
  host_name                     %s
  service_description           net_mem
  check_command                 check_nwc_health!memory-usage!80%%!90%%
}
EOEO
  printf <<'EOEO', $self->opts->hostname();
define service {
  host_name                     %s
  service_description           net_ifusage_$KEY$
  check_command                 check_nwc_health!interface-usage!$VALUE1$!$VALUE2$
  duplicate_foreach             _interfaces
  default_value                 80%%|90%%
}
EOEO
}


package NWC::Netscreen;

use strict;

use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

our @ISA = qw(NWC::Device);

use constant trees => (
  '1.3.6.1.2.1',        # mib-2
  '1.3.6.1.2.1.105',
);

sub init {
  my $self = shift;
  $self->{components} = {
      powersupply_subsystem => undef,
      fan_subsystem => undef,
      temperature_subsystem => undef,
      cpu_subsystem => undef,
      memory_subsystem => undef,
      disk_subsystem => undef,
      environmental_subsystem => undef,
  };
  $self->{serial} = 'unknown';
  $self->{product} = 'unknown';
  $self->{romversion} = 'unknown';
  # serial is 1.3.6.1.2.1.47.1.1.1.1.11.1
  #$self->collect();
  if (! $self->check_messages()) {
    ##$self->set_serial();
    if ($self->mode =~ /device::hardware::health/) {
      $self->no_such_mode();
    } elsif ($self->mode =~ /device::hardware::load/) {
      $self->no_such_mode();
    } elsif ($self->mode =~ /device::hardware::memory/) {
      $self->no_such_mode();
    } elsif ($self->mode =~ /device::interfaces/) {
      $self->analyze_interface_subsystem();
      $self->check_interface_subsystem();
    }
  }
}

sub analyze_interface_subsystem {
  my $self = shift;
  $self->{components}->{interface_subsystem} =
      NWC::IFMIB::Component::InterfaceSubsystem->new();
}

sub check_interface_subsystem {
  my $self = shift;
  $self->{components}->{interface_subsystem}->check();
  $self->{components}->{interface_subsystem}->dump()
      if $self->opts->verbose >= 2;
}


package NWC::AlliedTelesyn;

use strict;

use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

our @ISA = qw(NWC::Device);

sub init {
  my $self = shift;
  $self->{components} = {
      powersupply_subsystem => undef,
      fan_subsystem => undef,
      temperature_subsystem => undef,
      cpu_subsystem => undef,
      memory_subsystem => undef,
      disk_subsystem => undef,
      environmental_subsystem => undef,
  };
  $self->{serial} = 'unknown';
  $self->{product} = 'unknown';
  $self->{romversion} = 'unknown';
  # serial is 1.3.6.1.2.1.47.1.1.1.1.11.1
  #$self->collect();
  if (! $self->check_messages()) {
    ##$self->set_serial();
    if ($self->mode =~ /device::hardware::health/) {
      $self->analyze_environmental_subsystem();
      #$self->auto_blacklist();
      $self->check_environmental_subsystem();
    } elsif ($self->mode =~ /device::hardware::load/) {
      $self->analyze_cpu_subsystem();
      #$self->auto_blacklist();
      $self->check_cpu_subsystem();
    } elsif ($self->mode =~ /device::hardware::memory/) {
      $self->analyze_mem_subsystem();
      #$self->auto_blacklist();
      $self->check_mem_subsystem();
    } elsif ($self->mode =~ /device::interfaces/) {
      $self->analyze_interface_subsystem();
      $self->check_interface_subsystem();
    } elsif ($self->mode =~ /device::shinken::interface/) {
      $self->analyze_interface_subsystem();
      $self->shinken_interface_subsystem();
    } elsif ($self->mode =~ /device::hsrp/) {
      $self->analyze_hsrp_subsystem();
      $self->check_interface_subsystem();
    }
  }
}

sub analyze_hsrp_subsystem {
  my $self = shift;
  $self->{components}->{hsrp} =
      NWC::HSRP::Component::HSRPSubsystem->new();
}

sub analyze_environmental_subsystem {
  my $self = shift;
  $self->{components}->{environmental_subsystem} =
      NWC::AlliedTelesyn::Component::EnvironmentalSubsystem->new();
}

sub analyze_interface_subsystem {
  my $self = shift;
  $self->{components}->{interface_subsystem} =
      NWC::IFMIB::Component::InterfaceSubsystem->new();
}

sub analyze_cpu_subsystem {
  my $self = shift;
  $self->{components}->{cpu_subsystem} =
      NWC::AlliedTelesyn::Component::CpuSubsystem->new();
}

sub analyze_mem_subsystem {
  my $self = shift;
  $self->{components}->{mem_subsystem} =
      NWC::AlliedTelesyn::Component::MemSubsystem->new();
}

sub check_environmental_subsystem {
  my $self = shift;
  $self->{components}->{environmental_subsystem}->check();
  $self->{components}->{environmental_subsystem}->dump()
      if $self->opts->verbose >= 2;
}

sub check_interface_subsystem {
  my $self = shift;
  $self->{components}->{interface_subsystem}->check();
  $self->{components}->{interface_subsystem}->dump()
      if $self->opts->verbose >= 2;
}

sub check_cpu_subsystem {
  my $self = shift;
  $self->{components}->{cpu_subsystem}->check();
  $self->{components}->{cpu_subsystem}->dump()
      if $self->opts->verbose >= 2;
}

sub check_mem_subsystem {
  my $self = shift;
  $self->{components}->{mem_subsystem}->check();
  $self->{components}->{mem_subsystem}->dump()
      if $self->opts->verbose >= 2;
}

sub shinken_interface_subsystem {
  my $self = shift;
  my $attr = sprintf "%s", join(',', map {
      sprintf '%s$(%s)$$()$', $_->{ifDescr}, $_->{ifIndex}
  } @{$self->{components}->{interface_subsystem}->{interfaces}});
  printf <<'EOEO', $self->opts->hostname(), $self->opts->hostname(), $attr;
define host {
  host_name                     %s
  address                       %s
  use                           default-host
  _interfaces                   %s

}
EOEO
  printf <<'EOEO', $self->opts->hostname();
define service {
  host_name                     %s
  service_description           net_cpu
  check_command                 check_nwc_health!cpu-load!80%%!90%%
}
EOEO
  printf <<'EOEO', $self->opts->hostname();
define service {
  host_name                     %s
  service_description           net_mem
  check_command                 check_nwc_health!memory-usage!80%%!90%%
}
EOEO
  printf <<'EOEO', $self->opts->hostname();
define service {
  host_name                     %s
  service_description           net_ifusage_$KEY$
  check_command                 check_nwc_health!interface-usage!$VALUE1$!$VALUE2$
  duplicate_foreach             _interfaces
  default_value                 80%%|90%%
}
EOEO
}


package NWC::FabOS::Component::MemSubsystem;
our @ISA = qw(NWC::FabOS);

use strict;
use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    runtime => $params{runtime},
    rawdata => $params{rawdata},
    mems => [],
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  bless $self, $class;
  $self->init(%params);
  return $self;
}

sub init {
  my $self = shift;
  my %params = @_;
  my $snmpwalk = $params{rawdata};
  my $ignore_redundancy = $params{ignore_redundancy};
  my $type = 0;
  foreach (qw(swMemUsage swMemUsageLimit1 swMemUsageLimit3 swMemPollingInterval
      swMemNoOfRetries swMemAction)) {
    $self->{$_} = $self->get_snmp_object('SW-MIB', $_, 0);
  }
}

sub check {
  my $self = shift;
  my $errorfound = 0;
  $self->add_info('checking memory');
  $self->blacklist('m', '');
  if (defined $self->{swMemUsage}) {
    my $info = sprintf 'memory usage is %.2f%%',
        $self->{swMemUsage};
    $self->add_info($info);
    $self->set_thresholds(warning => $self->{swMemUsageLimit1},
        critical => $self->{swMemUsageLimit3});
    $self->add_message($self->check_thresholds($self->{swMemUsage}), $info);
    $self->add_perfdata(
        label => 'memory_usage',
        value => $self->{swMemUsage},
        uom => '%',
        warning => $self->{warning},
        critical => $self->{critical}
    );
  } else {
    $self->add_message(UNKNOWN, 'cannot aquire momory usage');
  }
}

sub dump {
  my $self = shift;
  printf "[MEMORY]\n";
  foreach (qw(swMemUsage swMemUsageLimit1 swMemUsageLimit3 swMemPollingInterval
      swMemNoOfRetries swMemAction)) {
    printf "%s: %s\n", $_, $self->{$_};
  }
  printf "info: %s\n", $self->{info};
  printf "\n";
}

package NWC::FabOS::Component::CpuSubsystem;
our @ISA = qw(NWC::FabOS);

use strict;
use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    cpus => [],
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  bless $self, $class;
  $self->init(%params);
  return $self;
}

sub init {
  my $self = shift;
  my %params = @_;
  my $type = 0;
  foreach (qw(swCpuUsage swCpuNoOfRetries swCpuUsageLimit swCpuPollingInterval
      swCpuAction)) {
    $self->{$_} = $self->get_snmp_object('SW-MIB', $_, 0);
  }
}

sub check {
  my $self = shift;
  my $errorfound = 0;
  $self->add_info('checking cpus');
  $self->blacklist('c', undef);
  my $info = sprintf 'cpu usage is %.2f%%', $self->{swCpuUsage};
  $self->add_info($info);
  $self->set_thresholds(warning => $self->{swCpuUsageLimit},
      critical => $self->{swCpuUsageLimit});
  $self->add_message($self->check_thresholds($self->{swCpuUsage}), $info);
  $self->add_perfdata(
      label => 'cpu_usage',
      value => $self->{swCpuUsage},
      uom => '%',
      warning => $self->{warning},
      critical => $self->{critical},
  );
}

sub dump {
  my $self = shift;
  printf "[CPU]\n";
  foreach (qw(swCpuUsage swCpuNoOfRetries swCpuUsageLimit swCpuPollingInterval
      swCpuAction)) {
    printf "%s: %s\n", $_, $self->{$_};
  }
  printf "info: %s\n", $self->{info};
  printf "\n";
}

package NWC::FabOS::Component::EnvironmentalSubsystem;
our @ISA = qw(NWC::FabOS);

use strict;
use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    runtime => $params{runtime},
    rawdata => $params{rawdata},
    method => $params{method},
    condition => $params{condition},
    status => $params{status},
    sensor_subsystem => undef,
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  bless $self, $class;
  $self->init(%params);
  return $self;
}

sub init {
  my $self = shift;
  my %params = @_;
  $self->{sensor_subsystem} =
      NWC::FabOS::Component::SensorSubsystem->new(%params);
}

sub check {
  my $self = shift;
  my $errorfound = 0;
  $self->{sensor_subsystem}->check();
  if (! $self->check_messages()) {
    $self->add_message(OK, "environmental hardware working fine");
  }
}

sub dump {
  my $self = shift;
  $self->{sensor_subsystem}->dump();
}


package NWC::FabOS::Component::SensorSubsystem;
our @ISA = qw(NWC::FabOS::Component::EnvironmentalSubsystem);

use strict;
use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    sensors => [],
    sensorthresholds => [],
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  bless $self, $class;
  $self->init(%params);
  return $self;
}

sub init {
  my $self = shift;
  my $sensors = {};
  foreach ($self->get_snmp_table_objects(
      'SW-MIB', 'swSensorTable')) {
    push(@{$self->{sensors}}, 
        NWC::FabOS::Component::SensorSubsystem::Sensor->new(%{$_}));
  }
  #foreach ($self->get_snmp_table_objects(
  #    'SW-MIB', 'swFwThresholdTable')) {
  #  printf "%s\n", Data::Dumper::Dumper($_);
  #}
}

sub check {
  my $self = shift;
  my $errorfound = 0;
  $self->add_info('checking sensors');
  $self->blacklist('ses', '');
  if (scalar (@{$self->{sensors}}) == 0) {
  } else {
    foreach (@{$self->{sensors}}) {
      $_->check();
    }
  }
}


sub dump {
  my $self = shift;
  foreach (@{$self->{sensors}}) {
    $_->dump();
  }
}


package NWC::FabOS::Component::SensorSubsystem::Sensor;
our @ISA = qw(NWC::FabOS::Component::SensorSubsystem);

use strict;
use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  foreach my $param (qw(swSensorIndex swSensorType swSensorStatus
      swSensorValue swSensorInfo)) {
    $self->{$param} = $params{$param};
  }
  bless $self, $class;
  return $self;
}

sub check {
  my $self = shift;
  $self->blacklist('se', $self->{swSensorIndex});
  $self->add_info(sprintf '%s sensor %s (%s) is %s',
      $self->{swSensorType},
      $self->{swSensorIndex},
      $self->{swSensorInfo},
      $self->{swSensorStatus});
  if ($self->{swSensorStatus} eq "faulty") {
    $self->add_message(CRITICAL, $self->{info});
  } elsif ($self->{swSensorStatus} eq "absent") {
  } elsif ($self->{swSensorStatus} eq "unknown") {
    $self->add_message(CRITICAL, $self->{info});
  } else {
    if ($self->{swSensorStatus} eq "nominal") {
      #$self->add_message(OK, $self->{info});
    } else {
      $self->add_message(CRITICAL, $self->{info});
    }
    $self->add_perfdata(
        label => sprintf('sensor_%s_%s', 
            $self->{swSensorType}, $self->{swSensorIndex}),
        value => $self->{swSensorValue},
    ) if $self->{swSensorType} ne "power-supply";
  }
}

sub dump {
  my $self = shift;
  printf "[SENSOR_%s_%s]\n", $self->{swSensorType}, $self->{swSensorIndex};
  foreach (qw(swSensorIndex swSensorType swSensorStatus
      swSensorValue swSensorInfo)) {
    printf "%s: %s\n", $_, $self->{$_};
  }
  printf "info: %s\n", $self->{info};
  printf "\n";
}

package NWC::FabOS::Component::SensorSubsystem::SensorThreshold;
our @ISA = qw(NWC::FabOS::Component::SensorSubsystem);

use strict;
use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  foreach my $param (qw(entSensorThresholdRelation entSensorThresholdValue
      entSensorThresholdSeverity entSensorThresholdNotificationEnable
      entSensorThresholdEvaluation indices)) {
    $self->{$param} = $params{$param};
  }
  $self->{entPhysicalIndex} = $params{indices}[0];
  $self->{entSensorThresholdIndex} = $params{indices}[1];
  bless $self, $class;
  return $self;
}

sub dump {
  my $self = shift;
  printf "[SENSOR_THRESHOLD_%s_%s]\n", 
      $self->{entPhysicalIndex}, $self->{entSensorThresholdIndex};
  foreach (qw(entSensorThresholdRelation entSensorThresholdValue
      entSensorThresholdSeverity entSensorThresholdNotificationEnable
      entSensorThresholdEvaluation)) {
    printf "%s: %s\n", $_, $self->{$_};
  }
}


package NWC::FabOS;

use strict;

use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

our @ISA = qw(NWC::Brocade);

sub init {
  my $self = shift;
  $self->{components} = {
      powersupply_subsystem => undef,
      fan_subsystem => undef,
      temperature_subsystem => undef,
      cpu_subsystem => undef,
  };
  $self->{serial} = 'unknown';
  $self->{product} = 'unknown';
  $self->{romversion} = 'unknown';
  # serial is 1.3.6.1.2.1.47.1.1.1.1.11.1
  #$self->collect();
  if (! $self->check_messages()) {
    ##$self->set_serial();
    if ($self->mode =~ /device::hardware::health/) {
      $self->analyze_environmental_subsystem();
      $self->check_environmental_subsystem();
    } elsif ($self->mode =~ /device::hardware::load/) {
      $self->analyze_cpu_subsystem();
      $self->check_cpu_subsystem();
    } elsif ($self->mode =~ /device::hardware::memory/) {
      $self->analyze_mem_subsystem();
      $self->check_mem_subsystem();
    } elsif ($self->mode =~ /device::interfaces/) {
      $self->analyze_interface_subsystem();
      $self->check_interface_subsystem();
    }
  }
}

sub analyze_environmental_subsystem {
  my $self = shift;
  $self->{components}->{environmental_subsystem} =
      NWC::FabOS::Component::EnvironmentalSubsystem->new();
}

sub analyze_cpu_subsystem {
  my $self = shift;
  $self->{components}->{cpu_subsystem} =
      NWC::FabOS::Component::CpuSubsystem->new();
#printf "%s\n", Data::Dumper::Dumper($self->{components});
}

sub analyze_mem_subsystem {
  my $self = shift;
  $self->{components}->{mem_subsystem} =
      NWC::FabOS::Component::MemSubsystem->new();
}

sub analyze_interface_subsystem {
  my $self = shift;
  $self->{components}->{interface_subsystem} =
      NWC::IFMIB::Component::InterfaceSubsystem->new();
}

sub check_environmental_subsystem {
  my $self = shift;
  $self->{components}->{environmental_subsystem}->check();
  $self->{components}->{environmental_subsystem}->dump()
      if $self->opts->verbose >= 2;
}

sub check_cpu_subsystem {
  my $self = shift;
  $self->{components}->{cpu_subsystem}->check();
  $self->{components}->{cpu_subsystem}->dump()
      if $self->opts->verbose >= 2;
}

sub check_mem_subsystem {
  my $self = shift;
  $self->{components}->{mem_subsystem}->check();
  $self->{components}->{mem_subsystem}->dump()
      if $self->opts->verbose >= 2;
}

sub check_interface_subsystem {
  my $self = shift;
  $self->{components}->{interface_subsystem}->check();
  $self->{components}->{interface_subsystem}->dump()
      if $self->opts->verbose >= 2;
}

package NWC::HP::Procurve::Component::MemSubsystem;
our @ISA = qw(NWC::HP::Procurve::Component::EnvironmentalSubsystem);

use strict;
use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    sensors => [],
    sensorthresholds => [],
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  bless $self, $class;
  $self->init(%params);
  return $self;
}

sub init {
  my $self = shift;
  my $sensors = {};
  foreach ($self->get_snmp_table_objects(
      'NETSWITCH-MIB', 'hpLocalMemTable')) {
    push(@{$self->{mem}}, 
        NWC::HP::Procurve::Component::MemSubsystem::Memory->new(%{$_}));
  }
}

sub check {
  my $self = shift;
  my $errorfound = 0;
  $self->add_info('checking memory');
  $self->blacklist('m', '');
  if (scalar (@{$self->{mem}}) == 0) {
  } else {
    foreach (@{$self->{mem}}) {
      $_->check();
    }
  }
}


sub dump {
  my $self = shift;
  foreach (@{$self->{mem}}) {
    $_->dump();
  }
}


package NWC::HP::Procurve::Component::MemSubsystem::Memory;
our @ISA = qw(NWC::HP::Procurve::Component::MemSubsystem);

use strict;
use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  foreach (qw(hpLocalMemSlotIndex  hpLocalMemSlabCnt
      hpLocalMemFreeSegCnt hpLocalMemAllocSegCnt hpLocalMemTotalBytes
      hpLocalMemFreeBytes hpLocalMemAllocBytes)) {
    $self->{$_} = $params{$_};
  }
  bless $self, $class;
  return $self;
}

sub check {
  my $self = shift;
  $self->blacklist('m', $self->{hpicfMemIndex});
  $self->{usage} = $self->{hpLocalMemAllocBytes} / 
      $self->{hpLocalMemTotalBytes} * 100;
  my $info = sprintf 'memory %s usage is %.2f',
      $self->{hpLocalMemSlotIndex},
      $self->{usage};
  $self->add_info($info);
  $self->set_thresholds(warning => 80, critical => 90);
  $self->add_message($self->check_thresholds($self->{usage}), $info);
  $self->add_perfdata(
      label => 'memory_'.$self->{hpLocalMemSlotIndex}.'_usage',
      value => $self->{usage},
      uom => '%',
      warning => $self->{warning},
      critical => $self->{critical}
  );
}

sub dump {
  my $self = shift;
  printf "[MEM%s]\n", $self->{hpLocalMemSlotIndex};
  foreach (qw(hpLocalMemSlotIndex  hpLocalMemSlabCnt
      hpLocalMemFreeSegCnt hpLocalMemAllocSegCnt hpLocalMemTotalBytes
      hpLocalMemFreeBytes hpLocalMemAllocBytes)) {
    printf "%s: %s\n", $_, $self->{$_};
  }
  printf "info: %s\n", $self->{info};
  printf "\n";
}


package NWC::HP::Procurve::Component::CpuSubsystem;
our @ISA = qw(NWC::HP::Procurve);

use strict;
use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    cpus => [],
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  bless $self, $class;
  $self->init(%params);
  return $self;
}

sub init {
  my $self = shift;
  my %params = @_;
  $self->{hpSwitchCpuStat} = $self->get_snmp_object('STATISTICS-MIB', 'hpSwitchCpuStat');
}

sub check {
  my $self = shift;
  my $errorfound = 0;
  $self->add_info('checking cpus');
  my $info = sprintf 'cpu usage is %.2f%%', $self->{hpSwitchCpuStat};
  $self->add_info($info);
  $self->set_thresholds(warning => 80, critical => 90); # maybe lower, because the switching is done in hardware
  $self->add_message($self->check_thresholds($self->{hpSwitchCpuStat}), $info);
  $self->add_perfdata(
      label => 'cpu_usage',
      value => $self->{hpSwitchCpuStat},
      uom => '%',
      warning => $self->{warning},
      critical => $self->{critical},
  );
}

sub dump {
  my $self = shift;
  printf "[CPU]\n";
  foreach (qw(hpSwitchCpuStat)) {
    printf "%s: %s\n", $_, $self->{$_};
  }
  printf "info: %s\n", $self->{info};
  printf "\n";
}

package NWC::HP::Procurve::Component::EnvironmentalSubsystem;
our @ISA = qw(NWC::HP::Procurve);

use strict;
use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    runtime => $params{runtime},
    rawdata => $params{rawdata},
    method => $params{method},
    condition => $params{condition},
    status => $params{status},
    sensor_subsystem => undef,
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  bless $self, $class;
  $self->init(%params);
  return $self;
}

sub init {
  my $self = shift;
  my %params = @_;
  $self->{sensor_subsystem} =
      NWC::HP::Procurve::Component::SensorSubsystem->new(%params);
}

sub check {
  my $self = shift;
  my $errorfound = 0;
  $self->{sensor_subsystem}->check();
  if (! $self->check_messages()) {
    $self->add_message(OK, "environmental hardware working fine");
  }
}

sub dump {
  my $self = shift;
  $self->{sensor_subsystem}->dump();
}


package NWC::HP::Procurve::Component::SensorSubsystem;
our @ISA = qw(NWC::HP::Procurve::Component::EnvironmentalSubsystem);

use strict;
use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    sensors => [],
    sensorthresholds => [],
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  bless $self, $class;
  $self->init(%params);
  return $self;
}

sub init {
  my $self = shift;
  my $sensors = {};
  foreach ($self->get_snmp_table_objects(
      'HP-ICF-CHASSIS-MIB', 'hpicfSensorTable')) {
    push(@{$self->{sensors}}, 
        NWC::HP::Procurve::Component::SensorSubsystem::Sensor->new(%{$_}));
  }
}

sub check {
  my $self = shift;
  my $errorfound = 0;
  $self->add_info('checking sensors');
  $self->blacklist('ses', '');
  if (scalar (@{$self->{sensors}}) == 0) {
  } else {
    foreach (@{$self->{sensors}}) {
      $_->check();
    }
  }
}


sub dump {
  my $self = shift;
  foreach (@{$self->{sensors}}) {
    $_->dump();
  }
}


package NWC::HP::Procurve::Component::SensorSubsystem::Sensor;
our @ISA = qw(NWC::HP::Procurve::Component::SensorSubsystem);

use strict;
use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  foreach my $param (qw(hpicfSensorIndex hpicfSensorObjectId 
      hpicfSensorNumber hpicfSensorStatus hpicfSensorWarnings
      hpicfSensorFailures hpicfSensorDescr)) {
    $self->{$param} = $params{$param};
  }
  bless $self, $class;
  return $self;
}

sub check {
  my $self = shift;
  $self->blacklist('se', $self->{hpicfSensorIndex});
  $self->add_info(sprintf 'sensor %s (%s) is %s',
      $self->{hpicfSensorIndex},
      $self->{hpicfSensorDescr},
      $self->{hpicfSensorStatus});
  if ($self->{hpicfSensorStatus} eq "notPresent") {
  } elsif ($self->{hpicfSensorStatus} eq "bad") {
    $self->add_message(CRITICAL, $self->{info});
  } elsif ($self->{hpicfSensorStatus} eq "warning") {
    $self->add_message(WARNING, $self->{info});
  } elsif ($self->{hpicfSensorStatus} eq "good") {
    #$self->add_message(OK, $self->{info});
  } else {
    $self->add_message(UNKNOWN, $self->{info});
  }
}

sub dump {
  my $self = shift;
  printf "[SENSOR_%s]\n", $self->{hpicfSensorIndex};
  foreach (qw(hpicfSensorIndex hpicfSensorObjectId 
      hpicfSensorNumber hpicfSensorStatus hpicfSensorWarnings
      hpicfSensorFailures hpicfSensorDescr)) {
    printf "%s: %s\n", $_, $self->{$_};
  }
  printf "info: %s\n", $self->{info};
  printf "\n";
}


package NWC::HP::Procurve;

use strict;

use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

our @ISA = qw(NWC::HP);

sub init {
  my $self = shift;
  $self->{components} = {
      cpu_subsystem => undef,
      memory_subsystem => undef,
      environmental_subsystem => undef,
  };
  $self->{serial} = 'unknown';
  $self->{product} = 'unknown';
  $self->{romversion} = 'unknown';
  # serial is 1.3.6.1.2.1.47.1.1.1.1.11.1
  #$self->collect();
  if (! $self->check_messages()) {
    ##$self->set_serial();
    if ($self->mode =~ /device::hardware::health/) {
      $self->analyze_environmental_subsystem();
      #$self->auto_blacklist();
      $self->check_environmental_subsystem();
    } elsif ($self->mode =~ /device::hardware::load/) {
      $self->analyze_cpu_subsystem();
      #$self->auto_blacklist();
      $self->check_cpu_subsystem();
    } elsif ($self->mode =~ /device::hardware::memory/) {
      $self->analyze_mem_subsystem();
      #$self->auto_blacklist();
      $self->check_mem_subsystem();
    } elsif ($self->mode =~ /device::interfaces/) {
      $self->analyze_interface_subsystem();
      $self->check_interface_subsystem();
    } elsif ($self->mode =~ /device::shinken::interface/) {
      $self->analyze_interface_subsystem();
      $self->shinken_interface_subsystem();
    }
  }
}

sub analyze_environmental_subsystem {
  my $self = shift;
  $self->{components}->{environmental_subsystem} =
      NWC::HP::Procurve::Component::EnvironmentalSubsystem->new();
}

sub analyze_interface_subsystem {
  my $self = shift;
  $self->{components}->{interface_subsystem} =
      NWC::IFMIB::Component::InterfaceSubsystem->new();
}

sub analyze_cpu_subsystem {
  my $self = shift;
  $self->{components}->{cpu_subsystem} =
      NWC::HP::Procurve::Component::CpuSubsystem->new();
}

sub analyze_mem_subsystem {
  my $self = shift;
  $self->{components}->{mem_subsystem} =
      NWC::HP::Procurve::Component::MemSubsystem->new();
}

sub check_environmental_subsystem {
  my $self = shift;
  $self->{components}->{environmental_subsystem}->check();
  $self->{components}->{environmental_subsystem}->dump()
      if $self->opts->verbose >= 2;
}

sub check_interface_subsystem {
  my $self = shift;
  $self->{components}->{interface_subsystem}->check();
  $self->{components}->{interface_subsystem}->dump()
      if $self->opts->verbose >= 2;
}

sub check_cpu_subsystem {
  my $self = shift;
  $self->{components}->{cpu_subsystem}->check();
  $self->{components}->{cpu_subsystem}->dump()
      if $self->opts->verbose >= 2;
}

sub check_mem_subsystem {
  my $self = shift;
  $self->{components}->{mem_subsystem}->check();
  $self->{components}->{mem_subsystem}->dump()
      if $self->opts->verbose >= 2;
}


package NWC::HP;

use strict;

use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

our @ISA = qw(NWC::Device);

use constant trees => (
    '1.3.6.1.4.1.11.2.14.11.1.2', # HP-ICF-CHASSIS
    '1.3.6.1.2.1.1.7.11.12.9', # STATISTICS-MIB (old?)
    '1.3.6.1.2.1.1.7.11.12.1', # NETSWITCH-MIB (old?)
    '1.3.6.1.4.1.11.2.14.11.5.1.9', # STATISTICS-MIB
    '1.3.6.1.4.1.11.2.14.11.5.1.1', # NETSWITCH-MIB

);

sub init {
  my $self = shift;
  if ($self->{productname} =~ /Procurve/i) {
    bless $self, 'NWC::HP::Procurve';
    $self->debug('using NWC::HP::Procurve');
  }
  $self->init();
}

package NWC::MEOS;

use strict;

use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

our @ISA = qw(NWC::Brocade);

sub init {
  my $self = shift;
  $self->{components} = {
      powersupply_subsystem => undef,
      fan_subsystem => undef,
      temperature_subsystem => undef,
      cpu_subsystem => undef,
  };
  $self->{serial} = 'unknown';
  $self->{product} = 'unknown';
  $self->{romversion} = 'unknown';
  # serial is 1.3.6.1.2.1.47.1.1.1.1.11.1
  #$self->collect();
  if (! $self->check_messages()) {
    if ($self->mode =~ /device::hardware::health/) {
      $self->analyze_environmental_subsystem();
      $self->check_environmental_subsystem();
    } elsif ($self->mode =~ /device::hardware::load/) {
      $self->analyze_cpu_subsystem();
      $self->check_cpu_subsystem();
    } elsif ($self->mode =~ /device::hardware::memory/) {
      $self->analyze_mem_subsystem();
      $self->check_mem_subsystem();
    } elsif ($self->mode =~ /device::interfaces/) {
      $self->analyze_interface_subsystem();
      $self->check_interface_subsystem();
    }
  }
}

sub analyze_environmental_subsystem {
  my $self = shift;
  $self->{components}->{environmental_subsystem1} =
      NWC::FCMGMT::Component::EnvironmentalSubsystem->new();
  $self->{components}->{environmental_subsystem2} =
      NWC::FCEOS::Component::EnvironmentalSubsystem->new();
}

sub analyze_cpu_subsystem {
  my $self = shift;
  $self->no_such_mode();
  $self->{components}->{cpu_subsystem} =
      NWC::UCDMIB::Component::CpuSubsystem->new();
}

sub analyze_mem_subsystem {
  my $self = shift;
  $self->no_such_mode();
  $self->{components}->{mem_subsystem} =
      NWC::UCDMIB::Component::MemSubsystem->new();
}

sub analyze_interface_subsystem {
  my $self = shift;
  $self->{components}->{interface_subsystem} =
      NWC::IFMIB::Component::InterfaceSubsystem->new();
}

sub check_environmental_subsystem {
  my $self = shift;
  $self->{components}->{environmental_subsystem1}->check();
  $self->{components}->{environmental_subsystem2}->check();
  if ($self->check_messages()) {
    $self->clear_messages(OK);
  }
  $self->{components}->{environmental_subsystem1}->dump()
      if $self->opts->verbose >= 2;
  $self->{components}->{environmental_subsystem2}->dump()
      if $self->opts->verbose >= 2;
}

sub check_cpu_subsystem {
  my $self = shift;
  $self->{components}->{cpu_subsystem}->check();
  $self->{components}->{cpu_subsystem}->dump()
      if $self->opts->verbose >= 2;
}

sub check_mem_subsystem {
  my $self = shift;
  $self->{components}->{mem_subsystem}->check();
  $self->{components}->{mem_subsystem}->dump()
      if $self->opts->verbose >= 2;
}

sub check_interface_subsystem {
  my $self = shift;
  $self->{components}->{interface_subsystem}->check();
  $self->{components}->{interface_subsystem}->dump()
      if $self->opts->verbose >= 2;
}

package NWC::Brocade;

use strict;

use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

our @ISA = qw(NWC::Device);

use constant trees => (
  '1.3.6.1.2.1',        # mib-2
  '1.3.6.1.4.1.289',    # mcData
  '1.3.6.1.4.1.333',    # cnt
  '1.3.6.1.4.1.1588',   # bcsi
  '1.3.6.1.4.1.1991',   # foundry
  '1.3.6.1.4.1.4369',   # nishan
);

sub init {
  my $self = shift;
  foreach ($self->get_snmp_table_objects(
      'ENTITY-MIB', 'entPhysicalTable')) {
    if ($_->{entPhysicalDescr} =~ /Brocade/) {
      $self->{productname} = "FabOS";
    }
  }
  my $swFirmwareVersion = $self->get_snmp_object('SW-MIB', 'swFirmwareVersion');
  if ($swFirmwareVersion && $swFirmwareVersion =~ /^v6/) {
    $self->{productname} = "FabOS"
  }
  if ($self->{productname} =~ /EMC\s*DS.*4700M/i) {
    bless $self, 'NWC::MEOS';
    $self->debug('using NWC::MEOS');
    $self->init();
  } elsif ($self->{productname} =~ /EMC\s*DS-24M2/i) {
    bless $self, 'NWC::MEOS';
    $self->debug('using NWC::MEOS');
    $self->init();
  } elsif ($self->{productname} =~ /FabOS/i) {
    bless $self, 'NWC::FabOS';
    $self->debug('using NWC::FabOS');
    $self->init();
  }
}

package NWC::SecureOS;

use strict;

use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

our @ISA = qw(NWC::Device);

sub init {
  my $self = shift;
  $self->{components} = {
      mem_subsystem => undef,
      cpu_subsystem => undef,
  };
  $self->{serial} = 'unknown';
  $self->{product} = 'unknown';
  $self->{romversion} = 'unknown';
  # serial is 1.3.6.1.2.1.47.1.1.1.1.11.1
  #$self->collect();
  if (! $self->check_messages()) {
    if ($self->mode =~ /device::hardware::health/) {
      $self->analyze_environmental_subsystem();
      $self->check_environmental_subsystem();
    } elsif ($self->mode =~ /device::hardware::load/) {
      $self->analyze_cpu_subsystem();
      $self->check_cpu_subsystem();
    } elsif ($self->mode =~ /device::hardware::memory/) {
      $self->analyze_mem_subsystem();
      $self->check_mem_subsystem();
    } elsif ($self->mode =~ /device::interfaces/) {
      $self->analyze_interface_subsystem();
      $self->check_interface_subsystem();
    }
  }
}

sub analyze_environmental_subsystem {
  my $self = shift;
  $self->no_such_mode();
  $self->{components}->{environmental_subsystem} =
      #NWC::SecureOS::Component::EnvironmentalSubsystem->new();
      NWC::FCMGMT::Component::EnvironmentalSubsystem->new();
}

sub analyze_cpu_subsystem {
  my $self = shift;
  $self->{components}->{cpu_subsystem} =
      NWC::UCDMIB::Component::CpuSubsystem->new();
}

sub analyze_mem_subsystem {
  my $self = shift;
  $self->{components}->{mem_subsystem} =
      NWC::UCDMIB::Component::MemSubsystem->new();
}

sub analyze_interface_subsystem {
  my $self = shift;
  $self->{components}->{interface_subsystem} =
      NWC::IFMIB::Component::InterfaceSubsystem->new();
}

sub check_environmental_subsystem {
  my $self = shift;
  $self->{components}->{environmental_subsystem}->check();
  $self->{components}->{environmental_subsystem}->dump()
      if $self->opts->verbose >= 2;
}

sub check_cpu_subsystem {
  my $self = shift;
  $self->{components}->{cpu_subsystem}->check();
  $self->{components}->{cpu_subsystem}->dump()
      if $self->opts->verbose >= 2;
}

sub check_mem_subsystem {
  my $self = shift;
  $self->{components}->{mem_subsystem}->check();
  $self->{components}->{mem_subsystem}->dump()
      if $self->opts->verbose >= 2;
}

sub check_interface_subsystem {
  my $self = shift;
  $self->{components}->{interface_subsystem}->check();
  $self->{components}->{interface_subsystem}->dump()
      if $self->opts->verbose >= 2;
}

package NWC::HSRP::Component::HSRPSubsystem;
our @ISA = qw(NWC::HSRP);

use strict;
use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    groups => [],
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  bless $self, $class;
  $self->init(%params);
  return $self;
}

sub init {
  my $self = shift;
  my %params = @_;
  if ($self->mode =~ /device::hsrp/) {
    foreach ($self->get_snmp_table_objects(
        'CISCO-HSRP-MIB', 'cHsrpGrpTable')) {
      push(@{$self->{groups}},
          NWC::HSRP::Component::HSRPSubsystem::Group->new(%{$_}));
    }
  }
}

sub check {
  my $self = shift;
  my $errorfound = 0;
  $self->add_info('checking hsrp groups');
  $self->blacklist('hhsrp', '');
  if ($self->mode =~ /device::hsrp::list/) {
    foreach (@{$self->{groups}}) {
      $_->list();
    }
  } elsif ($self->mode =~ /device::hsrp/) {
    if (scalar (@{$self->{groups}}) == 0) {
      $self->add_message(UNKNOWN, 'no hsrp groups');
    } else {
      foreach (@{$self->{groups}}) {
        $_->check();
      }
    }
  }
}

sub dump {
  my $self = shift;
  foreach (@{$self->{groups}}) {
    $_->dump();
  }
}


package NWC::HSRP::Component::HSRPSubsystem::Group;
our @ISA = qw(NWC::HSRP::Component::HSRPSubsystem);

use strict;
use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  bless $self, $class;
  foreach ($self->get_snmp_table_attributes(
      'CISCO-HSRP-MIB', 'cHsrpGrpTable')) {
    $self->{$_} = $params{$_};
  }
  $self->{ifIndex} = $params{indices}->[0];
  $self->{cHsrpGrpNumber} = $params{indices}->[1];
  $self->{name} = $self->{cHsrpGrpNumber}.':'.$self->{ifIndex};
  foreach my $key (keys %params) {
    $self->{$key} = 0 if ! defined $params{$key};
  }
  $self->init();
  return $self;
}

sub init {
  my $self = shift;
  if ($self->mode =~ /device::hsrp::state/) {
    if (! $self->opts->role()) {
      $self->opts->override_opt('role', 'active');
    }
  }
  return $self;
}

sub check {
  my $self = shift;
  $self->blacklist('hsrp', $self->{name});
  if ($self->mode =~ /device::hsrp::state/) {
    my $info = sprintf 'hsrp group %s (interface %s) state is %s (active router is %s, standby router is %s',
        $self->{cHsrpGrpNumber}, $self->{ifIndex},
        $self->{cHsrpGrpStandbyState},
        $self->{cHsrpGrpActiveRouter}, $self->{cHsrpGrpStandbyRouter};
    $self->add_info($info);
    if ($self->opts->role() eq $self->{cHsrpGrpStandbyState}) {
        $self->add_message(OK, $info);
    } else {
      $self->add_message(CRITICAL, 
          sprintf 'state in group %s (interface %s) is %s instead of %s',
              $self->{cHsrpGrpNumber}, $self->{ifIndex},
              $self->{cHsrpGrpStandbyState},
              $self->opts->role());
    }
  } elsif ($self->mode =~ /device::hsrp::failover/) {
    my $info = sprintf 'hsrp group %s/%s: active node is %s, standby node is %s',
        $self->{cHsrpGrpNumber}, $self->{ifIndex},
        $self->{cHsrpGrpActiveRouter}, $self->{cHsrpGrpStandbyRouter};
    if (my $laststate = $self->load_state( name => $self->{name} )) {
      if ($laststate->{active} ne $self->{cHsrpGrpActiveRouter}) {
        $self->add_message(CRITICAL, sprintf 'hsrp group %s/%s: active node %s --> %s',
            $self->{cHsrpGrpNumber}, $self->{ifIndex},
            $laststate->{active}, $self->{cHsrpGrpActiveRouter});
      }
      if ($laststate->{standby} ne $self->{cHsrpGrpStandbyRouter}) {
        $self->add_message(WARNING, sprintf 'hsrp group %s/%s: standby node %s --> %s',
            $self->{cHsrpGrpNumber}, $self->{ifIndex},
            $laststate->{standby}, $self->{cHsrpGrpStandbyRouter});
      }
      if (($laststate->{active} eq $self->{cHsrpGrpActiveRouter}) &&
          ($laststate->{standby} eq $self->{cHsrpGrpStandbyRouter})) {
        $self->add_message(OK, $info);
      }
    } else {
      $self->add_message(OK, 'initializing....');
    }
    $self->save_state( name => $self->{name}, save => {
        active => $self->{cHsrpGrpActiveRouter},
        standby => $self->{cHsrpGrpStandbyRouter},
    });
  }
}

sub list {
  my $self = shift;
  printf "%s %s %s %s\n", $self->{name}, $self->{cHsrpGrpVirtualIpAddr},
      $self->{cHsrpGrpActiveRouter}, $self->{cHsrpGrpStandbyRouter};
}

sub dump {
  my $self = shift;
  printf "[HSRPGRP_%s]\n", $self->{name};
  foreach (qw(cHsrpGrpNumber cHsrpGrpVirtualIpAddr cHsrpGrpStandbyState cHsrpGrpActiveRouter cHsrpGrpStandbyRouter cHsrpGrpEntryRowStatus)) {
    printf "%s: %s\n", $_, defined $self->{$_} ? $self->{$_} : 'undefined';
  }
#  printf "info: %s\n", $self->{info};
  printf "\n";
}

package NWC::HSRP;

use strict;

use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

our @ISA = qw(NWC::Device);

package NWC::IFMIB::Component::InterfaceSubsystem;
our @ISA = qw(NWC::IFMIB);

use strict;
use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    interface_cache => {},
    interfaces => [],
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  bless $self, $class;
  $self->init(%params);
  return $self;
}

sub init {
  my $self = shift;
  my %params = @_;
  if ($self->mode =~ /device::interfaces::list/) {
    $self->update_interface_cache(1);
    foreach my $ifidxdescr (keys %{$self->{interface_cache}}) {
      my ($ifIndex, $ifDescr) = split('#', $ifidxdescr, 2);
      push(@{$self->{interfaces}},
          NWC::IFMIB::Component::InterfaceSubsystem::Interface->new(
              #ifIndex => $self->{interface_cache}->{$ifDescr},
              #ifDescr => $ifDescr,
              ifIndex => $ifIndex,
              ifDescr => $ifDescr,
          ));
    }
  } else {
    $self->update_interface_cache(0);
    #next if $self->opts->can('name') && $self->opts->name && 
    #    $self->opts->name ne $_->{ifDescr};
    # if limited search
    # name is a number -> get_table with extra param
    # name is a regexp -> list of names -> list of numbers
    my @indices = $self->get_interface_indices();
    if (scalar(@indices) > 0) {
      foreach ($self->get_snmp_table_objects(
          'IFMIB', 'ifTable+ifXTable', \@indices)) {
        push(@{$self->{interfaces}},
            NWC::IFMIB::Component::InterfaceSubsystem::Interface->new(%{$_}));
      }
    }
  }
}

sub check {
  my $self = shift;
  my $errorfound = 0;
  $self->add_info('checking interfaces');
  $self->blacklist('ff', '');
  if (scalar(@{$self->{interfaces}}) == 0) {
    $self->add_message(UNKNOWN, 'no interfaces');
    return;
  }
  if ($self->mode =~ /device::interfaces::list/) {
    foreach (sort {$a->{ifIndex} <=> $b->{ifIndex}} @{$self->{interfaces}}) {
    #foreach (sort @{$self->{interfaces}}) {
      $_->list();
    }
  } else {
    if (scalar (@{$self->{interfaces}}) == 0) {
    } else {
      my $unique = {};
      foreach (@{$self->{interfaces}}) {
        if (exists $unique->{$_->{ifDescr}}) {
          $unique->{$_->{ifDescr}}++;
        } else {
          $unique->{$_->{ifDescr}} = 0;
        }
      }
      foreach (sort {$a->{ifIndex} <=> $b->{ifIndex}} @{$self->{interfaces}}) {
        if ($unique->{$_->{ifDescr}}) {
          $_->{ifDescr} .= ' '.$_->{ifIndex};
        }
        $_->check();
      }
    }
  }
}

sub update_interface_cache {
  my $self = shift;
  my $force = shift;
  my $statefile = lc sprintf "%s/%s_interface_cache",
      $NWC::Device::statefilesdir, $self->opts->hostname;
  my $update = time - 3600;
  if ($force || ! -f $statefile || ((stat $statefile)[9]) < ($update)) {
    $self->debug('force update of interface cache');
    $self->{interface_cache} = {};
    foreach ($self->get_snmp_table_objects( 'IFMIB', 'ifTable')) {
      # neuerdings index+descr, weil die drecksscheiss allied telesyn ports
      # alle gleich heissen
      $self->{interface_cache}->{$_->{ifIndex}.'#'.$_->{ifDescr}} =
          $_->{ifIndex};
    }
    $self->save_interface_cache();
  }
  $self->load_interface_cache();
}

sub save_interface_cache {
  my $self = shift;
  $self->create_statefilesdir();
  my $statefile = lc sprintf "%s/%s_interface_cache",
      $NWC::Device::statefilesdir, $self->opts->hostname;
  open(STATE, ">$statefile");
############################
# printf ohne %s ????
############################
  printf STATE Data::Dumper::Dumper($self->{interface_cache});
  close STATE;
  $self->debug(sprintf "saved %s to %s",
      Data::Dumper::Dumper($self->{interface_cache}), $statefile);
}

sub load_interface_cache {
  my $self = shift;
  my $statefile = lc sprintf "%s/%s_interface_cache",
      $NWC::Device::statefilesdir, $self->opts->hostname;
  if ( -f $statefile) {
    our $VAR1;
    eval {
      require $statefile;
    };
    if($@) {
      printf "rumms\n";
    }
    $self->debug(sprintf "load %s", Data::Dumper::Dumper($VAR1));
    $self->{interface_cache} = $VAR1;
  }
}

sub get_interface_indices {
  my $self = shift;
  my @indices = ();
  foreach my $ifidxdescr (keys %{$self->{interface_cache}}) {
    my ($ifindex, $ifdescr) = split('#', $ifidxdescr, 2);
    if ($self->opts->name) {
      if ($self->opts->regexp) {
        my $pattern = $self->opts->name;
        if ($ifdescr =~ /$pattern/i) {
          push(@indices, [$ifindex]);
        }
      } else {
        if ($self->opts->name =~ /^\d+$/) {
          if ($ifindex == 1 * $self->opts->name) {
            push(@indices, [1 * $self->opts->name]);
          }
        } else {
          if (lc $ifdescr eq lc $self->opts->name) {
            push(@indices, [$ifindex]);
          }
        }
      }
    } else {
      push(@indices, [$ifindex]);
    }
  }
  return @indices;
}

sub dump {
  my $self = shift;
  foreach (@{$self->{interfaces}}) {
    $_->dump();
  }
}


package NWC::IFMIB::Component::InterfaceSubsystem::Interface;
our @ISA = qw(NWC::IFMIB::Component::InterfaceSubsystem);

use strict;
use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    ifTable => $params{ifTable},
    ifEntry => $params{ifEntry},
    ifIndex => $params{ifIndex},
    ifDescr => $params{ifDescr},
    ifType => $params{ifType},
    ifMtu => $params{ifMtu},
    ifSpeed => $params{ifSpeed},
    ifPhysAddress => $params{ifPhysAddress},
    ifAdminStatus => $params{ifAdminStatus},
    ifOperStatus => $params{ifOperStatus},
    ifLastChange => $params{ifLastChange},
    ifInOctets => $params{ifInOctets},
    ifInUcastPkts => $params{ifInUcastPkts},
    ifInNUcastPkts => $params{ifInNUcastPkts},
    ifInDiscards => $params{ifInDiscards},
    ifInErrors => $params{ifInErrors},
    ifInUnknownProtos => $params{ifInUnknownProtos},
    ifOutOctets => $params{ifOutOctets},
    ifOutUcastPkts => $params{ifOutUcastPkts},
    ifOutNUcastPkts => $params{ifOutNUcastPkts},
    ifOutDiscards => $params{ifOutDiscards},
    ifOutErrors => $params{ifOutErrors},
    ifOutQLen => $params{ifOutQLen},
    ifSpecific => $params{ifSpecific},
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  foreach my $key (keys %{$self}) {
    next if $key !~ /^if/;
    $self->{$key} = 0 if ! defined $params{$key};
  }
  bless $self, $class;
  #if (0) {
  if ($params{ifName}) {
    my $self64 = {
      ifName => $params{ifName},
      ifInMulticastPkts => $params{ifInMulticastPkts},
      ifInBroadcastPkts => $params{ifInBroadcastPkts},
      ifOutMulticastPkts => $params{ifOutMulticastPkts},
      ifOutBroadcastPkts => $params{ifOutBroadcastPkts},
      ifHCInOctets => $params{ifHCInOctets},
      ifHCInUcastPkts => $params{ifHCInUcastPkts},
      ifHCInMulticastPkts => $params{ifHCInMulticastPkts},
      ifHCInBroadcastPkts => $params{ifHCInBroadcastPkts},
      ifHCOutOctets => $params{ifHCOutOctets},
      ifHCOutUcastPkts => $params{ifHCOutUcastPkts},
      ifHCOutMulticastPkts => $params{ifHCOutMulticastPkts},
      ifHCOutBroadcastPkts => $params{ifHCOutBroadcastPkts},
      ifLinkUpDownTrapEnable => $params{ifLinkUpDownTrapEnable},
      ifHighSpeed => $params{ifHighSpeed},
      ifPromiscuousMode => $params{ifPromiscuousMode},
      ifConnectorPresent => $params{ifConnectorPresent},
      ifAlias => $params{ifAlias},
      ifCounterDiscontinuityTime => $params{ifCounterDiscontinuityTime},
    };
    map { $self->{$_} = $self64->{$_} } keys %{$self64};
    bless $self, 'NWC::IFMIB::Component::InterfaceSubsystem::Interface::64bit';
  }
  $self->init();
  return $self;
}

sub init {
  my $self = shift;
  if ($self->mode =~ /device::interfaces::traffic/) {
    $self->valdiff({name => $self->{ifDescr}}, qw(ifInOctets ifInUcastPkts ifInNUcastPkts ifInDiscards ifInErrors ifInUnknownProtos ifOutOctets ifOutUcastPkts ifOutNUcastPkts ifOutDiscards ifOutErrors));
  } elsif ($self->mode =~ /device::interfaces::usage/) {
    $self->valdiff({name => $self->{ifIndex}.'#'.$self->{ifDescr}}, qw(ifInOctets ifOutOctets));
    if ($self->{ifSpeed} == 0) {
      # vlan graffl
      $self->{inputUtilization} = 0;
      $self->{outputUtilization} = 0;
    } else {
      $self->{inputUtilization} = $self->{delta_ifInOctets} * 8 * 100 /
          ($self->{delta_timestamp} * $self->{ifSpeed});
      $self->{outputUtilization} = $self->{delta_ifOutOctets} * 8 * 100 /
          ($self->{delta_timestamp} * $self->{ifSpeed});
    }
    if (defined $self->opts->ifspeedin) {
      $self->{inputUtilization} = $self->{delta_ifInOctets} * 8 * 100 /
          ($self->{delta_timestamp} * $self->opts->ifspeedin);
    }
    if (defined $self->opts->ifspeedout) {
      $self->{outputUtilization} = $self->{delta_ifOutOctets} * 8 * 100 /
          ($self->{delta_timestamp} * $self->opts->ifspeedout);
    }
    if (defined $self->opts->ifspeed) {
      $self->{inputUtilization} = $self->{delta_ifInOctets} * 8 * 100 /
          ($self->{delta_timestamp} * $self->opts->ifspeed);
      $self->{outputUtilization} = $self->{delta_ifOutOctets} * 8 * 100 /
          ($self->{delta_timestamp} * $self->opts->ifspeed);
    }
    $self->{inputRate} = $self->{delta_ifInOctets} / $self->{delta_timestamp};
    $self->{outputRate} = $self->{delta_ifOutOctets} / $self->{delta_timestamp};
    my $factor = 1/8; # default Bits
    if ($self->opts->units) {
      if ($self->opts->units eq "GB") {
        $factor = 1024 * 1024 * 1024;
      } elsif ($self->opts->units eq "MB") {
        $factor = 1024 * 1024;
      } elsif ($self->opts->units eq "KB") {
        $factor = 1024;
      } elsif ($self->opts->units eq "GBi") {
        $factor = 1024 * 1024 * 1024 / 8;
      } elsif ($self->opts->units eq "MBi") {
        $factor = 1024 * 1024 / 8;
      } elsif ($self->opts->units eq "KBi") {
        $factor = 1024 / 8;
      } elsif ($self->opts->units eq "B") {
        $factor = 1;
      } elsif ($self->opts->units eq "Bit") {
        $factor = 1/8;
      }
    }
    $self->{inputRate} /= $factor;
    $self->{outputRate} /= $factor;
  } elsif ($self->mode =~ /device::interfaces::errors/) {
    $self->valdiff({name => $self->{ifDescr}}, qw(ifInErrors ifOutErrors ifInDiscards ifOutDiscards));
    $self->{inputErrorRate} = $self->{delta_ifInErrors} 
        / $self->{delta_timestamp};
    $self->{outputErrorRate} = $self->{delta_ifOutErrors} 
        / $self->{delta_timestamp};
    $self->{inputDiscardRate} = $self->{delta_ifInDiscards} 
        / $self->{delta_timestamp};
    $self->{outputDiscardRate} = $self->{delta_ifOutDiscards} 
        / $self->{delta_timestamp};
    $self->{inputRate} = ($self->{delta_ifInErrors} + $self->{delta_ifInDiscards}) 
        / $self->{delta_timestamp};
    $self->{outputRate} = ($self->{delta_ifOutErrors} + $self->{delta_ifOutDiscards}) 
        / $self->{delta_timestamp};
  } elsif ($self->mode =~ /device::interfaces::operstatus/) {
  }
  return $self;
}

sub check {
  my $self = shift;
  $self->blacklist('if', $self->{ifIndex});
  if ($self->mode =~ /device::interfaces::traffic/) {
  } elsif ($self->mode =~ /device::interfaces::usage/) {
    my $info = sprintf 'interface %s usage is in:%.2f%% (%s) out:%.2f%% (%s)',
        $self->{ifDescr}, 
        $self->{inputUtilization}, 
        sprintf("%.2f%s/s", $self->{inputRate},
            ($self->opts->units ? $self->opts->units : 'Bits')),
        $self->{outputUtilization},
        sprintf("%.2f%s/s", $self->{outputRate},
            ($self->opts->units ? $self->opts->units : 'Bits'));
    $self->add_info($info);
    $self->set_thresholds(warning => 80, critical => 90);
    my $in = $self->check_thresholds($self->{inputUtilization});
    my $out = $self->check_thresholds($self->{outputUtilization});
    my $level = ($in > $out) ? $in : ($out > $in) ? $out : $in;
    $self->add_message($level, $info);
    $self->add_perfdata(
        label => $self->{ifDescr}.'_usage_in',
        value => $self->{inputUtilization},
        uom => '%',
        warning => $self->{warning},
        critical => $self->{critical},
    );
    $self->add_perfdata(
        label => $self->{ifDescr}.'_usage_out',
        value => $self->{outputUtilization},
        uom => '%',
        warning => $self->{warning},
        critical => $self->{critical},
    );
    $self->add_perfdata(
        label => $self->{ifDescr}.'_traffic_in',
        value => $self->{inputRate},
        uom => $self->opts->units,
    );
    $self->add_perfdata(
        label => $self->{ifDescr}.'_traffic_out',
        value => $self->{outputRate},
        uom => $self->opts->units,
    );
  } elsif ($self->mode =~ /device::interfaces::errors/) {
    my $info = sprintf 'interface %s errors in:%.2f/s out:%.2f/s '.
        'discards in:%.2f/s out:%.2f/s',
        $self->{ifDescr},
        $self->{inputErrorRate} , $self->{outputErrorRate},
        $self->{inputDiscardRate} , $self->{outputDiscardRate};
    $self->add_info($info);
    $self->set_thresholds(warning => 1, critical => 10);
    my $in = $self->check_thresholds($self->{inputRate});
    my $out = $self->check_thresholds($self->{outputRate});
    my $level = ($in > $out) ? $in : ($out > $in) ? $out : $in;
    $self->add_message($level, $info);
    $self->add_perfdata(
        label => $self->{ifDescr}.'_errors_in',
        value => $self->{inputErrorRate},
        warning => $self->{warning},
        critical => $self->{critical},
    );
    $self->add_perfdata(
        label => $self->{ifDescr}.'_errors_out',
        value => $self->{outputErrorRate},
        warning => $self->{warning},
        critical => $self->{critical},
    );
    $self->add_perfdata(
        label => $self->{ifDescr}.'_discards_in',
        value => $self->{inputDiscardRate},
        warning => $self->{warning},
        critical => $self->{critical},
    );
    $self->add_perfdata(
        label => $self->{ifDescr}.'_discards_out',
        value => $self->{outputDiscardRate},
        warning => $self->{warning},
        critical => $self->{critical},
    );
  } elsif ($self->mode =~ /device::interfaces::operstatus/) {
    #rfc2863
    #(1)   if ifAdminStatus is not down and ifOperStatus is down then a
    #     fault condition is presumed to exist on the interface.
    #(2)   if ifAdminStatus is down, then ifOperStatus will normally also
    #     be down (or notPresent) i.e., there is not (necessarily) a
    #     fault condition on the interface.
    # --warning onu,anu
    # Admin: admindown,admin
    # Admin: --warning 
    #        --critical admindown
    # !ad+od  ad+!(od*on)
    # warn & warnbitfield
#    if ($self->opts->critical) {
#      if ($self->opts->critical =~ /^u/) {
#      } elsif ($self->opts->critical =~ /^u/) {
#      }
#    }
#    if ($self->{ifOperStatus} ne 'up') {
#      }
#    } 
    my $info = sprintf '%s is %s/%s',
        $self->{ifDescr}, $self->{ifOperStatus}, $self->{ifAdminStatus};
    $self->add_info($info);
    $self->add_message(OK, $info);
    if ($self->{ifOperStatus} eq 'down' && $self->{ifAdminStatus} ne 'down') {
      $self->add_message(CRITICAL, 
          sprintf 'fault condition is presumed to exist on %s',
          $self->{ifDescr});
    }
  }
}

sub list {
  my $self = shift;
  if ($self->mode =~ /device::interfaces::listdetail/) {
    my $cL2L3IfModeOper = $self->get_snmp_object('CISCO-L2L3-INTERFACE-CONFIG-MIB', 'cL2L3IfModeOper', $self->{ifIndex}) || "unknown";
    my $vlanTrunkPortDynamicStatus = $self->get_snmp_object('CISCO-VTP-MIB', 'vlanTrunkPortDynamicStatus', $self->{ifIndex}) || "unknown";
    printf "%06d %s %s %s\n", $self->{ifIndex}, $self->{ifDescr},
        $cL2L3IfModeOper, $vlanTrunkPortDynamicStatus;
  } else {
    printf "%06d %s\n", $self->{ifIndex}, $self->{ifDescr};
  }
}

sub dump {
  my $self = shift;
  printf "[IF32_%s]\n", $self->{ifIndex};
  foreach (qw(ifIndex ifDescr ifType ifMtu ifSpeed ifPhysAddress ifAdminStatus ifOperStatus ifLastChange ifInOctets ifInUcastPkts ifInNUcastPkts ifInDiscards ifInErrors ifInUnknownProtos ifOutOctets ifOutUcastPkts ifOutNUcastPkts ifOutDiscards ifOutErrors ifOutQLen ifSpecific)) {
    printf "%s: %s\n", $_, defined $self->{$_} ? $self->{$_} : 'undefined';
  }
#  printf "info: %s\n", $self->{info};
  printf "\n";
}

package NWC::IFMIB::Component::InterfaceSubsystem::Interface::64bit;
our @ISA = qw(NWC::IFMIB::Component::InterfaceSubsystem::Interface);

use strict;
use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

sub dump {
  my $self = shift;
  printf "[IF64_%s]\n", $self->{ifIndex};
  foreach (qw(ifIndex ifDescr ifType ifMtu ifSpeed ifPhysAddress ifAdminStatus ifOperStatus ifLastChange ifInOctets ifInUcastPkts ifInNUcastPkts ifInDiscards ifInErrors ifInUnknownProtos ifOutOctets ifOutUcastPkts ifOutNUcastPkts ifOutDiscards ifOutErrors ifOutQLen ifSpecific ifName ifInMulticastPkts ifInBroadcastPkts ifOutMulticastPkts ifOutBroadcastPkts ifHCInOctets ifHCInUcastPkts ifHCInMulticastPkts ifHCInBroadcastPkts ifHCOutOctets ifHCOutUcastPkts ifHCOutMulticastPkts ifHCOutBroadcastPkts ifLinkUpDownTrapEnable ifHighSpeed ifPromiscuousMode ifConnectorPresent ifAlias ifCounterDiscontinuityTime)) {
    printf "%s: %s\n", $_, defined $self->{$_} ? $self->{$_} : 'undefined';
  }
#  printf "info: %s\n", $self->{info};
  printf "\n";
}


package NWC::IFMIB;

use strict;

use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

our @ISA = qw(NWC::Device);

package NWC::FCMGMT::Component::EnvironmentalSubsystem;
our @ISA = qw(NWC::FCMGMT);

use strict;
use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    runtime => $params{runtime},
    rawdata => $params{rawdata},
    method => $params{method},
    condition => $params{condition},
    status => $params{status},
    sensor_subsystem => undef,
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  bless $self, $class;
  $self->init(%params);
  return $self;
}

sub init {
  my $self = shift;
  my %params = @_;
  $self->{sensor_subsystem} =
      NWC::FCMGMT::Component::SensorSubsystem->new(%params);
}

sub check {
  my $self = shift;
  my $errorfound = 0;
  $self->{sensor_subsystem}->check();
  if (! $self->check_messages()) {
    $self->add_message(OK, "environmental hardware working fine");
  }
}

sub dump {
  my $self = shift;
  $self->{sensor_subsystem}->dump();
}


package NWC::FCMGMT::Component::SensorSubsystem;
our @ISA = qw(NWC::FCMGMT::Component::EnvironmentalSubsystem);

use strict;
use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    sensors => [],
    sensorthresholds => [],
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  bless $self, $class;
  $self->init(%params);
  return $self;
}

sub init {
  my $self = shift;
  my $sensors = {};
  foreach ($self->get_snmp_table_objects(
      'FCMGMT-MIB', 'fcConnUnitSensorTable')) {
    $_->{fcConnUnitSensorIndex} ||= $_->{indices}->[-1];
    push(@{$self->{sensors}}, 
        NWC::FCMGMT::Component::SensorSubsystem::Sensor->new(%{$_}));
  }
}

sub check {
  my $self = shift;
  my $errorfound = 0;
  $self->add_info('checking sensors');
  $self->blacklist('ses', '');
  if (scalar (@{$self->{sensors}}) == 0) {
  } else {
    foreach (@{$self->{sensors}}) {
      $_->check();
    }
  }
}


sub dump {
  my $self = shift;
  foreach (@{$self->{sensors}}) {
    $_->dump();
  }
}


package NWC::FCMGMT::Component::SensorSubsystem::Sensor;
our @ISA = qw(NWC::FCMGMT::Component::SensorSubsystem);

use strict;
use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  foreach my $param (qw(fcConnUnitSensorIndex fcConnUnitSensorName
      fcConnUnitSensorStatus fcConnUnitSensorStatus
      fcConnUnitSensorType fcConnUnitSensorCharacteristic
      fcConnUnitSensorInfo fcConnUnitSensorMessage)) {
    $self->{$param} = $params{$param};
  }
  bless $self, $class;
  return $self;
}

sub check {
  my $self = shift;
  $self->blacklist('se', $self->{swSensorIndex});
  $self->add_info(sprintf '%s sensor %s (%s) is %s (%s)',
      $self->{fcConnUnitSensorType},
      $self->{fcConnUnitSensorIndex},
      $self->{fcConnUnitSensorInfo},
      $self->{fcConnUnitSensorStatus},
      $self->{fcConnUnitSensorMessage});
  if ($self->{fcConnUnitSensorStatus} ne "ok") {
    $self->add_message(CRITICAL, $self->{info});
  } else {
    #$self->add_message(OK, $self->{info});
  }
}

sub dump {
  my $self = shift;
  printf "[SENSOR_%s_%s]\n", $self->{fcConnUnitSensorType}, $self->{fcConnUnitSensorIndex};
  foreach (qw(fcConnUnitSensorIndex fcConnUnitSensorName
      fcConnUnitSensorType fcConnUnitSensorCharacteristic
      fcConnUnitSensorStatus
      fcConnUnitSensorInfo fcConnUnitSensorMessage)) {
    printf "%s: %s\n", $_, $self->{$_};
  }
  printf "info: %s\n", $self->{info};
  printf "\n";
}

package NWC::FCMGMT;

use strict;

use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

our @ISA = qw(NWC::Device);

package NWC::FCEOS::Component::EnvironmentalSubsystem;
our @ISA = qw(NWC::FCEOS);

use strict;
use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    runtime => $params{runtime},
    rawdata => $params{rawdata},
    method => $params{method},
    condition => $params{condition},
    status => $params{status},
    fru_subsystem => undef,
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  bless $self, $class;
  $self->overall_init(%params);
  $self->init(%params);
  return $self;
}


sub overall_init {
  my $self = shift;
  my %params = @_;
  $self->{oper_status} = $self->get_snmp_object('FCEOS-MIB', 'fcEosSysOperStatus');
}

sub init {
  my $self = shift;
  my %params = @_;
  $self->{fru_subsystem} =
      NWC::FCEOS::Component::FruSubsystem->new(%params);
}

sub check {
  my $self = shift;
  my $errorfound = 0;
  $self->{fru_subsystem}->check();
  if (! $self->check_messages()) {
    $self->add_message(OK, "environmental hardware working fine");
  } else {
    if ($self->{oper_status} eq "operational") {
      $self->clear_messages(CRITICAL);
      $self->clear_messages(WARNING);
    } elsif ($self->{oper_status} eq "major-failure") {
      $self->add_message(CRITICAL, "major device failure");
    } else {
      $self->add_message(WARNING, $self->{oper_status});
    }
  }
}

sub dump {
  my $self = shift;
  $self->{fru_subsystem}->dump();
}



package NWC::FCEOS::Component::FruSubsystem;
our @ISA = qw(NWC::FCEOS::Component::EnvironmentalSubsystem);

use strict;
use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    frus => [],
    thresholds => [],
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  bless $self, $class;
  $self->init(%params);
  return $self;
}

sub init {
  my $self = shift;
  my $sensors = {};
  foreach ($self->get_snmp_table_objects(
      'FCEOS-MIB', 'fcEosFruTable')) {
    push(@{$self->{frus}}, 
        NWC::FCEOS::Component::FruSubsystem::Fcu->new(%{$_}));
  }
}

sub check {
  my $self = shift;
  my $errorfound = 0;
  $self->add_info('checking frus');
  $self->blacklist('frus', '');
  if (scalar (@{$self->{frus}}) == 0) {
  } else {
    foreach (@{$self->{frus}}) {
      $_->check();
    }
  }
}


sub dump {
  my $self = shift;
  foreach (@{$self->{frus}}) {
    $_->dump();
  }
}


package NWC::FCEOS::Component::FruSubsystem::Fcu;
our @ISA = qw(NWC::FCEOS::Component::FruSubsystem);

use strict;
use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  foreach my $param (qw(fcEosFruCode fcEosFruPosition fcEosFruStatus
      fcEosFruPartNumber fcEosFruPowerOnHours)) {
    $self->{$param} = $params{$param};
  }
  bless $self, $class;
  return $self;
}

sub check {
  my $self = shift;
  $self->blacklist('fru', $self->{swSensorIndex});
  $self->add_info(sprintf '%s fru (pos %s) is %s',
      $self->{fcEosFruCode},
      $self->{fcEosFruPosition},
      $self->{fcEosFruStatus});
  if ($self->{fcEosFruStatus} eq "failed") {
    $self->add_message(CRITICAL, $self->{info});
  } else {
    #$self->add_message(OK, $self->{info});
  }
}

sub dump {
  my $self = shift;
  printf "[FRU_%s]\n", $self->{fcEosFruPosition};
  foreach (qw(fcEosFruCode fcEosFruPosition fcEosFruStatus
      fcEosFruPartNumber fcEosFruPowerOnHours)) {
    printf "%s: %s\n", $_, $self->{$_};
  }
  printf "info: %s\n", $self->{info};
  printf "\n";
}


package NWC::FCEOS;

use strict;

use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

our @ISA = qw(NWC::Device);

package NWC::UCDMIB::Component::MemSubsystem;
our @ISA = qw(NWC::UCDMIB);

use strict;
use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    runtime => $params{runtime},
    rawdata => $params{rawdata},
    mems => [],
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  bless $self, $class;
  $self->init(%params);
  return $self;
}

sub init {
  my $self = shift;
  my %params = @_;
  my $snmpwalk = $params{rawdata};
  my $ignore_redundancy = $params{ignore_redundancy};
  my $type = 0;
  foreach (qw(memTotalSwap memAvailSwap memTotalReal memAvailReal memTotalFree)) {
    $self->{$_} = $self->get_snmp_object('UCD-SNMP-MIB', $_, 0);
  }
  # https://kc.mcafee.com/corporate/index?page=content&id=KB73175
  $self->{mem_usage} = ($self->{memTotalReal} - $self->{memTotalFree}) /
      $self->{memTotalReal} * 100;
  $self->{mem_usage} = $self->{memAvailReal} * 100 / $self->{memTotalReal};
}

sub check {
  my $self = shift;
  my $errorfound = 0;
  $self->add_info('checking memory');
  $self->blacklist('m', '');
  if (defined $self->{mem_usage}) {
    my $info = sprintf 'memory usage is %.2f%%',
        $self->{mem_usage};
    $self->add_info($info);
    $self->set_thresholds(warning => 80,
        critical => 90);
    $self->add_message($self->check_thresholds($self->{mem_usage}), $info);
    $self->add_perfdata(
        label => 'memory_usage',
        value => $self->{mem_usage},
        uom => '%',
        warning => $self->{warning},
        critical => $self->{critical}
    );
  } else {
    $self->add_message(UNKNOWN, 'cannot aquire momory usage');
  }
}

sub dump {
  my $self = shift;
  printf "[MEMORY]\n";
  foreach (qw(memTotalSwap memAvailSwap memTotalReal memAvailReal memTotalFree)) {
    printf "%s: %s\n", $_, $self->{$_};
  }
  printf "info: %s\n", $self->{info};
  printf "\n";
}

package NWC::UCDMIB::Component::CpuSubsystem;
our @ISA = qw(NWC::UCDMIB);

use strict;
use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    loads => [],
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  bless $self, $class;
  $self->init(%params);
  return $self;
}

sub init {
  my $self = shift;
  my %params = @_;
  my $type = 0;
  foreach (qw(ssCpuUser ssCpuSystem ssCpuIdle
      ssCpuRawUser ssCpuRawSystem ssCpuRawIdle ssCpuRawNice)) {
    $self->{$_} = $self->get_snmp_object('UCD-SNMP-MIB', $_, 0);
  }
  $self->valdiff(\%params, qw(ssCpuRawUser ssCpuRawSystem ssCpuRawIdle ssCpuRawNice));
  my $cpu_total = $self->{delta_ssCpuRawUser} + $self->{delta_ssCpuRawSystem} +
      $self->{delta_ssCpuRawIdle} + $self->{delta_ssCpuRawNice};
  if ($cpu_total == 0) {
    $self->{cpu_usage} = 0;
  } else {
    $self->{cpu_usage} = (100 - ($self->{delta_ssCpuRawIdle} / $cpu_total) * 100);
  }
}

sub check {
  my $self = shift;
  my $errorfound = 0;
  $self->add_info('checking cpus');
  $self->blacklist('c', undef);
  my $info = sprintf 'cpu usage is %.2f%%', $self->{cpu_usage};
  $self->add_info($info);
  $self->set_thresholds(warning => 50, critical => 90);
  $self->add_message($self->check_thresholds($self->{cpu_usage}), $info);
  $self->add_perfdata(
      label => 'cpu_usage',
      value => $self->{cpu_usage},
      uom => '%',
      warning => $self->{warning},
      critical => $self->{critical},
  );
}

sub dump {
  my $self = shift;
  printf "[CPU]\n";
  foreach (qw(ssCpuUser ssCpuSystem ssCpuIdle
      ssCpuRawUser ssCpuRawSystem ssCpuRawIdle ssCpuRawNice)) {
    printf "%s: %s\n", $_, $self->{$_};
  }
  printf "info: %s\n", $self->{info};
  printf "\n";
}

sub unix_init {
  my $self = shift;
  my %params = @_;
  my $type = 0;
  foreach ($self->get_snmp_table_objects(
     'UCD-SNMP-MIB', 'laTable')) {
    push(@{$self->{loads}},
        NWC::UCDMIB::Component::CpuSubsystem::Load->new(%{$_}));
  }
}

sub unix_check {
  my $self = shift;
  my $errorfound = 0;
  $self->add_info('checking loads');
  $self->blacklist('c', '');
  foreach (@{$self->{loads}}) {
    $_->check();
  }
}

sub unix_dump {
  my $self = shift;
  foreach (@{$self->{loads}}) {
    $_->dump();
  }
}


package NWC::UCDMIB::Component::CpuSubsystem::Load;
our @ISA = qw(NWC::UCDMIB::Component::CpuSubsystem);

use strict;
use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  foreach my $param (qw(laIndex laNames laLoad laConfig laLoadFloat 
      laErrorFlag laErrMessage)) {
    $self->{$param} = $params{$param};
  }
  bless $self, $class;
  return $self;
}

sub check {
  my $self = shift;
  my $errorfound = 0;
  $self->blacklist('c', undef);
  my $info = sprintf '%s is %.2f', lc $self->{laNames}, $self->{laLoadFloat};
  $self->add_info($info);
  $self->set_thresholds(warning => $self->{laConfig},
      critical => $self->{laConfig});
  $self->add_message($self->check_thresholds($self->{laLoadFloat}), $info);
  $self->add_perfdata(
      label => lc $self->{laNames},
      value => $self->{laLoadFloat},
      warning => $self->{warning},
      critical => $self->{critical},
  );
}

sub dump {
  my $self = shift;
  printf "[LOAD_%s]\n", lc $self->{laNames};
  foreach (qw(laIndex laNames laLoad laConfig laLoadFloat 
      laErrorFlag laErrMessage)) {
    printf "%s: %s\n", $_, $self->{$_};
  }
  printf "info: %s\n", $self->{info};
  printf "\n";
}

package NWC::UCDMIB;

use strict;

use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

our @ISA = qw(NWC::Device);

package NWC::F5::F5BIGIP::Component::LTMSubsystem;
our @ISA = qw(NWC::F5::F5BIGIP);

use strict;
use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    pools => [],
    poolmembers => [],
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  #bless $self, $class;
  # tables can be huge
  if ($NWC::Device::session) {
    $NWC::Device::session->max_msg_size(10 * $NWC::Device::session->max_msg_size());
  }
  if ($params{productversion} =~ /^4/) {
    bless $self, "NWC::F5::F5BIGIP::Component::LTMSubsystem4";
    $self->debug("use NWC::F5::F5BIGIP::Component::LTMSubsystem4");
  #} elsif ($params{productversion} =~ /^9/) {
  } else {
    bless $self, "NWC::F5::F5BIGIP::Component::LTMSubsystem9";
    $self->debug("use NWC::F5::F5BIGIP::Component::LTMSubsystem9");
  }
  $self->init(%params);
  return $self;
}

sub check {
  my $self = shift;
  my $errorfound = 0;
  $self->add_info('checking ltm pools');
  $self->blacklist('poo', '');
  if (scalar(@{$self->{pools}}) == 0) {
    $self->add_message(UNKNOWN, 'no pools');
    return;
  }
  if ($self->mode =~ /pool::list/) {
    foreach (sort {$a->{name} cmp $b->{name}} @{$self->{pools}}) {
      printf "%s\n", $_->{name};
      #$_->list();
    }
  } else {
    foreach (@{$self->{pools}}) {
      $_->check();
    }
  }
}

sub dump {
  my $self = shift;
  foreach (@{$self->{pools}}) {
    $_->dump();
  }
}

package NWC::F5::F5BIGIP::Component::LTMSubsystem9;
our @ISA = qw(NWC::F5::F5BIGIP::Component::LTMSubsystem);

use strict;
use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    pools => [],
    poolmembers => [],
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  bless $self, $class;
  $self->init(%params);
  return $self;
}

sub init {
  my $self = shift;
  my %params = @_;
  # ! merge ltmPoolStatus, ltmPoolMemberStatus, bec. ltmPoolAvailabilityState is deprecated
  my @auxpools = ();
  foreach ($self->get_snmp_table_objects(
      'F5-BIGIP-LOCAL-MIB', 'ltmPoolStatusTable')) {
    push(@auxpools, $_);
  }
  foreach ($self->get_snmp_table_objects(
      'F5-BIGIP-LOCAL-MIB', 'ltmPoolTable')) {
    if ($self->filter_name($_->{ltmPoolName})) {
      foreach my $auxpool (@auxpools) {
        if ($_->{ltmPoolName} eq $auxpool->{ltmPoolStatusName}) {
          foreach my $key (keys %{$auxpool}) {
            $_->{$key} = $auxpool->{$key};
          }
        }
      }
      push(@{$self->{pools}},
          NWC::F5::F5BIGIP::Component::LTMSubsystem9::LTMPool->new(%{$_}));
    }
  }
  my @auxmembers = ();
  foreach ($self->get_snmp_table_objects(
      'F5-BIGIP-LOCAL-MIB', 'ltmPoolMbrStatusTable')) {
    push(@auxmembers, $_);
  }
  my @auxaddrs = ();
  foreach ($self->get_snmp_table_objects(
      'F5-BIGIP-LOCAL-MIB', 'ltmNodeAddrStatusTable')) {
    push(@auxaddrs, $_);
  }
  foreach ($self->get_snmp_table_objects(
      'F5-BIGIP-LOCAL-MIB', 'ltmPoolMemberTable')) {
    if ($self->filter_name($_->{ltmPoolMemberPoolName})) {
      foreach my $auxmember (@auxmembers) {
        if ($_->{ltmPoolMemberPoolName} eq $auxmember->{ltmPoolMbrStatusPoolName} &&
            $_->{ltmPoolMemberAddrType} eq $auxmember->{ltmPoolMbrStatusAddrType} &&
            $_->{ltmPoolMemberAddr} eq $auxmember->{ltmPoolMbrStatusAddr}) {
          foreach my $key (keys %{$auxmember}) {
            $_->{$key} = $auxmember->{$key};
          }
        }
      }
      foreach my $auxaddr (@auxaddrs) {
        if ($_->{ltmPoolMemberAddrType} eq $auxaddr->{ltmNodeAddrStatusAddrType} &&
            $_->{ltmPoolMemberAddr} eq $auxaddr->{ltmNodeAddrStatusAddr}) {
          $_->{ltmNodeAddrStatusName} = $auxaddr->{ltmNodeAddrStatusName};
        }
      }
      push(@{$self->{poolmembers}},
          NWC::F5::F5BIGIP::Component::LTMSubsystem9::LTMPoolMember->new(%{$_}));
    }
  }
  $self->assign_members_to_pools();
}

sub assign_members_to_pools {
  my $self = shift;
  foreach my $pool (@{$self->{pools}}) {
    foreach my $poolmember (@{$self->{poolmembers}}) {
      if ($poolmember->{ltmPoolMemberPoolName} eq $pool->{ltmPoolName}) {
        $poolmember->{ltmPoolMonitorRule} = $pool->{ltmPoolMonitorRule};
        push(@{$pool->{members}}, $poolmember);
      }
    }
    if (! defined $pool->{ltmPoolMemberCnt}) {
      $pool->{ltmPoolMemberCnt} = scalar(@{$pool->{members}}) ;
      $self->debug("calculate ltmPoolMemberCnt");
    }
    $pool->{completeness} = $pool->{ltmPoolMemberCnt} ?
        $pool->{ltmPoolActiveMemberCnt} / $pool->{ltmPoolMemberCnt} * 100
        : 0;
  }
}


package NWC::F5::F5BIGIP::Component::LTMSubsystem9::LTMPool;
our @ISA = qw(NWC::F5::F5BIGIP::Component::LTMSubsystem9);

use strict;
use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
    members => [],
  };
  foreach(keys %params) {
    $self->{$_} = $params{$_};
  }
  $self->{ltmPoolMemberMonitorRule} ||= $self->{ltmPoolMonitorRule};
  $self->{name} = $self->{ltmPoolName};
  bless $self, $class;
  return $self;
}

sub check {
  my $self = shift;
  my %params = @_;
  $self->blacklist('po', $self->{ltmPoolName});
  $self->add_info(sprintf "pool %s is %s, avail state is %s, active members: %d of %d", 
      $self->{ltmPoolName},
      $self->{ltmPoolStatusEnabledState}, $self->{ltmPoolStatusAvailState},
      $self->{ltmPoolActiveMemberCnt}, $self->{ltmPoolMemberCnt});
  if ($self->{ltmPoolActiveMemberCnt} == 1) {
    # only one member left = no more redundancy!!
    $self->set_thresholds(warning => "100:", critical => "51:");
  } else {
    $self->set_thresholds(warning => "51:", critical => "26:");
  }
  $self->add_message($self->check_thresholds($self->{completeness}),
      sprintf ("pool %s has %d active members (of %d)",
          $self->{ltmPoolName},
          $self->{ltmPoolActiveMemberCnt}, $self->{ltmPoolMemberCnt}));
  if ($self->{ltmPoolMinActiveMembers} > 0 &&
      $self->{ltmPoolActiveMemberCnt} < $self->{ltmPoolMinActiveMembers}) {
    $self->add_message(
        defined $params{mitigation} ? $params{mitigation} : 2,
        sprintf("pool %s has not enough active members (%d, min is %d)", 
            $self->{ltmPoolName}, $self->{ltmPoolActiveMemberCnt}, 
            $self->{ltmPoolMinActiveMembers})
    );
  }
  if ($self->check_messages()) {
  foreach my $member (@{$self->{members}}) {
    $member->check();
  }
  }
  $self->add_perfdata(
      label => sprintf('pool_%s_completeness', $self->{ltmPoolName}),
      value => $self->{completeness},
      uom => '%',
      warning => $self->{warning},
      critical => $self->{critical},
  );
}

sub dump { 
  my $self = shift;
  printf "[POOL_%s]\n", $self->{ltmPoolName};
  foreach(qw(ltmPoolName ltmPoolLbMode ltmPoolMinActiveMembers
      ltmPoolActiveMemberCnt ltmPoolMemberCnt
      ltmPoolStatusAvailState ltmPoolStatusEnabledState ltmPoolStatusDetailReason)) {
    printf "%s: %s\n", $_, $self->{$_};
  }
  foreach my $member (@{$self->{members}}) {
    $member->dump();
  }
  printf "info: %s\n", $self->{info};
  printf "\n";
}


package NWC::F5::F5BIGIP::Component::LTMSubsystem9::LTMPoolMember;
our @ISA = qw(NWC::F5::F5BIGIP::Component::LTMSubsystem9);

use strict;
use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  foreach(keys %params) {
    $self->{$_} = $params{$_};
  }
  $self->{ltmPoolMemberAddr} =~ s/ //g;
  if ($self->{ltmPoolMemberAddr} =~ /^0x([0-9a-zA-Z]{8})/) {
    $self->{ltmPoolMemberAddr} = join(".", unpack "C*", pack "H*", $1);
  }
  $self->{ltmPoolMemberNodeName} ||= $self->{ltmPoolMemberAddr};
  if ($self->{ltmPoolMemberNodeName} eq $self->{ltmPoolMemberAddr} &&
      $self->{ltmNodeAddrStatusName}) {
    $self->{ltmPoolMemberNodeName} = $self->{ltmNodeAddrStatusName};
  }
  bless $self, $class;
  return $self;
}

sub check {
  my $self = shift;
  if ($self->{ltmPoolMbrStatusEnabledState} eq "enabled") {
    if ($self->{ltmPoolMbrStatusAvailState} ne "green") {
      $self->add_message(CRITICAL, sprintf
          "member %s is %s/%s (%s)",
          $self->{ltmPoolMemberNodeName},
          $self->{ltmPoolMemberMonitorState},
          $self->{ltmPoolMbrStatusAvailState},
          $self->{ltmPoolMbrStatusDetailReason});
    }
  }
}

sub dump { 
  my $self = shift;
  printf "[POOL_%s_MEMBER]\n", $self->{ltmPoolMemberPoolName};
  foreach(qw(ltmPoolMemberPoolName ltmPoolMemberNodeName
      ltmPoolMemberAddr ltmPoolMemberPort
      ltmPoolMemberMonitorRule
      ltmPoolMemberMonitorState ltmPoolMemberMonitorStatus
      ltmPoolMbrStatusAvailState  ltmPoolMbrStatusEnabledState ltmPoolMbrStatusDetailReason)) {
    printf "%s: %s\n", $_, $self->{$_};
  }
}


package NWC::F5::F5BIGIP::Component::LTMSubsystem4;
our @ISA = qw(NWC::F5::F5BIGIP::Component::LTMSubsystem);

use strict;
use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    pools => [],
    poolmembers => [],
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  bless $self, $class;
  $self->init(%params);
  return $self;
}

sub init {
  my $self = shift;
  my %params = @_;
  foreach ($self->get_snmp_table_objects(
      'LOAD-BAL-SYSTEM-MIB', 'poolTable')) {
    if ($self->filter_name($_->{poolName})) {
      push(@{$self->{pools}},
          NWC::F5::F5BIGIP::Component::LTMSubsystem4::LTMPool->new(%{$_}));
    }
  }
  foreach ($self->get_snmp_table_objects(
      'LOAD-BAL-SYSTEM-MIB', 'poolMemberTable')) {
    if ($self->filter_name($_->{poolMemberPoolName})) {
      push(@{$self->{poolmembers}},
          NWC::F5::F5BIGIP::Component::LTMSubsystem4::LTMPoolMember->new(%{$_}));
    }
  }
  $self->assign_members_to_pools();
}

sub assign_members_to_pools {
  my $self = shift;
  foreach my $pool (@{$self->{pools}}) {
    foreach my $poolmember (@{$self->{poolmembers}}) {
      if ($poolmember->{poolMemberPoolName} eq $pool->{poolName}) {
        push(@{$pool->{members}}, $poolmember);
      }
    }
    if (! defined $pool->{poolMemberQty}) {
      $pool->{poolMemberQty} = scalar(@{$pool->{members}}) ;
      $self->debug("calculate poolMemberQty");
    }
    $pool->{completeness} = $pool->{poolMemberQty} ?
        $pool->{poolActiveMemberCount} / $pool->{poolMemberQty} * 100
        : 0;
  }
}


package NWC::F5::F5BIGIP::Component::LTMSubsystem4::LTMPool;
our @ISA = qw(NWC::F5::F5BIGIP::Component::LTMSubsystem4);

use strict;
use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
    members => [],
  };
  foreach(qw(poolName poolLBMode poolMinActiveMembers 
      poolActiveMemberCount poolMemberQty)) {
    $self->{$_} = $params{$_};
  }
  $self->{name} = $self->{poolName};
  bless $self, $class;
  return $self;
}

sub check {
  my $self = shift;
  my %params = @_;
  $self->blacklist('po', $self->{poolName});
  my $info = sprintf 'pool %s active members: %d of %d', $self->{poolName},
      $self->{poolActiveMemberCount},
      $self->{poolMemberQty};
  $self->add_info($info);
  if ($self->{poolActiveMemberCount} == 1) {
    # only one member left = no more redundancy!!
    $self->set_thresholds(warning => "100:", critical => "51:");
  } else {
    $self->set_thresholds(warning => "51:", critical => "26:");
  }
  $self->add_message($self->check_thresholds($self->{completeness}), $info);
  if ($self->{poolMinActiveMembers} > 0 &&
      $self->{poolActiveMemberCount} < $self->{poolMinActiveMembers}) {
    $self->add_nagios(
        defined $params{mitigation} ? $params{mitigation} : 2,
        sprintf("pool %s has not enough active members (%d, min is %d)", 
            $self->{poolName}, $self->{poolActiveMemberCount}, 
            $self->{poolMinActiveMembers})
    );
  }
  $self->add_perfdata(
      label => sprintf('pool_%s_completeness', $self->{poolName}),
      value => $self->{completeness},
      uom => '%',
      warning => $self->{warning},
      critical => $self->{critical},
  );
}

sub dump { 
  my $self = shift;
  printf "[POOL_%s]\n", $self->{poolName};
  foreach(qw(poolName poolLBMode poolMinActiveMembers 
      poolActiveMemberCount poolMemberQty)) {
    printf "%s: %s\n", $_, $self->{$_};
  }
  foreach my $member (@{$self->{members}}) {
    $member->dump();
  }
  printf "info: %s\n", $self->{info};
  printf "\n";
}


package NWC::F5::F5BIGIP::Component::LTMSubsystem4::LTMPoolMember;
our @ISA = qw(NWC::F5::F5BIGIP::Component::LTMSubsystem4);

use strict;
use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  foreach(qw(poolMemberPoolName poolMemberStatus)) {
    $self->{$_} = $params{$_};
  }
  bless $self, $class;
  return $self;
}

sub dump { 
  my $self = shift;
  printf "[POOL_%s_MEMBER]\n", $self->{poolMemberPoolName};
  foreach(qw(poolMemberPoolName poolMemberStatus)) {
    printf "%s: %s\n", $_, $self->{$_};
  }
}



package NWC::F5::F5BIGIP::Component::MemSubsystem;
our @ISA = qw(NWC::F5::F5BIGIP::Component::EnvironmentalSubsystem);

use strict;
use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    mems => [],
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  bless $self, $class;
  $self->overall_init(%params);
  return $self;
}

sub overall_init {
  my $self = shift;
  $self->{sysStatMemoryTotal} = $self->get_snmp_object(
      'F5-BIGIP-SYSTEM-MIB', 'sysStatMemoryTotal');
  $self->{sysStatMemoryUsed} = $self->get_snmp_object(
      'F5-BIGIP-SYSTEM-MIB', 'sysStatMemoryUsed');
  $self->{sysHostMemoryTotal} = $self->get_snmp_object(
      'F5-BIGIP-SYSTEM-MIB', 'sysHostMemoryTotal');
  $self->{sysHostMemoryUsed} = $self->get_snmp_object(
      'F5-BIGIP-SYSTEM-MIB', 'sysHostMemoryUsed');
  $self->{stat_mem_usage} = ($self->{sysStatMemoryUsed} / $self->{sysStatMemoryTotal}) * 100;
  $self->{host_mem_usage} = ($self->{sysHostMemoryUsed} / $self->{sysHostMemoryTotal}) * 100;
}

sub check {
  my $self = shift;
  my %params = @_;
  my $errorfound = 0;
  $self->add_info('checking memory');
  $self->blacklist('mm', '');
  my $info = sprintf 'tmm memory usage is %.2f%%',
      $self->{stat_mem_usage};
  $self->add_info($info);
  $self->set_thresholds(warning => 80, critical => 90);
  $self->add_message($self->check_thresholds($self->{stat_mem_usage}), $info);
  $self->add_perfdata(
      label => 'tmm_usage',
      value => $self->{stat_mem_usage},
      uom => '%',
      warning => $self->{warning},
      critical => $self->{critical},
  );
  $info = sprintf 'host memory usage is %.2f%%',
      $self->{host_mem_usage};
  $self->add_info($info);
  $self->set_thresholds(warning => 80, critical => 90);
  $self->add_message(OK, $info);
  $self->add_perfdata(
      label => 'host_usage',
      value => $self->{host_mem_usage},
      uom => '%',
      warning => $self->{warning},
      critical => $self->{critical},
  );
  return;
}

sub dump {
  my $self = shift;
  foreach (@{$self->{mems}}) {
    $_->dump();
  }
}


package NWC::F5::F5BIGIP::Component::PowersupplySubsystem;
our @ISA = qw(NWC::F5::F5BIGIP::Component::EnvironmentalSubsystem);

use strict;
use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    powersupplies => [],
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  bless $self, $class;
  $self->init(%params);
  return $self;
}

sub init {
  my $self = shift;
  foreach ($self->get_snmp_table_objects(
      'F5-BIGIP-SYSTEM-MIB', 'sysChassisPowerSupplyTable')) {
    push(@{$self->{powersupplies}},
        NWC::F5::F5BIGIP::Component::PowersupplySubsystem::Powersupply->new(%{$_}));
  }
}

sub check {
  my $self = shift;
  my $errorfound = 0;
  $self->add_info('checking powersupplies');
  $self->blacklist('pp', '');
  if (scalar (@{$self->{powersupplies}}) == 0) {
  } else {
    foreach (@{$self->{powersupplies}}) {
      $_->check();
    }
  }
}


sub dump {
  my $self = shift;
  foreach (@{$self->{powersupplies}}) {
    $_->dump();
  }
}


package NWC::F5::F5BIGIP::Component::PowersupplySubsystem::Powersupply;
our @ISA = qw(NWC::F5::F5BIGIP::Component::PowersupplySubsystem);

use strict;
use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  foreach(qw(sysChassisPowerSupplyIndex sysChassisPowerSupplyStatus)) {
    $self->{$_} = $params{$_};
  }
  bless $self, $class;
  return $self;
}

sub check {
  my $self = shift;
  $self->blacklist('p', $self->{sysChassisPowerSupplyIndex});
  $self->add_info(sprintf 'chassis powersupply %d is %s',
      $self->{sysChassisPowerSupplyIndex},
      $self->{sysChassisPowerSupplyStatus});
  if ($self->{sysChassisPowerSupplyStatus} eq 'notpresent') {
  } else {
    if ($self->{sysChassisPowerSupplyStatus} ne 'good') {
      $self->add_message(CRITICAL, $self->{info});
    }
  }
}

sub dump {
  my $self = shift;
  printf "[PS_%s]\n", $self->{sysChassisPowerSupplyIndex};
  foreach(qw(sysChassisPowerSupplyIndex sysChassisPowerSupplyStatus)) {
    printf "%s: %s\n", $_, $self->{$_};
  }
  printf "info: %s\n", $self->{info};
  printf "\n";
}

package NWC::F5::F5BIGIP::Component::TemperatureSubsystem;
our @ISA = qw(NWC::F5::F5BIGIP::Component::EnvironmentalSubsystem);

use strict;
use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    temperatures => [],
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  bless $self, $class;
  $self->init(%params);
  return $self;
}

sub init {
  my $self = shift;
  foreach ($self->get_snmp_table_objects(
      'F5-BIGIP-SYSTEM-MIB', 'sysChassisTempTable')) {
    push(@{$self->{temperatures}},
        NWC::F5::F5BIGIP::Component::TemperatureSubsystem::Temperature->new(%{$_}));
  }
}

sub check {
  my $self = shift;
  my $errorfound = 0;
  $self->add_info('checking temperatures');
  $self->blacklist('tt', '');
  if (scalar (@{$self->{temperatures}}) == 0) {
  } else {
    foreach (@{$self->{temperatures}}) {
      $_->check();
    }
  }
}


sub dump {
  my $self = shift;
  foreach (@{$self->{temperatures}}) {
    $_->dump();
  }
}


package NWC::F5::F5BIGIP::Component::TemperatureSubsystem::Temperature;
our @ISA = qw(NWC::F5::F5BIGIP::Component::TemperatureSubsystem);

use strict;
use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  foreach(qw(sysChassisTempIndex sysChassisTempTemperature)) {
    $self->{$_} = $params{$_};
  }
  bless $self, $class;
  return $self;
}

sub check {
  my $self = shift;
  $self->blacklist('t', $self->{sysChassisTempIndex});
  $self->add_info(sprintf 'chassis temperature %d is %sC',
      $self->{sysChassisTempIndex},
      $self->{sysChassisTempTemperature});
  $self->add_perfdata(
      label => sprintf('temp_%s', $self->{sysChassisTempIndex}),
      value => $self->{sysChassisTempTemperature},
      warning => undef,
      critical => undef,
  );
}

sub dump {
  my $self = shift;
  printf "[TEMP_%s]\n", $self->{sysChassisTempIndex};
  foreach(qw(sysChassisTempIndex sysChassisTempTemperature)) {
    printf "%s: %s\n", $_, $self->{$_};
  }
  printf "info: %s\n", $self->{info};
  printf "\n";
}

package NWC::F5::F5BIGIP::Component::CpuSubsystem;
our @ISA = qw(NWC::F5::F5BIGIP::Component::EnvironmentalSubsystem);

use strict;
use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    cpus => [],
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  bless $self, $class;
  if ($self->mode =~ /load/) {
    $self->overall_init(%params);
  } else {
    $self->init(%params);
  }
  return $self;
}

sub overall_init {
  my $self = shift;
  $self->{sysStatTmTotalCycles} = $self->get_snmp_object(
      'F5-BIGIP-SYSTEM-MIB', 'sysStatTmTotalCycles');
  $self->{sysStatTmIdleCycles} = $self->get_snmp_object(
      'F5-BIGIP-SYSTEM-MIB', 'sysStatTmIdleCycles');
  $self->{sysStatTmSleepCycles} = $self->get_snmp_object(
      'F5-BIGIP-SYSTEM-MIB', 'sysStatTmSleepCycles');
  $self->valdiff({name => 'cpu'}, qw(sysStatTmTotalCycles sysStatTmIdleCycles sysStatTmSleepCycles ));
  my $delta_used_cycles = $self->{delta_sysStatTmTotalCycles} -
     ($self->{delta_sysStatTmIdleCycles} + $self->{delta_sysStatTmSleepCycles});
  $self->{cpu_usage} =  $self->{delta_sysStatTmTotalCycles} ?
      (($delta_used_cycles / $self->{delta_sysStatTmTotalCycles}) * 100) : 0;
}

sub init {
  my $self = shift;
  foreach ($self->get_snmp_table_objects(
      'F5-BIGIP-SYSTEM-MIB', 'sysCpuTable')) {
    push(@{$self->{cpus}},
        NWC::F5::F5BIGIP::Component::CpuSubsystem::Cpu->new(%{$_}));
  }
}

sub check {
  my $self = shift;
  my %params = @_;
  my $errorfound = 0;
  $self->add_info('checking cpus');
  $self->blacklist('cc', '');
  if ($self->mode =~ /load/) {
    my $info = sprintf 'tmm cpu usage is %.2f%%',
        $self->{cpu_usage};
    $self->add_info($info);
    $self->set_thresholds(warning => 80, critical => 90);
    $self->add_message($self->check_thresholds($self->{cpu_usage}), $info);
    $self->add_perfdata(
        label => 'cpu_tmm_usage',
        value => $self->{cpu_usage},
        uom => '%',
        warning => $self->{warning},
        critical => $self->{critical},
    );
    return;
  }
  if (scalar (@{$self->{cpus}}) == 0) {
  } else {
    foreach (@{$self->{cpus}}) {
      $_->check();
    }
  }
}


sub dump {
  my $self = shift;
  foreach (@{$self->{cpus}}) {
    $_->dump();
  }
}


package NWC::F5::F5BIGIP::Component::CpuSubsystem::Cpu;
our @ISA = qw(NWC::F5::F5BIGIP::Component::CpuSubsystem);

use strict;
use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  foreach(qw(sysCpuIndex sysCpuTemperature sysCpuFanSpeed
      sysCpuName sysCpuSlot)) {
    $self->{$_} = $params{$_};
  }
  bless $self, $class;
  return $self;
}

sub check {
  my $self = shift;
  $self->blacklist('c', $self->{sysCpuIndex});
  $self->add_info(sprintf 'cpu %d has %dC (%drpm)',
      $self->{sysCpuIndex},
      $self->{sysCpuTemperature},
      $self->{sysCpuFanSpeed});
  $self->add_perfdata(
      label => sprintf('temp_c%s', $self->{sysCpuIndex}),
      value => $self->{sysCpuTemperature},
      warning => undef,
      critical => undef,
  );
  $self->add_perfdata(
      label => sprintf('fan_c%s', $self->{sysCpuIndex}),
      value => $self->{sysCpuFanSpeed},
      warning => undef,
      critical => undef,
  );
}

sub dump {
  my $self = shift;
  printf "[CPU_%s]\n", $self->{sysCpuIndex};
  foreach(qw(sysCpuIndex sysCpuTemperature sysCpuFanSpeed
      sysCpuName sysCpuSlot)) {
    printf "%s: %s\n", $_, $self->{$_};
  }
  printf "info: %s\n", $self->{info};
  printf "\n";
}

package NWC::F5::F5BIGIP::Component::FanSubsystem;
our @ISA = qw(NWC::F5::F5BIGIP::Component::EnvironmentalSubsystem);

use strict;
use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    fans => [],
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  bless $self, $class;
  $self->init(%params);
  return $self;
}

sub init {
  my $self = shift;
  foreach ($self->get_snmp_table_objects(
      'F5-BIGIP-SYSTEM-MIB', 'sysChassisFanTable')) {
    push(@{$self->{fans}},
        NWC::F5::F5BIGIP::Component::FanSubsystem::Fan->new(%{$_}));
  }
}

sub check {
  my $self = shift;
  my $errorfound = 0;
  $self->add_info('checking fans');
  $self->blacklist('ff', '');
  if (scalar (@{$self->{fans}}) == 0) {
  } else {
    foreach (@{$self->{fans}}) {
      $_->check();
    }
  }
}


sub dump {
  my $self = shift;
  foreach (@{$self->{fans}}) {
    $_->dump();
  }
}


package NWC::F5::F5BIGIP::Component::FanSubsystem::Fan;
our @ISA = qw(NWC::F5::F5BIGIP::Component::FanSubsystem);

use strict;
use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  foreach(qw(sysChassisFanIndex sysChassisFanStatus
      sysChassisFanSpeed)) {
    $self->{$_} = $params{$_};
  }
  bless $self, $class;
  return $self;
}

sub check {
  my $self = shift;
  $self->blacklist('f', $self->{sysChassisFanIndex});
  $self->add_info(sprintf 'chassis fan %d is %s (%drpm)',
      $self->{sysChassisFanIndex},
      $self->{sysChassisFanStatus},
      $self->{sysChassisFanSpeed});
  if ($self->{sysChassisFanStatus} eq 'notpresent') {
  } else {
    if ($self->{sysChassisFanStatus} ne 'good') {
      $self->add_message(CRITICAL, $self->{info});
    }
    $self->add_perfdata(
        label => sprintf('fan_%s', $self->{sysChassisFanIndex}),
        value => $self->{sysChassisFanSpeed},
        warning => undef,
        critical => undef,
    );
  }
}

sub dump {
  my $self = shift;
  printf "[FAN_%s]\n", $self->{sysChassisFanIndex};
  foreach(qw(sysChassisFanIndex sysChassisFanStatus
      sysChassisFanSpeed)) {
    printf "%s: %s\n", $_, $self->{$_};
  }
  printf "info: %s\n", $self->{info};
  printf "\n";
}

package NWC::F5::F5BIGIP::Component::EnvironmentalSubsystem;
our @ISA = qw(NWC::F5::F5BIGIP);

use strict;
use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    runtime => $params{runtime},
    rawdata => $params{rawdata},
    method => $params{method},
    condition => $params{condition},
    status => $params{status},
    cpu_subsystem => undef,
    fan_subsystem => undef,
    temperature_subsystem => undef,
    powersupply_subsystem => undef,
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  bless $self, $class;
  $self->init(%params);
  return $self;
}

sub init {
  my $self = shift;
  my %params = @_;
  $self->{cpu_subsystem} =
      NWC::F5::F5BIGIP::Component::CpuSubsystem->new(%params);
  $self->{fan_subsystem} =
      NWC::F5::F5BIGIP::Component::FanSubsystem->new(%params);
  $self->{temperature_subsystem} =
      NWC::F5::F5BIGIP::Component::TemperatureSubsystem->new(%params);
  $self->{powersupply_subsystem} = 
      NWC::F5::F5BIGIP::Component::PowersupplySubsystem->new(%params);
}

sub check {
  my $self = shift;
  my $errorfound = 0;
  $self->{cpu_subsystem}->check();
  $self->{fan_subsystem}->check();
  $self->{temperature_subsystem}->check();
  $self->{powersupply_subsystem}->check();
  if (! $self->check_messages()) {
    $self->add_message(OK, "environmental hardware working fine");
  }
}

sub dump {
  my $self = shift;
  $self->{cpu_subsystem}->dump();
  $self->{fan_subsystem}->dump();
  $self->{temperature_subsystem}->dump();
  $self->{powersupply_subsystem}->dump();
}


package NWC::F5::F5BIGIP;

use strict;

use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

our @ISA = qw(NWC::F5);

sub init {
  my $self = shift;
  my %params = @_;
  $self->{components} = {
      powersupply_subsystem => undef,
      fan_subsystem => undef,
      temperature_subsystem => undef,
      cpu_subsystem => undef,
      memory_subsystem => undef,
      disk_subsystem => undef,
      environmental_subsystem => undef,
      ltm_subsystem => undef,
  };
  $self->{productversion} = $self->get_snmp_object('F5-BIGIP-SYSTEM-MIB', 'sysProductVersion');
  if (! defined $self->{productversion} ||
      $self->{productversion} !~ /^((9)|(10)|(11))/) {
    $self->{productversion} = "4";
  }
  $params{productversion} = $self->{productversion};
  if (! $self->check_messages()) {
    if ($self->mode =~ /device::hardware::health/) {
      $self->analyze_environmental_subsystem();
      #$self->auto_blacklist();
      $self->check_environmental_subsystem();
    } elsif ($self->mode =~ /device::hardware::load/) {
      $self->analyze_cpu_subsystem();
      #$self->auto_blacklist();
      $self->check_cpu_subsystem();
    } elsif ($self->mode =~ /device::hardware::memory/) {
      $self->analyze_mem_subsystem();
      #$self->auto_blacklist();
      $self->check_mem_subsystem();
    } elsif ($self->mode =~ /device::interfaces/) {
      $self->analyze_interface_subsystem();
      $self->check_interface_subsystem();
    } elsif ($self->mode =~ /device::lb/) {
      $self->analyze_ltm_subsystem(%params);
      $self->check_ltm_subsystem();
    } elsif ($self->mode =~ /device::shinken::interface/) {
      $self->analyze_interface_subsystem();
      $self->shinken_interface_subsystem();
    }
  }
}

sub analyze_environmental_subsystem {
  my $self = shift;
  $self->{components}->{environmental_subsystem} =
      NWC::F5::F5BIGIP::Component::EnvironmentalSubsystem->new();
}

sub analyze_interface_subsystem {
  my $self = shift;
  $self->{components}->{interface_subsystem} =
      NWC::IFMIB::Component::InterfaceSubsystem->new();
}

sub analyze_cpu_subsystem {
  my $self = shift;
  $self->{components}->{cpu_subsystem} =
      NWC::F5::F5BIGIP::Component::CpuSubsystem->new();
}

sub analyze_mem_subsystem {
  my $self = shift;
  $self->{components}->{mem_subsystem} =
      NWC::F5::F5BIGIP::Component::MemSubsystem->new();
}

sub analyze_ltm_subsystem {
  my $self = shift;
  my %params = @_;
  $self->{components}->{ltm_subsystem} =
      NWC::F5::F5BIGIP::Component::LTMSubsystem->new(%params);
}

sub check_environmental_subsystem {
  my $self = shift;
  $self->{components}->{environmental_subsystem}->check();
  $self->{components}->{environmental_subsystem}->dump()
      if $self->opts->verbose >= 2;
}

sub check_interface_subsystem {
  my $self = shift;
  $self->{components}->{interface_subsystem}->check();
  $self->{components}->{interface_subsystem}->dump()
      if $self->opts->verbose >= 2;
}

sub check_cpu_subsystem {
  my $self = shift;
  $self->{components}->{cpu_subsystem}->check();
  $self->{components}->{cpu_subsystem}->dump()
      if $self->opts->verbose >= 2;
}

sub check_mem_subsystem {
  my $self = shift;
  $self->{components}->{mem_subsystem}->check();
  $self->{components}->{mem_subsystem}->dump()
      if $self->opts->verbose >= 2;
}

sub check_ltm_subsystem {
  my $self = shift;
  $self->{components}->{ltm_subsystem}->check();
  $self->{components}->{ltm_subsystem}->dump()
      if $self->opts->verbose >= 2;
}


package NWC::F5;

use strict;

use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

our @ISA = qw(NWC::Device);

use constant trees => (
    '1.3.6.1.4.1.3375.1.2.1.1.1', # F5-3DNS-MIB
    '1.3.6.1.4.1.3375', # F5-BIGIP-COMMON-MIB
    '1.3.6.1.4.1.3375.2.2', # F5-BIGIP-LOCAL-MIB
    '1.3.6.1.4.1.3375.2.1', # F5-BIGIP-SYSTEM-MIB
    '1.3.6.1.4.1.3375.1.1.1.1', # LOAD-BAL-SYSTEM-MIB
    '1.3.6.1.4.1.2021', # UCD-SNMP-MIB
);

sub init {
  my $self = shift;
  my %params = @_;
  if ($self->{productname} =~ /Linux.*((el6.f5.x86_64)|(el5.1.0.f5app)) .*/i) {
    bless $self, 'NWC::F5::F5BIGIP';
    $self->debug('using NWC::F5::F5BIGIP');
  }
  $self->init(%params);
}

package NWC::CheckPoint::Firewall1::Component::MngmtSubsystem;
our @ISA = qw(NWC::CheckPoint::Firewall1);

use strict;
use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    cpus => [],
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  bless $self, $class;
  $self->init(%params);
  return $self;
}

sub init {
  my $self = shift;
  my %params = @_;
  if ($self->mode =~ /device::mngmt::status/) {
    $self->{mgStatShortDescr} = $self->get_snmp_object('CHECKPOINT-MIB', 'mgStatShortDescr');
    $self->{mgStatLongDescr} = $self->get_snmp_object('CHECKPOINT-MIB', 'mgStatLongDescr');
  }
}

sub check {
  my $self = shift;
  my %params = @_;
  my $errorfound = 0;
  $self->add_info('checking mngmt');
  if ($self->mode =~ /device::mngmt::status/) {
    if ($self->{mgStatShortDescr} ne 'OK') {
      $self->add_message(CRITICAL,
          sprintf 'status of management is %s', $self->{mgStatLongDescr});
    } else {
      $self->add_message(OK,
          sprintf 'status of management is %s', $self->{mgStatLongDescr});
    }
  }
}

sub dump {
  my $self = shift;
  printf "[MNGMT]\n";
}

package NWC::CheckPoint::Firewall1::Component::SvnSubsystem;
our @ISA = qw(NWC::CheckPoint::Firewall1);

use strict;
use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    cpus => [],
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  bless $self, $class;
  $self->init(%params);
  return $self;
}

sub init {
  my $self = shift;
  my %params = @_;
  if ($self->mode =~ /device::svn::status/) {
    $self->{svnStatShortDescr} = $self->get_snmp_object('CHECKPOINT-MIB', 'svnStatShortDescr');
    $self->{svnStatLongDescr} = $self->get_snmp_object('CHECKPOINT-MIB', 'svnStatLongDescr');
  }
}

sub check {
  my $self = shift;
  my %params = @_;
  my $errorfound = 0;
  $self->add_info('checking svn');
  if ($self->mode =~ /device::svn::status/) {
    if ($self->{svnStatShortDescr} ne 'OK') {
      $self->add_message(CRITICAL,
          sprintf 'status of svn is %s', $self->{svnStatLongDescr});
    } else {
      $self->add_message(OK,
          sprintf 'status of svn is %s', $self->{svnStatLongDescr});
    }
  }
}

sub dump {
  my $self = shift;
  printf "[SVN]\n";
}

package NWC::CheckPoint::Firewall1::Component::FwSubsystem;
our @ISA = qw(NWC::CheckPoint::Firewall1);

use strict;
use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    cpus => [],
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  bless $self, $class;
  $self->init(%params);
  return $self;
}

sub init {
  my $self = shift;
  my %params = @_;
  $self->{fwModuleState} = $self->get_snmp_object('CHECKPOINT-MIB', 'fwModuleState');
  $self->{fwPolicyName} = $self->get_snmp_object('CHECKPOINT-MIB', 'fwPolicyName');
  if ($self->mode =~ /device::fw::policy::installed/) {
  } elsif ($self->mode =~ /device::fw::policy::connections/) {
    $self->{fwNumConn} = $self->get_snmp_object('CHECKPOINT-MIB', 'fwNumConn');
  }
}

sub check {
  my $self = shift;
  my %params = @_;
  my $errorfound = 0;
  $self->add_info('checking fw module');
  if ($self->{fwModuleState} ne 'Installed') {
    $self->add_message(CRITICAL,
        sprintf 'fw module is %s', $self->{fwPolicyName});
  } elsif ($self->mode =~ /device::fw::policy::installed/) {
    if ($self->{fwPolicyName} eq $self->opts->name()) {
      $self->add_message(OK,
        sprintf 'fw policy is %s', $self->{fwPolicyName});
    } else {
      $self->add_message(CRITICAL,
          sprintf 'fw policy is %s, expected %s',
              $self->{fwPolicyName}, $self->opts->name());
    }
  } elsif ($self->mode =~ /device::fw::policy::connections/) {
    $self->set_thresholds(warning => 20000, critical => 23000);
    $self->add_message($self->check_thresholds($self->{fwNumConn}),
        sprintf 'policy %s has %s open connections',
            $self->{fwPolicyName}, $self->{fwNumConn});
    $self->add_perfdata(
        label => 'fw_policy_numconn',
        value => $self->{fwNumConn},
    );
  }
}

sub dump {
  my $self = shift;
  printf "[FW]\n";
}

package NWC::CheckPoint::Firewall1::Component::HaSubsystem;
our @ISA = qw(NWC::CheckPoint::Firewall1);

use strict;
use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    cpus => [],
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  bless $self, $class;
  $self->init(%params);
  return $self;
}

sub init {
  my $self = shift;
  my %params = @_;
  if ($self->mode =~ /device::ha::role/) {
    $self->{haStarted} = $self->get_snmp_object('CHECKPOINT-MIB', 'haStarted');
    $self->{haState} = $self->get_snmp_object('CHECKPOINT-MIB', 'haState');
    $self->{haStatShort} = $self->get_snmp_object('CHECKPOINT-MIB', 'haStatShort');
    if (! $self->opts->role()) {
      $self->opts->override_opt('role', 'active');
    }
  }
}

sub check {
  my $self = shift;
  my %params = @_;
  my $errorfound = 0;
  $self->add_info('checking ha');
  my $info = sprintf 'ha %sstarted, role is %s, status is %s', 
      $self->{haStarted} eq 'yes' ? '' : 'not ', 
      $self->{haState}, $self->{haStatShort};
  $self->add_info($info);
  if ($self->{haStarted} eq 'yes') {
    if ($self->{haStatShort} ne 'OK') {
      $self->add_message(
          defined $self->opts->mitigation() ? $self->opts->mitigation() : 2,
          $info);
    } elsif ($self->{haState} ne $self->opts->role()) {
      $self->add_message(
          defined $self->opts->mitigation() ? $self->opts->mitigation() : 1,
          $info);
      $self->add_message(
          defined $self->opts->mitigation() ? $self->opts->mitigation() : 1,
          sprintf "expected role %s", $self->opts->role())
    } else {
      $self->add_message(OK, $info);
    }
  } else {
    $self->add_message(
        defined $self->opts->mitigation() ? $self->opts->mitigation() : 1,
        'ha was not started');
  }

  #$self->add_message($self->check_thresholds($self->{procUsage}), $info);
}

sub dump {
  my $self = shift;
  printf "[CPU]\n";
  foreach (qw(procUsage procQueue)) {
    printf "%s: %s\n", $_, $self->{$_} if defined $self->{$_};
  }
  printf "info: %s\n", $self->{info};
  printf "\n";
}

package NWC::CheckPoint::Firewall1::Component::CpuSubsystem;
our @ISA = qw(NWC::CheckPoint::Firewall1);

use strict;
use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    cpus => [],
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  bless $self, $class;
  $self->init(%params);
  return $self;
}

sub init {
  my $self = shift;
  my %params = @_;
  $self->{procUsage} = $self->get_snmp_object('CHECKPOINT-MIB', 'procUsage');
  $self->{procQueue} = $self->get_snmp_object('CHECKPOINT-MIB', 'procQueue');
}

sub check {
  my $self = shift;
  my $errorfound = 0;
  $self->add_info('checking cpus');
  my $info = sprintf 'cpu usage is %.2f%%', $self->{procUsage};
  $self->add_info($info);
  $self->set_thresholds(warning => 80, critical => 90);
  $self->add_message($self->check_thresholds($self->{procUsage}), $info);
  $self->add_perfdata(
      label => 'cpu_usage',
      value => $self->{procUsage},
      uom => '%',
      warning => $self->{warning},
      critical => $self->{critical},
  );
  if (defined $self->{procQueue}) {
    $self->add_perfdata(
        label => 'cpu_queue_length',
        value => $self->{procQueue},
    );
  }
}

sub dump {
  my $self = shift;
  printf "[CPU]\n";
  foreach (qw(procUsage procQueue)) {
    printf "%s: %s\n", $_, $self->{$_} if defined $self->{$_};
  }
  printf "info: %s\n", $self->{info};
  printf "\n";
}

package NWC::CheckPoint::Firewall1;

use strict;

use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

our @ISA = qw(NWC::CheckPoint);

sub init {
  my $self = shift;
  $self->{components} = {
      powersupply_subsystem => undef,
      fan_subsystem => undef,
      temperature_subsystem => undef,
      cpu_subsystem => undef,
  };
  $self->{serial} = 'unknown';
  $self->{product} = 'unknown';
  $self->{romversion} = 'unknown';
  # serial is 1.3.6.1.2.1.47.1.1.1.1.11.1
  #$self->collect();
  if (! $self->check_messages()) {
    ##$self->set_serial();
    if ($self->mode =~ /device::hardware::health/) {
      $self->analyze_environmental_subsystem();
      $self->check_environmental_subsystem();
    } elsif ($self->mode =~ /device::hardware::load/) {
      $self->analyze_cpu_subsystem();
      $self->check_cpu_subsystem();
    } elsif ($self->mode =~ /device::hardware::memory/) {
      $self->analyze_mem_subsystem();
      $self->check_mem_subsystem();
    } elsif ($self->mode =~ /device::ha::/) {
      $self->analyze_ha_subsystem();
      $self->check_ha_subsystem();
    } elsif ($self->mode =~ /device::fw::/) {
      $self->analyze_fw_subsystem();
      $self->check_fw_subsystem();
    } elsif ($self->mode =~ /device::svn::/) {
      $self->analyze_svn_subsystem();
      $self->check_svn_subsystem();
    } elsif ($self->mode =~ /device::mngmt::/) {
      $self->analyze_mngmt_subsystem();
      $self->check_svn_subsystem();
    } elsif ($self->mode =~ /device::interfaces/) {
      $self->analyze_interface_subsystem();
      $self->check_interface_subsystem();
    }
  }
}

sub analyze_environmental_subsystem {
  my $self = shift;
  $self->{components}->{environmental_subsystem} =
      NWC::CheckPoint::Firewall1::Component::EnvironmentalSubsystem->new();
}

sub analyze_cpu_subsystem {
  my $self = shift;
  $self->{components}->{cpu_subsystem} =
      NWC::CheckPoint::Firewall1::Component::CpuSubsystem->new();
#printf "%s\n", Data::Dumper::Dumper($self->{components});
}

sub analyze_mem_subsystem {
  my $self = shift;
  $self->{components}->{mem_subsystem} =
      NWC::CheckPoint::Firewall1::Component::MemSubsystem->new();
}

sub analyze_ha_subsystem {
  my $self = shift;
  $self->{components}->{ha_subsystem} =
      NWC::CheckPoint::Firewall1::Component::HaSubsystem->new();
}

sub analyze_fw_subsystem {
  my $self = shift;
  $self->{components}->{fw_subsystem} =
      NWC::CheckPoint::Firewall1::Component::FwSubsystem->new();
}

sub analyze_svn_subsystem {
  my $self = shift;
  $self->{components}->{svn_subsystem} =
      NWC::CheckPoint::Firewall1::Component::SvnSubsystem->new();
}

sub analyze_mngmt_subsystem {
  my $self = shift;
  $self->{components}->{mngmt_subsystem} =
      NWC::CheckPoint::Firewall1::Component::MngmtSubsystem->new();
printf "%s\n", Data::Dumper::Dumper($self->{components});
}

sub analyze_interface_subsystem {
  my $self = shift;
  $self->{components}->{interface_subsystem} =
      NWC::IFMIB::Component::InterfaceSubsystem->new();
}

sub check_environmental_subsystem {
  my $self = shift;
  $self->{components}->{environmental_subsystem}->check();
  $self->{components}->{environmental_subsystem}->dump()
      if $self->opts->verbose >= 2;
}

sub check_cpu_subsystem {
  my $self = shift;
  $self->{components}->{cpu_subsystem}->check();
  $self->{components}->{cpu_subsystem}->dump()
      if $self->opts->verbose >= 2;
}

sub check_mem_subsystem {
  my $self = shift;
  $self->{components}->{mem_subsystem}->check();
  $self->{components}->{mem_subsystem}->dump()
      if $self->opts->verbose >= 2;
}

sub check_ha_subsystem {
  my $self = shift;
  $self->{components}->{ha_subsystem}->check();
  $self->{components}->{ha_subsystem}->dump()
      if $self->opts->verbose >= 2;
}

sub check_fw_subsystem {
  my $self = shift;
  $self->{components}->{fw_subsystem}->check();
  $self->{components}->{fw_subsystem}->dump()
      if $self->opts->verbose >= 2;
}

sub check_svn_subsystem {
  my $self = shift;
  $self->{components}->{svn_subsystem}->check();
  $self->{components}->{svn_subsystem}->dump()
      if $self->opts->verbose >= 2;
}

sub check_mngmt_subsystem {
  my $self = shift;
  $self->{components}->{mngmt_subsystem}->check();
  $self->{components}->{mngmt_subsystem}->dump()
      if $self->opts->verbose >= 2;
}

sub check_interface_subsystem {
  my $self = shift;
  $self->{components}->{interface_subsystem}->check();
  $self->{components}->{interface_subsystem}->dump()
      if $self->opts->verbose >= 2;
}

package NWC::CheckPoint;

use strict;

use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

our @ISA = qw(NWC::Device);

use constant trees => (
    '1.3.6.1.4.1.2620', # CHECKPOINT-MIB
);

sub init {
  my $self = shift;
  if ($self->{productname} =~ /FireWall\-1\s/i) {
    bless $self, 'NWC::CheckPoint::Firewall1';
    $self->debug('using NWC::CheckPoint::Firewall1');
  }
  $self->init();
}

$NWC::Device::mibs_and_oids = {
  'MIB-II' => {
      sysDescr => '1.3.6.1.2.1.1.1',
      sysUpTime => '1.3.6.1.2.1.1.3',
  },
  'IFMIB' => {
      ifTable => '1.3.6.1.2.1.2.2',
      ifEntry => '1.3.6.1.2.1.2.2.1',
      ifIndex => '1.3.6.1.2.1.2.2.1.1',
      ifDescr => '1.3.6.1.2.1.2.2.1.2',
      ifType => '1.3.6.1.2.1.2.2.1.3',
      ifMtu => '1.3.6.1.2.1.2.2.1.4',
      ifSpeed => '1.3.6.1.2.1.2.2.1.5',
      ifPhysAddress => '1.3.6.1.2.1.2.2.1.6',
      ifAdminStatus => '1.3.6.1.2.1.2.2.1.7',
      ifOperStatus => '1.3.6.1.2.1.2.2.1.8',
      ifLastChange => '1.3.6.1.2.1.2.2.1.9',
      ifInOctets => '1.3.6.1.2.1.2.2.1.10',
      ifInUcastPkts => '1.3.6.1.2.1.2.2.1.11',
      ifInNUcastPkts => '1.3.6.1.2.1.2.2.1.12',
      ifInDiscards => '1.3.6.1.2.1.2.2.1.13',
      ifInErrors => '1.3.6.1.2.1.2.2.1.14',
      ifInUnknownProtos => '1.3.6.1.2.1.2.2.1.15',
      ifOutOctets => '1.3.6.1.2.1.2.2.1.16',
      ifOutUcastPkts => '1.3.6.1.2.1.2.2.1.17',
      ifOutNUcastPkts => '1.3.6.1.2.1.2.2.1.18',
      ifOutDiscards => '1.3.6.1.2.1.2.2.1.19',
      ifOutErrors => '1.3.6.1.2.1.2.2.1.20',
      ifOutQLen => '1.3.6.1.2.1.2.2.1.21',
      ifSpecific => '1.3.6.1.2.1.2.2.1.22',
      ifAdminStatusDefinition => {
          1 => 'up',
          2 => 'down',
          3 => 'testing',
      },
      ifOperStatusDefinition => {
          1 => 'up',
          2 => 'down',
          3 => 'testing',
          4 => 'unknown',
          5 => 'dormant',
          6 => 'notPresent',
          7 => 'lowerLayerDown',
      },
      # INDEX { ifIndex }
      #
      ifXTable => '1.3.6.1.2.1.31.1.1',
      ifXEntry => '1.3.6.1.2.1.31.1.1.1',
      ifName => '1.3.6.1.2.1.31.1.1.1.1',
      ifInMulticastPkts => '1.3.6.1.2.1.31.1.1.1.2',
      ifInBroadcastPkts => '1.3.6.1.2.1.31.1.1.1.3',
      ifOutMulticastPkts => '1.3.6.1.2.1.31.1.1.1.4',
      ifOutBroadcastPkts => '1.3.6.1.2.1.31.1.1.1.5',
      ifHCInOctets => '1.3.6.1.2.1.31.1.1.1.6',
      ifHCInUcastPkts => '1.3.6.1.2.1.31.1.1.1.7',
      ifHCInMulticastPkts => '1.3.6.1.2.1.31.1.1.1.8',
      ifHCInBroadcastPkts => '1.3.6.1.2.1.31.1.1.1.9',
      ifHCOutOctets => '1.3.6.1.2.1.31.1.1.1.10',
      ifHCOutUcastPkts => '1.3.6.1.2.1.31.1.1.1.11',
      ifHCOutMulticastPkts => '1.3.6.1.2.1.31.1.1.1.12',
      ifHCOutBroadcastPkts => '1.3.6.1.2.1.31.1.1.1.13',
      ifLinkUpDownTrapEnable => '1.3.6.1.2.1.31.1.1.1.14',
      ifHighSpeed => '1.3.6.1.2.1.31.1.1.1.15',
      ifPromiscuousMode => '1.3.6.1.2.1.31.1.1.1.16',
      ifConnectorPresent => '1.3.6.1.2.1.31.1.1.1.17',
      ifAlias => '1.3.6.1.2.1.31.1.1.1.18',
      ifCounterDiscontinuityTime => '1.3.6.1.2.1.31.1.1.1.19',
      ifLinkUpDownTrapEnableDefinition => {
          1 => 'enabled',
          2 => 'disabled',
      },
      # ifXEntry AUGMENTS ifEntry
      #
  },
  'CISCO-PROCESS-MIB' => {
      cpmCPUTotalTable => '1.3.6.1.4.1.9.9.109.1.1.1',
      cpmCPUTotalEntry => '1.3.6.1.4.1.9.9.109.1.1.1.1',
      cpmCPUTotalIndex => '1.3.6.1.4.1.9.9.109.1.1.1.1.1',
      cpmCPUTotalPhysicalIndex => '1.3.6.1.4.1.9.9.109.1.1.1.1.2',
      cpmCPUTotal5sec => '1.3.6.1.4.1.9.9.109.1.1.1.1.3',
      cpmCPUTotal1min => '1.3.6.1.4.1.9.9.109.1.1.1.1.4',
      cpmCPUTotal5min => '1.3.6.1.4.1.9.9.109.1.1.1.1.5',
      cpmCPUTotal5secRev => '1.3.6.1.4.1.9.9.109.1.1.1.1.6',
      cpmCPUTotal1minRev => '1.3.6.1.4.1.9.9.109.1.1.1.1.7',
      cpmCPUTotal5minRev => '1.3.6.1.4.1.9.9.109.1.1.1.1.8',
      cpmCPUMonInterval => '1.3.6.1.4.1.9.9.109.1.1.1.1.9',
      cpmCPUTotalMonIntervalDefinition => '1.3.6.1.4.1.9.9.109.1.1.1.1.10',
      cpmCPUInterruptMonIntervalDefinition => '1.3.6.1.4.1.9.9.109.1.1.1.1.11',
      # INDEX { cpmCPUTotalIndex }
  },
  'CISCO-MEMORY-POOL-MIB' => {
      ciscoMemoryPoolTable => '1.3.6.1.4.1.9.9.48.1.1',
      ciscoMemoryPoolEntry => '1.3.6.1.4.1.9.9.48.1.1.1',
      ciscoMemoryPoolType => '1.3.6.1.4.1.9.9.48.1.1.1.1',
      ciscoMemoryPoolName => '1.3.6.1.4.1.9.9.48.1.1.1.2',
      ciscoMemoryPoolAlternate => '1.3.6.1.4.1.9.9.48.1.1.1.3',
      ciscoMemoryPoolValid => '1.3.6.1.4.1.9.9.48.1.1.1.4',
      ciscoMemoryPoolUsed => '1.3.6.1.4.1.9.9.48.1.1.1.5',
      ciscoMemoryPoolFree => '1.3.6.1.4.1.9.9.48.1.1.1.6',
      ciscoMemoryPoolLargestFree => '1.3.6.1.4.1.9.9.48.1.1.1.7',
      # INDEX { ciscoMemoryPoolType }
  },
  'CISCO-ENVMON-MIB' => {
     ciscoEnvMonFanStatusTable => '1.3.6.1.4.1.9.9.13.1.4',
     ciscoEnvMonFanStatusEntry => '1.3.6.1.4.1.9.9.13.1.4.1',
     ciscoEnvMonFanStatusIndex => '1.3.6.1.4.1.9.9.13.1.4.1.1',
     ciscoEnvMonFanStatusDescr => '1.3.6.1.4.1.9.9.13.1.4.1.2',
     ciscoEnvMonFanState => '1.3.6.1.4.1.9.9.13.1.4.1.3',
     ciscoEnvMonFanStateDefinition => 'CISCO-ENVMON-MIB::ciscoEnvMonState',
     # INDEX { ciscoEnvMonFanStatusIndex }
     ciscoEnvMonSupplyStatusTable => '1.3.6.1.4.1.9.9.13.1.5',
     ciscoEnvMonSupplyStatusEntry => '1.3.6.1.4.1.9.9.13.1.5.1',
     ciscoEnvMonSupplyStatusIndex => '1.3.6.1.4.1.9.9.13.1.5.1.1',
     ciscoEnvMonSupplyStatusDescr => '1.3.6.1.4.1.9.9.13.1.5.1.2',
     ciscoEnvMonSupplyState => '1.3.6.1.4.1.9.9.13.1.5.1.3',
     ciscoEnvMonSupplySource => '1.3.6.1.4.1.9.9.13.1.5.1.4',
     ciscoEnvMonSupplyStateDefinition => 'CISCO-ENVMON-MIB::ciscoEnvMonState',
     # INDEX { ciscoEnvMonSupplyStatusIndex }
     ciscoEnvMonTemperatureStatusTable => '1.3.6.1.4.1.9.9.13.1.3',
     ciscoEnvMonTemperatureStatusEntry => '1.3.6.1.4.1.9.9.13.1.3.1',
     ciscoEnvMonTemperatureStatusIndex => '1.3.6.1.4.1.9.9.13.1.3.1.1',
     ciscoEnvMonTemperatureStatusDescr => '1.3.6.1.4.1.9.9.13.1.3.1.2',
     ciscoEnvMonTemperatureStatusValue => '1.3.6.1.4.1.9.9.13.1.3.1.3',
     ciscoEnvMonTemperatureThreshold => '1.3.6.1.4.1.9.9.13.1.3.1.4',
     ciscoEnvMonTemperatureLastShutdown => '1.3.6.1.4.1.9.9.13.1.3.1.5',
     ciscoEnvMonTemperatureState => '1.3.6.1.4.1.9.9.13.1.3.1.6',
     ciscoEnvMonTemperatureStateDefinition => 'CISCO-ENVMON-MIB::ciscoEnvMonState',
     # INDEX { ciscoEnvMonTemperatureStatusIndex }
     ciscoEnvMonVoltageStatusTable => '1.3.6.1.4.1.9.9.13.1.2',
     ciscoEnvMonVoltageStatusEntry => '1.3.6.1.4.1.9.9.13.1.2.1',
     ciscoEnvMonVoltageStatusIndex => '1.3.6.1.4.1.9.9.13.1.2.1.1',
     ciscoEnvMonVoltageStatusDescr => '1.3.6.1.4.1.9.9.13.1.2.1.2',
     ciscoEnvMonVoltageStatusValue => '1.3.6.1.4.1.9.9.13.1.2.1.3',
     ciscoEnvMonVoltageThresholdLow => '1.3.6.1.4.1.9.9.13.1.2.1.4',
     ciscoEnvMonVoltageThresholdHigh => '1.3.6.1.4.1.9.9.13.1.2.1.5',
     ciscoEnvMonVoltageLastShutdown => '1.3.6.1.4.1.9.9.13.1.2.1.6',
     ciscoEnvMonVoltageState => '1.3.6.1.4.1.9.9.13.1.2.1.7',
     ciscoEnvMonVoltageStateDefinition => 'CISCO-ENVMON-MIB::ciscoEnvMonState',
     # INDEX { ciscoEnvMonVoltageStatusIndex }
  },
  'CISCO-HSRP-MIB' => {
      cHsrpGrpTable => '1.3.6.1.4.1.9.9.106.1.2.1',
      cHsrpGrpEntry => '1.3.6.1.4.1.9.9.106.1.2.1.1',
      cHsrpGrpNumber => '1.3.6.1.4.1.9.9.106.1.2.1.1.1',
      cHsrpGrpAuth => '1.3.6.1.4.1.9.9.106.1.2.1.1.2',
      cHsrpGrpPriority => '1.3.6.1.4.1.9.9.106.1.2.1.1.3',
      cHsrpGrpPreempt => '1.3.6.1.4.1.9.9.106.1.2.1.1.4',
      cHsrpGrpPreemptDelay => '1.3.6.1.4.1.9.9.106.1.2.1.1.5',
      cHsrpGrpUseConfiguredTimers => '1.3.6.1.4.1.9.9.106.1.2.1.1.6',
      cHsrpGrpConfiguredHelloTime => '1.3.6.1.4.1.9.9.106.1.2.1.1.7',
      cHsrpGrpConfiguredHoldTime => '1.3.6.1.4.1.9.9.106.1.2.1.1.8',
      cHsrpGrpLearnedHelloTime => '1.3.6.1.4.1.9.9.106.1.2.1.1.9',
      cHsrpGrpLearnedHoldTime => '1.3.6.1.4.1.9.9.106.1.2.1.1.10',
      cHsrpGrpVirtualIpAddr => '1.3.6.1.4.1.9.9.106.1.2.1.1.11',
      cHsrpGrpUseConfigVirtualIpAddr => '1.3.6.1.4.1.9.9.106.1.2.1.1.12',
      cHsrpGrpActiveRouter => '1.3.6.1.4.1.9.9.106.1.2.1.1.13',
      cHsrpGrpStandbyRouter => '1.3.6.1.4.1.9.9.106.1.2.1.1.14',
      cHsrpGrpStandbyState => '1.3.6.1.4.1.9.9.106.1.2.1.1.15',
      cHsrpGrpStandbyStateDefinition => 'CISCO-HSRP-MIB::HsrpState',
      cHsrpGrpVirtualMacAddr => '1.3.6.1.4.1.9.9.106.1.2.1.1.16',
      cHsrpGrpEntryRowStatus => '1.3.6.1.4.1.9.9.106.1.2.1.1.17',
      cHsrpGrpEntryRowStatusDefinition => 'SNMPv2-TC-v1::RowStatus',
      # INDEX { ifIndex, cHsrpGrpNumber }
  },
  'OLD-CISCO-CPU-MIB' => {
      'avgBusy1' => '1.3.6.1.4.1.9.2.1.57.0',
      'avgBusy5' => '1.3.6.1.4.1.9.2.1.58.0',
      'busyPer' => '1.3.6.1.4.1.9.2.1.56.0',
      'idleCount' => '1.3.6.1.4.1.9.2.1.59.0',
      'idleWired' => '1.3.6.1.4.1.9.2.1.60.0',
  },
  'CISCO-SYSTEM-EXT-MIB' => {
      cseSysCPUUtilization => '1.3.6.1.4.1.9.9.305.1.1.1.0',
      cseSysMemoryUtilization => '1.3.6.1.4.1.9.9.305.1.1.2.0',
      cseSysConfLastChange => '1.3.6.1.4.1.9.9.305.1.1.3.0',
      cseSysAutoSync => '1.3.6.1.4.1.9.9.305.1.1.4.0',
      cseSysAutoSyncState => '1.3.6.1.4.1.9.9.305.1.1.5.0',
      cseWriteErase => '1.3.6.1.4.1.9.9.305.1.1.6.0',
      cseSysConsolePortStatus => '1.3.6.1.4.1.9.9.305.1.1.7.0',
      cseSysTelnetServiceActivation => '1.3.6.1.4.1.9.9.305.1.1.8.0',
      cseSysFIPSModeActivation => '1.3.6.1.4.1.9.9.305.1.1.9.0',
      cseSysUpTime => '1.3.6.1.4.1.9.9.305.1.1.10.0',
  },
  'CISCO-ENTITY-SENSOR-MIB' => {
      entSensorValueTable => '1.3.6.1.4.1.9.9.91.1.1.1',
      entSensorValueEntry => '1.3.6.1.4.1.9.9.91.1.1.1.1',
      entSensorType => '1.3.6.1.4.1.9.9.91.1.1.1.1.1',
      entSensorTypeDefinition => 'CISCO-ENTITY-SENSOR-MIB::SensorDataType',
      entSensorScale => '1.3.6.1.4.1.9.9.91.1.1.1.1.2',
      entSensorScaleDefinition => 'CISCO-ENTITY-SENSOR-MIB::SensorDataScale',
      entSensorPrecision => '1.3.6.1.4.1.9.9.91.1.1.1.1.3',
      entSensorValue => '1.3.6.1.4.1.9.9.91.1.1.1.1.4',
      entSensorStatus => '1.3.6.1.4.1.9.9.91.1.1.1.1.5',
      entSensorStatusDefinition => 'CISCO-ENTITY-SENSOR-MIB::SensorStatus',
      entSensorValueTimeStamp => '1.3.6.1.4.1.9.9.91.1.1.1.1.6',
      entSensorValueUpdateRate => '1.3.6.1.4.1.9.9.91.1.1.1.1.7',
      entSensorMeasuredEntity => '1.3.6.1.4.1.9.9.91.1.1.1.1.8',
      entSensorThresholdTable => '1.3.6.1.4.1.9.9.91.1.2.1',
      entSensorThresholdEntry => '1.3.6.1.4.1.9.9.91.1.2.1.1',
      entSensorThresholdIndex => '1.3.6.1.4.1.9.9.91.1.2.1.1.1',
      entSensorThresholdSeverity => '1.3.6.1.4.1.9.9.91.1.2.1.1.2',
      entSensorThresholdSeverityDefinition => 'CISCO-ENTITY-SENSOR-MIB::SensorThresholdSeverity',
      entSensorThresholdRelation => '1.3.6.1.4.1.9.9.91.1.2.1.1.3',
      entSensorThresholdRelationDefinition => 'CISCO-ENTITY-SENSOR-MIB::SensorThresholdRelation',
      entSensorThresholdValue => '1.3.6.1.4.1.9.9.91.1.2.1.1.4',
      entSensorThresholdEvaluation => '1.3.6.1.4.1.9.9.91.1.2.1.1.5',
      entSensorThresholdEvaluationDefinition => 'SNMPv2-TC-v1::TruthValue',
      entSensorThresholdNotificationEnable => '1.3.6.1.4.1.9.9.91.1.2.1.1.6',
      entSensorThresholdNotificationEnableDefinition => 'SNMPv2-TC-v1::TruthValue',
  },
  'CISCO-L2L3-INTERFACE-CONFIG-MIB' => {
      cL2L3IfTable => '1.3.6.1.4.1.9.9.151.1.1.1',
      cL2L3IfEntry => '1.3.6.1.4.1.9.9.151.1.1.1.1',
      cL2L3IfModeAdmin => '1.3.6.1.4.1.9.9.151.1.1.1.1.1',
      cL2L3IfModeAdminDefinition => 'CISCO-L2L3-INTERFACE-CONFIG-MIB::CL2L3InterfaceMode',
      cL2L3IfModeOper => '1.3.6.1.4.1.9.9.151.1.1.1.1.2',
      cL2L3IfModeOperDefinition => 'CISCO-L2L3-INTERFACE-CONFIG-MIB::CL2L3InterfaceMode',
  },
  'CISCO-VTP-MIB' => {
      vlanTrunkPortTable => '1.3.6.1.4.1.9.9.46.1.6.1',
      vlanTrunkPortEntry => '1.3.6.1.4.1.9.9.46.1.6.1.1',
      vlanTrunkPortIfIndex => '1.3.6.1.4.1.9.9.46.1.6.1.1.1',
      vlanTrunkPortVlansPruningEligible => '1.3.6.1.4.1.9.9.46.1.6.1.1.10',
      vlanTrunkPortVlansXmitJoined => '1.3.6.1.4.1.9.9.46.1.6.1.1.11',
      vlanTrunkPortVlansRcvJoined => '1.3.6.1.4.1.9.9.46.1.6.1.1.12',
      vlanTrunkPortDynamicState => '1.3.6.1.4.1.9.9.46.1.6.1.1.13',
      vlanTrunkPortDynamicStatus => '1.3.6.1.4.1.9.9.46.1.6.1.1.14',
      vlanTrunkPortDynamicStatusDefinition => {
          1 => 'trunking',
          2 => 'notTrunking',
      },
      vlanTrunkPortVtpEnabled => '1.3.6.1.4.1.9.9.46.1.6.1.1.15',
      vlanTrunkPortEncapsulationOperType => '1.3.6.1.4.1.9.9.46.1.6.1.1.16',
      vlanTrunkPortVlansEnabled2k => '1.3.6.1.4.1.9.9.46.1.6.1.1.17',
      vlanTrunkPortVlansEnabled3k => '1.3.6.1.4.1.9.9.46.1.6.1.1.18',
      vlanTrunkPortVlansEnabled4k => '1.3.6.1.4.1.9.9.46.1.6.1.1.19',
      vlanTrunkPortManagementDomain => '1.3.6.1.4.1.9.9.46.1.6.1.1.2',
      vtpVlansPruningEligible2k => '1.3.6.1.4.1.9.9.46.1.6.1.1.20',
      vtpVlansPruningEligible3k => '1.3.6.1.4.1.9.9.46.1.6.1.1.21',
      vtpVlansPruningEligible4k => '1.3.6.1.4.1.9.9.46.1.6.1.1.22',
      vlanTrunkPortVlansXmitJoined2k => '1.3.6.1.4.1.9.9.46.1.6.1.1.23',
      vlanTrunkPortVlansXmitJoined3k => '1.3.6.1.4.1.9.9.46.1.6.1.1.24',
      vlanTrunkPortVlansXmitJoined4k => '1.3.6.1.4.1.9.9.46.1.6.1.1.25',
      vlanTrunkPortVlansRcvJoined2k => '1.3.6.1.4.1.9.9.46.1.6.1.1.26',
      vlanTrunkPortVlansRcvJoined3k => '1.3.6.1.4.1.9.9.46.1.6.1.1.27',
      vlanTrunkPortVlansRcvJoined4k => '1.3.6.1.4.1.9.9.46.1.6.1.1.28',
      vlanTrunkPortDot1qTunnel => '1.3.6.1.4.1.9.9.46.1.6.1.1.29',
      vlanTrunkPortEncapsulationType => '1.3.6.1.4.1.9.9.46.1.6.1.1.3',
      vlanTrunkPortVlansActiveFirst2k => '1.3.6.1.4.1.9.9.46.1.6.1.1.30',
      vlanTrunkPortVlansActiveSecond2k => '1.3.6.1.4.1.9.9.46.1.6.1.1.31',
      vlanTrunkPortVlansEnabled => '1.3.6.1.4.1.9.9.46.1.6.1.1.4',
      vlanTrunkPortNativeVlan => '1.3.6.1.4.1.9.9.46.1.6.1.1.5',
      vlanTrunkPortRowStatus => '1.3.6.1.4.1.9.9.46.1.6.1.1.6',
      vlanTrunkPortInJoins => '1.3.6.1.4.1.9.9.46.1.6.1.1.7',
      vlanTrunkPortOutJoins => '1.3.6.1.4.1.9.9.46.1.6.1.1.8',
      vlanTrunkPortOldAdverts => '1.3.6.1.4.1.9.9.46.1.6.1.1.9',
  },
  'SW-MIB' => {
      swFirmwareVersion => '1.3.6.1.4.1.1588.2.1.1.1.1.6.0',
      swSensorTable => '1.3.6.1.4.1.1588.2.1.1.1.1.22',
      swSensorEntry => '1.3.6.1.4.1.1588.2.1.1.1.1.22.1',
      swSensorIndex => '1.3.6.1.4.1.1588.2.1.1.1.1.22.1.1',
      swSensorType => '1.3.6.1.4.1.1588.2.1.1.1.1.22.1.2',
      swSensorTypeDefinition => {
          1 => 'temperature',
          2 => 'fan',
          3 => 'power-supply',
      },
      swSensorStatus => '1.3.6.1.4.1.1588.2.1.1.1.1.22.1.3',
      swSensorStatusDefinition => {
          1 => 'unknown',
          2 => 'faulty',
          3 => 'below-min',
          4 => 'nominal',
          5 => 'above-max',
          6 => 'absent',
      },
      # The value, -2147483648, represents an unknown quantity
      # In V2.0, the temperature sensor
      # value will be in Celsius; the fan value will be in RPM
      # (revoluation per minute); and the power supply sensor reading
      # will be unknown.
      swSensorValue => '1.3.6.1.4.1.1588.2.1.1.1.1.22.1.4',
      swSensorInfo => '1.3.6.1.4.1.1588.2.1.1.1.1.22.1.5',

      swFwThresholdTable => '1.3.6.1.4.1.1588.2.1.1.1.10.3',
      swFwThresholdEntry => '1.3.6.1.4.1.1588.2.1.1.1.10.3.1',
      swFwThresholdIndex => '1.3.6.1.4.1.1588.2.1.1.1.10.3.1.1',
      swFwStatus => '1.3.6.1.4.1.1588.2.1.1.1.10.3.1.2',
      swFwName => '1.3.6.1.4.1.1588.2.1.1.1.10.3.1.3',
      swFwLabel => '1.3.6.1.4.1.1588.2.1.1.1.10.3.1.4',
      swFwCurVal => '1.3.6.1.4.1.1588.2.1.1.1.10.3.1.5',
      swFwLastEvent => '1.3.6.1.4.1.1588.2.1.1.1.10.3.1.6',
      swFwLastEventVal => '1.3.6.1.4.1.1588.2.1.1.1.10.3.1.7',
      swFwLastEventTime => '1.3.6.1.4.1.1588.2.1.1.1.10.3.1.8',
      swFwLastState => '1.3.6.1.4.1.1588.2.1.1.1.10.3.1.9',
      swFwBehaviorType => '1.3.6.1.4.1.1588.2.1.1.1.10.3.1.10',
      swFwBehaviorInt => '1.3.6.1.4.1.1588.2.1.1.1.10.3.1.11',

      swCpuOrMemoryUsage => '1.3.6.1.4.1.1588.2.1.1.1.26',
      swCpuUsage => '1.3.6.1.4.1.1588.2.1.1.1.26.1',
        # The system's CPU usage.
      swCpuNoOfRetries => '1.3.6.1.4.1.1588.2.1.1.1.26.2',
        # The number of times the system should take a CPU utilization sample before sending the CPU utilization trap.
      swCpuUsageLimit => '1.3.6.1.4.1.1588.2.1.1.1.26.3',
        # The CPU usage limit.
      swCpuPollingInterval => '1.3.6.1.4.1.1588.2.1.1.1.26.4',
        # The time after which the next CPU usage value will be recorded.
      swCpuAction => '1.3.6.1.4.1.1588.2.1.1.1.26.5',
        # The action to be taken if the CPU usage exceeds the specified threshold limit.
      swMemUsage => '1.3.6.1.4.1.1588.2.1.1.1.26.6',
        # The system's memory usage.
      swMemNoOfRetries => '1.3.6.1.4.1.1588.2.1.1.1.26.7',
        # The number of times the system should take a memory usage sample before sending the Fabric Watch trap that indicates the current memory usage.
      swMemUsageLimit => '1.3.6.1.4.1.1588.2.1.1.1.26.8',
        # The memory usage limit. This OID specifies the in-between threshold value.
      swMemPollingInterval => '1.3.6.1.4.1.1588.2.1.1.1.26.9',
        # The time after which the next memory usage sample will be taken
      swMemAction => '1.3.6.1.4.1.1588.2.1.1.1.26.10',
        # The action to be taken if the memory usage exceed the specified threshold limit.
      swMemUsageLimit1 => '1.3.6.1.4.1.1588.2.1.1.1.26.11',
        # This OID specifies the low threshold value.
      swMemUsageLimit3 => '1.3.6.1.4.1.1588.2.1.1.1.26.12',
        # This OID specifies the high threshold value.
  },
  'ENTITY-MIB' => {
      entPhysicalTable => '1.3.6.1.2.1.47.1.1.1',
      entPhysicalEntry => '1.3.6.1.2.1.47.1.1.1.1',
      entPhysicalDescr => '1.3.6.1.2.1.47.1.1.1.1.2',
  },
  'UCD-SNMP-MIB' => {
        laTable => '1.3.6.1.4.1.2021.10',
        laEntry => '1.3.6.1.4.1.2021.10.1',
        laIndex => '1.3.6.1.4.1.2021.10.1.1',
        laNames => '1.3.6.1.4.1.2021.10.1.2',
        laLoad => '1.3.6.1.4.1.2021.10.1.3',
        laConfig => '1.3.6.1.4.1.2021.10.1.4',
        laLoadInt => '1.3.6.1.4.1.2021.10.1.5',
        laLoadFloat => '1.3.6.1.4.1.2021.10.1.6',
        laErrorFlag => '1.3.6.1.4.1.2021.10.1.100',
        laErrMessage => '1.3.6.1.4.1.2021.10.1.101',

        memoryGroup => '1.3.6.1.4.1.2021.4',
        memIndex => '1.3.6.1.4.1.2021.4.1',
        memErrorName => '1.3.6.1.4.1.2021.4.2',
        memTotalSwap => '1.3.6.1.4.1.2021.4.3',
        memAvailSwap => '1.3.6.1.4.1.2021.4.4',
        memTotalReal => '1.3.6.1.4.1.2021.4.5',
        memAvailReal => '1.3.6.1.4.1.2021.4.6',
        memTotalSwapTXT => '1.3.6.1.4.1.2021.4.7',
        memAvailSwapTXT => '1.3.6.1.4.1.2021.4.8',
        memTotalRealTXT => '1.3.6.1.4.1.2021.4.9',
        memAvailRealTXT => '1.3.6.1.4.1.2021.4.10',
        memTotalFree => '1.3.6.1.4.1.2021.4.11',
        memMinimumSwap => '1.3.6.1.4.1.2021.4.12',
        memShared => '1.3.6.1.4.1.2021.4.13',
        memBuffer => '1.3.6.1.4.1.2021.4.14',
        memCached => '1.3.6.1.4.1.2021.4.15',
        memSwapError => '1.3.6.1.4.1.2021.4.100',
        memSwapErrorMsg => '1.3.6.1.4.1.2021.4.101',

        systemStatsGroup => '1.3.6.1.4.1.2021.11',
        ssIndex => '1.3.6.1.4.1.2021.11.1',
        ssErrorName => '1.3.6.1.4.1.2021.11.2',
        ssSwapIn => '1.3',
        ssSwapOut => '1.3.6.1.4.1.2021.11.4',
        ssIOSent => '1.3.6.1.4.1.2021.11.5',
        ssIOReceive => '1.3.6.1.4.1.2021.11.6',
        ssSysInterrupts => '1.3.6.1.4.1.2021.11.7',
        ssSysContext => '1.3.6.1.4.1.2021.11.8',
        ssCpuUser => '1.3.6.1.4.1.2021.11.9',
        ssCpuSystem => '1.3.6.1.4.1.2021.11.10',
        ssCpuIdle => '1.3.6.1.4.1.2021.11.11',
        ssCpuRawUser => '1.3.6.1.4.1.2021.11.50',
        ssCpuRawNice => '1.3.6.1.4.1.2021.11.51',
        ssCpuRawSystem => '1.3.6.1.4.1.2021.11.52',
        ssCpuRawIdle => '1.3.6.1.4.1.2021.11.53',
  },
  'FCMGMT-MIB' => {
      fcConnUnitTable => '1.3',
      fcConnUnitEntry => '1.3.1',
      fcConnUnitId => '1.3.1.1',
      fcConnUnitGlobalId => '1.3.1.2',
      fcConnUnitType => '1.3',
      fcConnUnitNumPorts => '1.3.1.4',
      fcConnUnitState => '1.3.1.5',
      fcConnUnitStatus => '1.3.1.6',
      fcConnUnitProduct => '1.3.1.7',
      fcConnUnitSerialNo => '1.3.1.8',
      fcConnUnitUpTime => '1.3.1.9',
      fcConnUnitUrl => '1.3.1.10',
      fcConnUnitDomainId => '1.3.1.11',
      fcConnUnitProxyMaster => '1.3.1.12',
      fcConnUnitPrincipal => '1.3.1.13',
      fcConnUnitNumSensors => '1.3.1.14',
      fcConnUnitNumRevs => '1.3.1.15',
      fcConnUnitModuleId => '1.3.1.16',
      fcConnUnitName => '1.3.1.17',
      fcConnUnitInfo => '1.3.1.18',
      fcConnUnitControl => '1.3.1.19',
      fcConnUnitContact => '1.3.1.20',
      fcConnUnitLocation => '1.3.1.21',
      fcConnUnitEventFilter => '1.3.1.22',
      fcConnUnitNumEvents => '1.3.1.23',
      fcConnUnitMaxEvents => '1.3.1.24',
      fcConnUnitEventCurrID => '1.3.1.25',

      fcConnUnitRevsTable => '1.3.6.1.2.1.8888.1.1.4',
      fcConnUnitRevsEntry => '1.3.6.1.2.1.8888.1.1.4.1',
      fcConnUnitRevsIndex => '1.3.6.1.2.1.8888.1.1.4.1.1',
      fcConnUnitRevsRevision => '1.3.6.1.2.1.8888.1.1.4.1.2',
      fcConnUnitRevsDescription => '1.3',

      fcConnUnitSensorTable => '1.3.6.1.2.1.8888.1.1.5',
      fcConnUnitSensorEntry => '1.3.6.1.2.1.8888.1.1.5.1',
      fcConnUnitSensorIndex => '1.3.6.1.2.1.8888.1.1.5.1.1',
      fcConnUnitSensorName => '1.3.6.1.2.1.8888.1.1.5.1.2',
      fcConnUnitSensorStatus => '1.3.6.1.2.1.8888.1.1.5.1.3',
      fcConnUnitSensorStatusDefinition => {
          1 => 'unknown',
          2 => 'other',
          3 => 'ok',
          4 => 'warning',
          5 => 'failed',
      },
      fcConnUnitSensorInfo => '1.3.6.1.2.1.8888.1.1.5.1.4',
      fcConnUnitSensorMessage => '1.3.6.1.2.1.8888.1.1.5.1.5',
      fcConnUnitSensorType => '1.3.6.1.2.1.8888.1.1.5.1.6',
      fcConnUnitSensorTypeDefinition => {
          1 => 'unknown',
          2 => 'other',
          3 => 'battery',
          4 => 'fan',
          5 => 'powerSupply',
          6 => 'transmitter',
          7 => 'enclosure',
          8 => 'board',
          9 => 'receiver',
      },
      fcConnUnitSensorCharacteristic => '1.3.6.1.2.1.8888.1.1.5.1.7',
      fcConnUnitSensorCharacteristicDefinition => {
          1 => 'unknown',
          2 => 'other',
          3 => 'temperature',
          4 => 'pressure',
          5 => 'emf',
          6 => 'currentValue',
          7 => 'airflow',
          8 => 'frequency',
          9 => 'power',
      },

      fcConnUnitPortTable => '1.3.6.1.2.1.8888.1.1.6',
      fcConnUnitPortEntry => '1.3.6.1.2.1.8888.1.1.6.1',
      fcConnUnitPortIndex => '1.3.6.1.2.1.8888.1.1.6.1.1',
      fcConnUnitPortType => '1.3.6.1.2.1.8888.1.1.6.1.2',
      fcConnUnitPortFCClassCap => '1.3',
      fcConnUnitPortFCClassOp => '1.3.6.1.2.1.8888.1.1.6.1.4',
      fcConnUnitPortState => '1.3.6.1.2.1.8888.1.1.6.1.5',
      fcConnUnitPortStatus => '1.3.6.1.2.1.8888.1.1.6.1.6',
      fcConnUnitPortTransmitterType => '1.3.6.1.2.1.8888.1.1.6.1.7',
      fcConnUnitPortModuleType => '1.3.6.1.2.1.8888.1.1.6.1.8',
      fcConnUnitPortWwn => '1.3.6.1.2.1.8888.1.1.6.1.9',
      fcConnUnitPortFCId => '1.3.6.1.2.1.8888.1.1.6.1.10',
      fcConnUnitPortSerialNo => '1.3.6.1.2.1.8888.1.1.6.1.11',
      fcConnUnitPortRevision => '1.3.6.1.2.1.8888.1.1.6.1.12',
      fcConnUnitPortVendor => '1.3.6.1.2.1.8888.1.1.6.1.13',
      fcConnUnitPortSpeed => '1.3.6.1.2.1.8888.1.1.6.1.14',
      fcConnUnitPortControl => '1.3.6.1.2.1.8888.1.1.6.1.15',
      fcConnUnitPortName => '1.3.6.1.2.1.8888.1.1.6.1.16',
      fcConnUnitPortPhysicalNumber => '1.3.6.1.2.1.8888.1.1.6.1.17',
      fcConnUnitPortProtocolCap => '1.3.6.1.2.1.8888.1.1.6.1.18',
      fcConnUnitPortProtocolOp => '1.3.6.1.2.1.8888.1.1.6.1.19',
      fcConnUnitPortNodeWwn => '1.3.6.1.2.1.8888.1.1.6.1.20',
      fcConnUnitPortHWState => '1.3.6.1.2.1.8888.1.1.6.1.21',

      fcConnUnitEventTable => '1.3.6.1.2.1.8888.1.1.7',
      fcConnUnitEventEntry => '1.3.6.1.2.1.8888.1.1.7.1',
      fcConnUnitEventIndex => '1.3.6.1.2.1.8888.1.1.7.1.1',
      fcConnUnitREventTime => '1.3.6.1.2.1.8888.1.1.7.1.2',
      fcConnUnitSEventTime => '1.3',
      fcConnUnitEventSeverity => '1.3.6.1.2.1.8888.1.1.7.1.4',
      fcConnUnitEventType => '1.3.6.1.2.1.8888.1.1.7.1.5',
      fcConnUnitEventObject => '1.3.6.1.2.1.8888.1.1.7.1.6',
      fcConnUnitEventDescr => '1.3.6.1.2.1.8888.1.1.7.1.7',

      fcConnUnitLinkTable => '1.3.6.1.2.1.8888.1.1.8',
      fcConnUnitLinkEntry => '1.3.6.1.2.1.8888.1.1.8.1',
      fcConnUnitLinkIndex => '1.3.6.1.2.1.8888.1.1.8.1.1',
      fcConnUnitLinkNodeIdX => '1.3.6.1.2.1.8888.1.1.8.1.2',
      fcConnUnitLinkPortNumberX => '1.3',
      fcConnUnitLinkPortWwnX => '1.3.6.1.2.1.8888.1.1.8.1.4',
      fcConnUnitLinkNodeIdY => '1.3.6.1.2.1.8888.1.1.8.1.5',
      fcConnUnitLinkPortNumberY => '1.3.6.1.2.1.8888.1.1.8.1.6',
      fcConnUnitLinkPortWwnY => '1.3.6.1.2.1.8888.1.1.8.1.7',
      fcConnUnitLinkAgentAddressY => '1.3.6.1.2.1.8888.1.1.8.1.8',
      fcConnUnitLinkAgentAddressTypeY => '1.3.6.1.2.1.8888.1.1.8.1.9',
      fcConnUnitLinkAgentPortY => '1.3.6.1.2.1.8888.1.1.8.1.10',
      fcConnUnitLinkUnitTypeY => '1.3.6.1.2.1.8888.1.1.8.1.11',
      fcConnUnitLinkConnIdY => '1.3.6.1.2.1.8888.1.1.8.1.12',

      fcConnUnitPortStatTable => '1.3.1',
      fcConnUnitPortStatEntry => '1.3.1.1',
      fcConnUnitPortStatIndex => '1.3.1.1.1',
      fcConnUnitPortStatErrs => '1.3.1.1.2',
      fcConnUnitPortStatTxObjects => '1.3',
      fcConnUnitPortStatRxObjects => '1.3.1.1.4',
      fcConnUnitPortStatTxElements => '1.3.1.1.5',
      fcConnUnitPortStatRxElements => '1.3.1.1.6',
      fcConnUnitPortStatBBCreditZero => '1.3.1.1.7',
      fcConnUnitPortStatInputBuffsFull => '1.3.1.1.8',
      fcConnUnitPortStatFBSYFrames => '1.3.1.1.9',
      fcConnUnitPortStatPBSYFrames => '1.3.1.1.10',
      fcConnUnitPortStatFRJTFrames => '1.3.1.1.11',
      fcConnUnitPortStatPRJTFrames => '1.3.1.1.12',
      fcConnUnitPortStatC1RxFrames => '1.3.1.1.13',
      fcConnUnitPortStatC1TxFrames => '1.3.1.1.14',
      fcConnUnitPortStatC1FBSYFrames => '1.3.1.1.15',
      fcConnUnitPortStatC1PBSYFrames => '1.3.1.1.16',
      fcConnUnitPortStatC1FRJTFrames => '1.3.1.1.17',
      fcConnUnitPortStatC1PRJTFrames => '1.3.1.1.18',
      fcConnUnitPortStatC2RxFrames => '1.3.1.1.19',
      fcConnUnitPortStatC2TxFrames => '1.3.1.1.20',
      fcConnUnitPortStatC2FBSYFrames => '1.3.1.1.21',
      fcConnUnitPortStatC2PBSYFrames => '1.3.1.1.22',
      fcConnUnitPortStatC2FRJTFrames => '1.3.1.1.23',
      fcConnUnitPortStatC2PRJTFrames => '1.3.1.1.24',
      fcConnUnitPortStatC3RxFrames => '1.3.1.1.25',
      fcConnUnitPortStatC3TxFrames => '1.3.1.1.26',
      fcConnUnitPortStatC3Discards => '1.3.1.1.27',
      fcConnUnitPortStatRxMcastObjects => '1.3.1.1.28',
      fcConnUnitPortStatTxMcastObjects => '1.3.1.1.29',
      fcConnUnitPortStatRxBcastObjects => '1.30',
      fcConnUnitPortStatTxBcastObjects => '1.31',
      fcConnUnitPortStatRxLinkResets => '1.32',
      fcConnUnitPortStatTxLinkResets => '1.33',
      fcConnUnitPortStatLinkResets => '1.34',
      fcConnUnitPortStatRxOfflineSeqs => '1.35',
      fcConnUnitPortStatTxOfflineSeqs => '1.36',
      fcConnUnitPortStatOfflineSeqs => '1.37',
      fcConnUnitPortStatLinkFailures => '1.38',
      fcConnUnitPortStatInvalidCRC => '1.39',
      fcConnUnitPortStatInvalidTxWords => '1.3.1.1.40',
      fcConnUnitPortStatPSPErrs => '1.3.1.1.41',
      fcConnUnitPortStatLossOfSignal => '1.3.1.1.42',
      fcConnUnitPortStatLossOfSync => '1.3.1.1.43',
      fcConnUnitPortStatInvOrderedSets => '1.3.1.1.44',
      fcConnUnitPortStatFramesTooLong => '1.3.1.1.45',
      fcConnUnitPortStatFramesTooShort => '1.3.1.1.46',
      fcConnUnitPortStatAddressErrs => '1.3.1.1.47',
      fcConnUnitPortStatDelimiterErrs => '1.3.1.1.48',
      fcConnUnitPortStatEncodingErrs => '1.3.1.1.49',

      fcConnUnitSnsMaxRows => '1.3.6.1.2.1.8888.1.1.9.0',
      fcConnUnitSnsTable => '1.3.6.1.2.1.8888.1.4.1',
      fcConnUnitSnsEntry => '1.3.6.1.2.1.8888.1.4.1.1',
      fcConnUnitSnsPortIndex => '1.3.6.1.2.1.8888.1.4.1.1.1',
      fcConnUnitSnsPortIdentifier => '1.3.6.1.2.1.8888.1.4.1.1.2',
      fcConnUnitSnsPortName => '1.3',
      fcConnUnitSnsNodeName => '1.3.6.1.2.1.8888.1.4.1.1.4',
      fcConnUnitSnsClassOfSvc => '1.3.6.1.2.1.8888.1.4.1.1.5',
      fcConnUnitSnsNodeIPAddress => '1.3.6.1.2.1.8888.1.4.1.1.6',
      fcConnUnitSnsProcAssoc => '1.3.6.1.2.1.8888.1.4.1.1.7',
      fcConnUnitSnsFC4Type => '1.3.6.1.2.1.8888.1.4.1.1.8',
      fcConnUnitSnsPortType => '1.3.6.1.2.1.8888.1.4.1.1.9',
      fcConnUnitSnsPortIPAddress => '1.3.6.1.2.1.8888.1.4.1.1.10',
      fcConnUnitSnsFabricPortName => '1.3.6.1.2.1.8888.1.4.1.1.11',
      fcConnUnitSnsHardAddress => '1.3.6.1.2.1.8888.1.4.1.1.12',
      fcConnUnitSnsSymbolicPortName => '1.3.6.1.2.1.8888.1.4.1.1.13',
      fcConnUnitSnsSymbolicNodeName => '1.3.6.1.2.1.8888.1.4.1.1.14',
  },
  'FCEOS-MIB' => {
      fcEosSysCurrentDate => '1.3.6.1.4.1.289.2.1.1.2.1.1.0',
      fcEosSysBootDate => '1.3.6.1.4.1.289.2.1.1.2.1.2.0',
      fcEosSysFirmwareVersion => '1.3.6.1.4.1.289.2.1.1.2.1.3.0',
      fcEosSysTypeNum => '1.3.6.1.4.1.289.2.1.1.2.1.4.0',
      fcEosSysModelNum => '1.3.6.1.4.1.289.2.1.1.2.1.5.0',
      fcEosSysMfg => '1.3.6.1.4.1.289.2.1.1.2.1.6.0',
      fcEosSysPlantOfMfg => '1.3.6.1.4.1.289.2.1.1.2.1.7.0',
      fcEosSysEcLevel => '1.3.6.1.4.1.289.2.1.1.2.1.8.0',
      fcEosSysSerialNum => '1.3.6.1.4.1.289.2.1.1.2.1.9.0',
      fcEosSysOperStatus => '1.3.6.1.4.1.289.2.1.1.2.1.10.0',
      fcEosSysOperStatusDefinition => {
          1 => 'operational',
          2 => 'redundant-failure',
          3 => 'minor-failure',
          4 => 'major-failure',
          5 => 'not-operational',
      },
      fcEosSysState => '1.3.6.1.4.1.289.2.1.1.2.1.11.0',
      fcEosSysAdmStatus => '1.3.6.1.4.1.289.2.1.1.2.1.12.0',
      fcEosSysConfigSpeed => '1.3.6.1.4.1.289.2.1.1.2.1.13.0',
      fcEosSysOpenTrunking => '1.3.6.1.4.1.289.2.1.1.2.1.14.0',

      fcEosFruTable => '1.3.6.1.4.1.289.2.1.1.2.2.1',
      fcEosFruEntry => '1.3.6.1.4.1.289.2.1.1.2.2.1.1',
      fcEosFruCode => '1.3.6.1.4.1.289.2.1.1.2.2.1.1.1',
      fcEosFruCodeDefinition => {
          1 => 'fru-bkplane', # Backplane 
          2 => 'fru-ctp', # Control Processor card 
          3 => 'fru-sbar', # Serial Crossbar 
          4 => 'fru-fan2', # Center fan module 
          5 => 'fru-fan', # Fan module 
          6 => 'fru-power', # Power supply module 
          7 => 'fru-reserved', # Reserved, not used 
          8 => 'fru-glsl', # Longwave, Single-Mode, LC connector, 1 Gig 
          9 => 'fru-gsml', # Shortwave, Multi-Mode, LC connector, 1 Gig 
          10 => 'fru-gxxl', # Mixed, LC connector, 1 Gig 
          11 => 'fru-gsf1', # SFO pluggable, 1 Gig 
          12 => 'fru-gsf2', # SFO pluggable, 2 Gig 
          13 => 'fru-glsr', # Longwave, Single-Mode, MT-RJ connector, 1 Gig 
          14 => 'fru-gsmr', # Shortwave, Multi-Mode, MT-RJ connector, 1 Gig 
          15 => 'fru-gxxr', # Mixed, MT-RJ connector, 1 Gig 
          16 => 'fru-fint1', # F-Port, internal, 1 Gig 
      },
      fcEosFruPosition => '1.3.6.1.4.1.289.2.1.1.2.2.1.1.2',
      fcEosFruStatus => '1.3.6.1.4.1.289.2.1.1.2.2.1.1.3',
      fcEosFruStatusDefinition => {
          0 => 'unknown',
          1 => 'active',
          2 => 'backup',
          3 => 'update-busy',
          4 => 'failed',
      },
      fcEosFruPartNumber => '1.3.6.1.4.1.289.2.1.1.2.2.1.1.4',
      fcEosFruSerialNumber => '1.3.6.1.4.1.289.2.1.1.2.2.1.1.5',
      fcEosFruPowerOnHours => '1.3.6.1.4.1.289.2.1.1.2.2.1.1.6',
      fcEosFruTestDate => '1.3.6.1.4.1.289.2.1.1.2.2.1.1.7',

      fcEosTATable => '1.3.6.1.4.1.289.2.1.1.2.6.1',
      fcEosTAEntry => '1.3.6.1.4.1.289.2.1.1.2.6.1.1',
      fcEosTAIndex => '1.3.6.1.4.1.289.2.1.1.2.6.1.1.1',
      fcEosTAName => '1.3.6.1.4.1.289.2.1.1.2.6.1.1.2',
      fcEosTAState => '1.3.6.1.4.1.289.2.1.1.2.6.1.1.3',
      fcEosTAType => '1.3.6.1.4.1.289.2.1.1.2.6.1.1.4',
      fcEosTAPortType => '1.3.6.1.4.1.289.2.1.1.2.6.1.1.5',
      fcEosTAPortList => '1.3.6.1.4.1.289.2.1.1.2.6.1.1.6',
      fcEosTAInterval => '1.3.6.1.4.1.289.2.1.1.2.6.1.1.7',
      fcEosTATriggerValue => '1.3.6.1.4.1.289.2.1.1.2.6.1.1.8',
      fcEosTTADirection => '1.3.6.1.4.1.289.2.1.1.2.6.1.1.9',
      fcEosTTATriggerDuration => '1.3.6.1.4.1.289.2.1.1.2.6.1.1.10',
      fcEosCTACounter => '1.3.6.1.4.1.289.2.1.1.2.6.1.1.11',
  },
  'F5-BIGIP-SYSTEM-MIB' => {
# http://support.f5.com/kb/en-us/products/big-ip_ltm/manuals/product/bigip9_2_2mgmt/BIG-IP_9_2_2nsm_guide-16-1.html
# http://support.f5.com/kb/en-us/products/big-ip_ltm/manuals/product/tmos_management_guide_10_0_0/tmos_appendix_a_traps.html
# http://support.f5.com/kb/en-us/solutions/public/9000/400/sol9476.html
      sysStatMemoryTotal => '1.3.6.1.4.1.3375.2.1.1.2.1.44.0',
      sysStatMemoryUsed => '1.3.6.1.4.1.3375.2.1.1.2.1.45.0',
      sysHostMemoryTotal => '1.3.6.1.4.1.3375.2.1.7.1.1.0',
      sysHostMemoryUsed => '1.3.6.1.4.1.3375.2.1.7.1.2.0',
# http://www.midnight-visions.de/f5-bigip-und-snmp/
      sysStatTmTotalCycles => '1.3.6.1.4.1.3375.2.1.1.2.1.41.0',
      sysStatTmIdleCycles => '1.3.6.1.4.1.3375.2.1.1.2.1.42.0',
      sysStatTmSleepCycles => '1.3.6.1.4.1.3375.2.1.1.2.1.43.0',

      sysCpuNumber => '1.3.6.1.4.1.3375.2.1.3.1.1.0',
      sysCpuTable => '1.3.6.1.4.1.3375.2.1.3.1.2',
      sysCpuEntry => '1.3.6.1.4.1.3375.2.1.3.1.2.1',
      sysCpuIndex => '1.3.6.1.4.1.3375.2.1.3.1.2.1.1',
      sysCpuTemperature => '1.3.6.1.4.1.3375.2.1.3.1.2.1.2',
      sysCpuFanSpeed => '1.3.6.1.4.1.3375.2.1.3.1.2.1.3',
      sysCpuName => '1.3.6.1.4.1.3375.2.1.3.1.2.1.4',
      sysCpuSlot => '1.3.6.1.4.1.3375.2.1.3.1.2.1.5',

      sysChassisFan => '1.3.6.1.4.1.3375.2.1.3.2.1',
      sysChassisFanNumber => '1.3.6.1.4.1.3375.2.1.3.2.1.1.0',
      sysChassisFanTable => '1.3.6.1.4.1.3375.2.1.3.2.1.2',
      sysChassisFanEntry => '1.3.6.1.4.1.3375.2.1.3.2.1.2.1',
      sysChassisFanIndex => '1.3.6.1.4.1.3375.2.1.3.2.1.2.1.1',
      sysChassisFanStatus => '1.3.6.1.4.1.3375.2.1.3.2.1.2.1.2',
      sysChassisFanStatusDefinition => {
          0 => 'bad',
          1 => 'good',
          2 => 'notpresent',
      },
      sysChassisFanSpeed => '1.3.6.1.4.1.3375.2.1.3.2.1.2.1.3',

      sysChassisPowerSupply => '1.3.6.1.4.1.3375.2.1.3.2.2',
      sysChassisPowerSupplyNumber => '1.3.6.1.4.1.3375.2.1.3.2.2.1.0',
      sysChassisPowerSupplyTable => '1.3.6.1.4.1.3375.2.1.3.2.2.2',
      sysChassisPowerSupplyEntry => '1.3.6.1.4.1.3375.2.1.3.2.2.2.1',
      sysChassisPowerSupplyIndex => '1.3.6.1.4.1.3375.2.1.3.2.2.2.1.1',
      sysChassisPowerSupplyStatus => '1.3.6.1.4.1.3375.2.1.3.2.2.2.1.2',
      sysChassisPowerSupplyStatusDefinition => {
          0 => 'bad',
          1 => 'good',
          2 => 'notpresent',
      },

      sysChassisTemp => '1.3.6.1.4.1.3375.2.1.3.2.3',
      sysChassisTempNumber => '1.3.6.1.4.1.3375.2.1.3.2.3.1.0',
      sysChassisTempTable => '1.3.6.1.4.1.3375.2.1.3.2.3.2',
      sysChassisTempEntry => '1.3.6.1.4.1.3375.2.1.3.2.3.2.1',
      sysChassisTempIndex => '1.3.6.1.4.1.3375.2.1.3.2.3.2.1.1',
      sysChassisTempTemperature => '1.3.6.1.4.1.3375.2.1.3.2.3.2.1.2',

      sysProduct => '1.3.6.1.4.1.3375.2.1.4',
      sysProductName => '1.3.6.1.4.1.3375.2.1.4.1.0',
      sysProductVersion => '1.3.6.1.4.1.3375.2.1.4.2.0',
      sysProductBuild => '1.3.6.1.4.1.3375.2.1.4.3.0',
      sysProductEdition => '1.3.6.1.4.1.3375.2.1.4.4.0',
      sysProductDate => '1.3.6.1.4.1.3375.2.1.4.5.0',

      sysSubMemory => '1.3.6.1.4.1.3375.2.1.5',
      sysSubMemoryResetStats => '1.3.6.1.4.1.3375.2.1.5.1.0',
      sysSubMemoryNumber => '1.3.6.1.4.1.3375.2.1.5.2.0',
      sysSubMemoryTable => '1.3.6.1.4.1.3375.2.1.5.3',
      sysSubMemoryEntry => '1.3.6.1.4.1.3375.2.1.5.3.1',
      sysSubMemoryName => '1.3.6.1.4.1.3375.2.1.5.3.1.1',
      sysSubMemoryAllocated => '1.3.6.1.4.1.3375.2.1.5.3.1.2',
      sysSubMemoryMaxAllocated => '1.3.6.1.4.1.3375.2.1.5.3.1.3',
      sysSubMemorySize => '1.3.6.1.4.1.3375.2.1.5.3.1.4',

      sysSystem => '1.3.6.1.4.1.3375.2.1.6',
      sysSystemName => '1.3.6.1.4.1.3375.2.1.6.1.0',
      sysSystemNodeName => '1.3.6.1.4.1.3375.2.1.6.2.0',
      sysSystemRelease => '1.3.6.1.4.1.3375.2.1.6.3.0',
      sysSystemVersion => '1.3.6.1.4.1.3375.2.1.6.4.0',
      sysSystemMachine => '1.3.6.1.4.1.3375.2.1.6.5.0',
      sysSystemUptime => '1.3.6.1.4.1.3375.2.1.6.6.0',
      bigipSystemGroups => '1.3.6.1.4.1.3375.2.5.2.1',
  },
  'HP-ICF-CHASSIS-MIB' => {
      hpicfSensorTable => '1.3.6.1.4.1.11.2.14.11.1.2.6',
      hpicfSensorEntry => '1.3.6.1.4.1.11.2.14.11.1.2.6.1',
      hpicfSensorIndex => '1.3.6.1.4.1.11.2.14.11.1.2.6.1.1',
      hpicfSensorObjectId => '1.3.6.1.4.1.11.2.14.11.1.2.6.1.2',
      hpicfSensorObjectIdDefinition => {
          1 => 'fan sensor',
          2 => 'power supply',
          3 => 'redundant power supply',
          4 => 'over-temperature sensor',
      },
      hpicfSensorNumber => '1.3.6.1.4.1.11.2.14.11.1.2.6.1.3',
      hpicfSensorStatus => '1.3.6.1.4.1.11.2.14.11.1.2.6.1.4',
      hpicfSensorStatusDefinition => {
          1 => 'unknown',
          2 => 'bad',
          3 => 'warning',
          4 => 'good',
          5 => 'notPresent',
      },
      hpicfSensorWarnings => '1.3.6.1.4.1.11.2.14.11.1.2.6.1.5',
      hpicfSensorFailures => '1.3.6.1.4.1.11.2.14.11.1.2.6.1.6',
      hpicfSensorDescr => '1.3.6.1.4.1.11.2.14.11.1.2.6.1.7',
#hpicfSensorObjectId.1 = icfFanSensor
#hpicfSensorObjectId.2 = icfPowerSupplySensor
#hpicfSensorObjectId.3 = icfPowerSupplySensor
#hpicfSensorObjectId.4 = icfTemperatureSensor

#hpicfSensorDescr.1 = Fan Sensor
#hpicfSensorDescr.2 = Power Supply Sensor
#hpicfSensorDescr.3 = Redundant Power Supply Sensor
#hpicfSensorDescr.4 = Over-temperature Sensor

  },
  'F5-BIGIP-LOCAL-MIB' => {
    ltmPoolNumber => '1.3.6.1.4.1.3375.2.2.5.1.1.0',
    ltmPoolTable => '1.3.6.1.4.1.3375.2.2.5.1.2',
    ltmPoolEntry => '1.3.6.1.4.1.3375.2.2.5.1.2.1',
    ltmPoolName => '1.3.6.1.4.1.3375.2.2.5.1.2.1.1',
    ltmPoolLbMode => '1.3.6.1.4.1.3375.2.2.5.1.2.1.2',
    ltmPoolActionOnServiceDown => '1.3.6.1.4.1.3375.2.2.5.1.2.1.3',
    ltmPoolMinUpMembers => '1.3.6.1.4.1.3375.2.2.5.1.2.1.4',
    ltmPoolMinUpMembersEnable => '1.3.6.1.4.1.3375.2.2.5.1.2.1.5',
    ltmPoolMinUpMemberAction => '1.3.6.1.4.1.3375.2.2.5.1.2.1.6',
    ltmPoolMinActiveMembers => '1.3.6.1.4.1.3375.2.2.5.1.2.1.7',
    ltmPoolActiveMemberCnt => '1.3.6.1.4.1.3375.2.2.5.1.2.1.8',
    ltmPoolDisallowSnat => '1.3.6.1.4.1.3375.2.2.5.1.2.1.9',
    ltmPoolDisallowNat => '1.3.6.1.4.1.3375.2.2.5.1.2.1.10',
    ltmPoolSimpleTimeout => '1.3.6.1.4.1.3375.2.2.5.1.2.1.11',
    ltmPoolIpTosToClient => '1.3.6.1.4.1.3375.2.2.5.1.2.1.12',
    ltmPoolIpTosToServer => '1.3.6.1.4.1.3375.2.2.5.1.2.1.13',
    ltmPoolLinkQosToClient => '1.3.6.1.4.1.3375.2.2.5.1.2.1.14',
    ltmPoolLinkQosToServer => '1.3.6.1.4.1.3375.2.2.5.1.2.1.15',
    ltmPoolDynamicRatioSum => '1.3.6.1.4.1.3375.2.2.5.1.2.1.16',
    ltmPoolMonitorRule => '1.3.6.1.4.1.3375.2.2.5.1.2.1.17',
    ltmPoolAvailabilityState => '1.3.6.1.4.1.3375.2.2.5.1.2.1.18',
    ltmPoolEnabledState => '1.3.6.1.4.1.3375.2.2.5.1.2.1.19',
    ltmPoolDisabledParentType => '1.3.6.1.4.1.3375.2.2.5.1.2.1.20',
    ltmPoolStatusReason => '1.3.6.1.4.1.3375.2.2.5.1.2.1.21',
    ltmPoolSlowRampTime => '1.3.6.1.4.1.3375.2.2.5.1.2.1.22',
    ltmPoolMemberCnt => '1.3.6.1.4.1.3375.2.2.5.1.2.1.23',

    ltmPoolMemberTable => '1.3.6.1.4.1.3375.2.2.5.3.2',
    ltmPoolMemberEntry => '1.3.6.1.4.1.3375.2.2.5.3.2.1',
    ltmPoolMemberPoolName => '1.3.6.1.4.1.3375.2.2.5.3.2.1.1',
    ltmPoolMemberAddrType => '1.3.6.1.4.1.3375.2.2.5.3.2.1.2',
    ltmPoolMemberAddr => '1.3.6.1.4.1.3375.2.2.5.3.2.1.3',
    ltmPoolMemberPort => '1.3.6.1.4.1.3375.2.2.5.3.2.1.4',
    ltmPoolMemberConnLimit => '1.3.6.1.4.1.3375.2.2.5.3.2.1.5',
    ltmPoolMemberRatio => '1.3.6.1.4.1.3375.2.2.5.3.2.1.6',
    ltmPoolMemberWeight => '1.3.6.1.4.1.3375.2.2.5.3.2.1.7',
    ltmPoolMemberPriority => '1.3.6.1.4.1.3375.2.2.5.3.2.1.8',
    ltmPoolMemberDynamicRatio => '1.3.6.1.4.1.3375.2.2.5.3.2.1.9',
    ltmPoolMemberMonitorState => '1.3.6.1.4.1.3375.2.2.5.3.2.1.10',
    ltmPoolMemberMonitorStateDefinition => 'F5-BIGIP-LOCAL-MIB::ltmPoolMemberMonitorState',
    ltmPoolMemberMonitorStatus => '1.3.6.1.4.1.3375.2.2.5.3.2.1.11',
    ltmPoolMemberMonitorStatusDefinition => 'F5-BIGIP-LOCAL-MIB::ltmPoolMemberMonitorStatus',
    ltmPoolMemberNewSessionEnable => '1.3.6.1.4.1.3375.2.2.5.3.2.1.12',
    ltmPoolMemberSessionStatus => '1.3.6.1.4.1.3375.2.2.5.3.2.1.13',
    ltmPoolMemberMonitorRule => '1.3.6.1.4.1.3375.2.2.5.3.2.1.14',
    ltmPoolMemberAvailabilityState => '1.3.6.1.4.1.3375.2.2.5.3.2.1.15',
    ltmPoolMemberEnabledState => '1.3.6.1.4.1.3375.2.2.5.3.2.1.16',
    ltmPoolMemberDisabledParentType => '1.3.6.1.4.1.3375.2.2.5.3.2.1.17',
    ltmPoolMemberStatusReason => '1.3.6.1.4.1.3375.2.2.5.3.2.1.18',

    ltmPoolStatusNumber => '1.3.6.1.4.1.3375.2.2.5.5.1.0',
    ltmPoolStatusTable => '1.3.6.1.4.1.3375.2.2.5.5.2',
    ltmPoolStatusEntry => '1.3.6.1.4.1.3375.2.2.5.5.2.1',
    ltmPoolStatusName => '1.3.6.1.4.1.3375.2.2.5.5.2.1.1',
    ltmPoolStatusAvailState => '1.3.6.1.4.1.3375.2.2.5.5.2.1.2',
    ltmPoolStatusAvailStateDefinition => 'F5-BIGIP-LOCAL-MIB::ltmPoolStatusAvailState',
    ltmPoolStatusEnabledState => '1.3.6.1.4.1.3375.2.2.5.5.2.1.3',
    ltmPoolStatusEnabledStateDefinition => 'F5-BIGIP-LOCAL-MIB::ltmPoolStatusEnabledState',
    ltmPoolStatusParentType => '1.3.6.1.4.1.3375.2.2.5.5.2.1.4',
    ltmPoolStatusDetailReason => '1.3.6.1.4.1.3375.2.2.5.5.2.1.5',

    ltmPoolMbrStatusNumber => '1.3.6.1.4.1.3375.2.2.5.6.1.0',
    ltmPoolMbrStatusTable => '1.3.6.1.4.1.3375.2.2.5.6.2',
    ltmPoolMbrStatusEntry => '1.3.6.1.4.1.3375.2.2.5.6.2.1',
    ltmPoolMbrStatusPoolName => '1.3.6.1.4.1.3375.2.2.5.6.2.1.1',
    ltmPoolMbrStatusAddrType => '1.3.6.1.4.1.3375.2.2.5.6.2.1.2',
    ltmPoolMbrStatusAddr => '1.3.6.1.4.1.3375.2.2.5.6.2.1.3',
    ltmPoolMbrStatusPort => '1.3.6.1.4.1.3375.2.2.5.6.2.1.4',
    ltmPoolMbrStatusAvailState => '1.3.6.1.4.1.3375.2.2.5.6.2.1.5',
    ltmPoolMbrStatusAvailStateDefinition => 'F5-BIGIP-LOCAL-MIB::ltmPoolMbrStatusAvailState',
    ltmPoolMbrStatusEnabledState => '1.3.6.1.4.1.3375.2.2.5.6.2.1.6',
    ltmPoolMbrStatusEnabledStateDefinition => 'F5-BIGIP-LOCAL-MIB::ltmPoolMbrStatusEnabledState',
    ltmPoolMbrStatusParentType => '1.3.6.1.4.1.3375.2.2.5.6.2.1.7',
    ltmPoolMbrStatusDetailReason => '1.3.6.1.4.1.3375.2.2.5.6.2.1.8',
    ltmPoolMbrStatusNodeName => '1.3.6.1.4.1.3375.2.2.5.6.2.1.9',

    ltmNodeAddrStatusTable => '1.3.6.1.4.1.3375.2.2.4.3.2',
    ltmNodeAddrStatusEntry => '1.3.6.1.4.1.3375.2.2.4.3.2.1',
    ltmNodeAddrStatusAddrType => '1.3.6.1.4.1.3375.2.2.4.3.2.1.1',
    ltmNodeAddrStatusAddr => '1.3.6.1.4.1.3375.2.2.4.3.2.1.2',
    ltmNodeAddrStatusAvailState => '1.3.6.1.4.1.3375.2.2.4.3.2.1.3',
    ltmNodeAddrStatusEnabledState => '1.3.6.1.4.1.3375.2.2.4.3.2.1.4',
    ltmNodeAddrStatusParentType => '1.3.6.1.4.1.3375.2.2.4.3.2.1.5',
    ltmNodeAddrStatusDetailReason => '1.3.6.1.4.1.3375.2.2.4.3.2.1.6',
    ltmNodeAddrStatusName => '1.3.6.1.4.1.3375.2.2.4.3.2.1.7',
  },
  'LOAD-BAL-SYSTEM-MIB' => {
    poolTable => '1.3.6.1.4.1.3375.1.1.7.2',
    poolEntry => '1.3.6.1.4.1.3375.1.1.7.2.1',
    poolName => '1.3.6.1.4.1.3375.1.1.7.2.1.1',
    poolLBMode => '1.3.6.1.4.1.3375.1.1.7.2.1.2',
    poolDependent => '1.3.6.1.4.1.3375.1.1.7.2.1.3',
    poolMemberQty => '1.3.6.1.4.1.3375.1.1.7.2.1.4',
    poolBitsin => '1.3.6.1.4.1.3375.1.1.7.2.1.5',
    poolBitsout => '1.3.6.1.4.1.3375.1.1.7.2.1.6',
    poolBitsinHi32 => '1.3.6.1.4.1.3375.1.1.7.2.1.7',
    poolBitsoutHi32 => '1.3.6.1.4.1.3375.1.1.7.2.1.8',
    poolPktsin => '1.3.6.1.4.1.3375.1.1.7.2.1.9',
    poolPktsout => '1.3.6.1.4.1.3375.1.1.7.2.1.10',
    poolPktsinHi32 => '1.3.6.1.4.1.3375.1.1.7.2.1.11',
    poolPktsoutHi32 => '1.3.6.1.4.1.3375.1.1.7.2.1.12',
    poolMaxConn => '1.3.6.1.4.1.3375.1.1.7.2.1.13',
    poolCurrentConn => '1.3.6.1.4.1.3375.1.1.7.2.1.14',
    poolTotalConn => '1.3.6.1.4.1.3375.1.1.7.2.1.15',
    poolPersistMode => '1.3.6.1.4.1.3375.1.1.7.2.1.16',
    poolSSLTimeout => '1.3.6.1.4.1.3375.1.1.7.2.1.17',
    poolSimpleTimeout => '1.3.6.1.4.1.3375.1.1.7.2.1.18',
    poolSimpleMask => '1.3.6.1.4.1.3375.1.1.7.2.1.19',
    poolStickyMask => '1.3.6.1.4.1.3375.1.1.7.2.1.20',
    poolCookieMode => '1.3.6.1.4.1.3375.1.1.7.2.1.21',
    poolCookieExpiration => '1.3.6.1.4.1.3375.1.1.7.2.1.22',
    poolCookieHashName => '1.3.6.1.4.1.3375.1.1.7.2.1.23',
    poolCookieHashOffset => '1.3.6.1.4.1.3375.1.1.7.2.1.24',
    poolCookieHashLength => '1.3.6.1.4.1.3375.1.1.7.2.1.25',
    poolMinActiveMembers => '1.3.6.1.4.1.3375.1.1.7.2.1.26',
    poolActiveMemberCount => '1.3.6.1.4.1.3375.1.1.7.2.1.27',
    poolPersistMirror => '1.3.6.1.4.1.3375.1.1.7.2.1.28',
    poolFallbackHost => '1.3.6.1.4.1.3375.1.1.7.2.1.29',
    poolMemberTable => '1.3.6.1.4.1.3375.1.1.8.2',
    poolMemberEntry => '1.3.6.1.4.1.3375.1.1.8.2.1',
    poolMemberPoolName => '1.3.6.1.4.1.3375.1.1.8.2.1.1',
    poolMemberIpAddress => '1.3.6.1.4.1.3375.1.1.8.2.1.2',
    poolMemberPort => '1.3.6.1.4.1.3375.1.1.8.2.1.3',
    poolMemberMaintenance => '1.3.6.1.4.1.3375.1.1.8.2.1.4',
    poolMemberRatio => '1.3.6.1.4.1.3375.1.1.8.2.1.5',
    poolMemberPriority => '1.3.6.1.4.1.3375.1.1.8.2.1.6',
    poolMemberWeight => '1.3.6.1.4.1.3375.1.1.8.2.1.7',
    poolMemberRipeness => '1.3.6.1.4.1.3375.1.1.8.2.1.8',
    poolMemberBitsin => '1.3.6.1.4.1.3375.1.1.8.2.1.9',
    poolMemberBitsout => '1.3.6.1.4.1.3375.1.1.8.2.1.10',
    poolMemberBitsinHi32 => '1.3.6.1.4.1.3375.1.1.8.2.1.11',
    poolMemberBitsoutHi32 => '1.3.6.1.4.1.3375.1.1.8.2.1.12',
    poolMemberPktsin => '1.3.6.1.4.1.3375.1.1.8.2.1.13',
    poolMemberPktsout => '1.3.6.1.4.1.3375.1.1.8.2.1.14',
    poolMemberPktsinHi32 => '1.3.6.1.4.1.3375.1.1.8.2.1.15',
    poolMemberPktsoutHi32 => '1.3.6.1.4.1.3375.1.1.8.2.1.16',
    poolMemberConnLimit => '1.3.6.1.4.1.3375.1.1.8.2.1.17',
    poolMemberMaxConn => '1.3.6.1.4.1.3375.1.1.8.2.1.18',
    poolMemberCurrentConn => '1.3.6.1.4.1.3375.1.1.8.2.1.19',
    poolMemberTotalConn => '1.3.6.1.4.1.3375.1.1.8.2.1.20',
    poolMemberStatus => '1.3.6.1.4.1.3375.1.1.8.2.1.21',
    poolMemberIpStatus => '1.3.6.1.4.1.3375.1.1.8.2.1.22',
  },
  'OLD-STATISTICS-MIB' => {
      hpSwitchCpuStat => '1.3.6.1.2.1.1.7.11.12.9.6.1.0',  # "The CPU utilization in percent(%)."
  },
  'STATISTICS-MIB' => {
      hpSwitchCpuStat => '1.3.6.1.4.1.11.2.14.11.5.1.9.6.1.0',  # "The CPU utilization in percent(%)."
  },
  'OLD-NETSWITCH-MIB' => {
      # hpLocalMemTotalBytes   1.3.6.1.4.1.11.2.14.11.5.1.1.2.1.1.1.5
      # hpLocalMemFreeBytes    1.3.6.1.4.1.11.2.14.11.5.1.1.2.1.1.1.6
      # hpLocalMemAllocBytes   1.3.6.1.4.1.11.2.14.11.5.1.1.2.1.1.1.7
      hpLocalMemTable => '1.3.6.1.2.1.1.7.11.12.1.2.1.1',
      hpLocalMemEntry => '1.3.6.1.2.1.1.7.11.12.1.2.1.1.1',
      hpLocalMemSlotIndex => '1.3.6.1.2.1.1.7.11.12.1.2.1.1.1.1',
      hpLocalMemSlabCnt => '1.3.6.1.2.1.1.7.11.12.1.2.1.1.1.2',
      hpLocalMemFreeSegCnt => '1.3.6.1.2.1.1.7.11.12.1.2.1.1.1.3',
      hpLocalMemAllocSegCnt => '1.3.6.1.2.1.1.7.11.12.1.2.1.1.1.4',
      hpLocalMemTotalBytes => '1.3.6.1.2.1.1.7.11.12.1.2.1.1.1.5',
      hpLocalMemFreeBytes => '1.3.6.1.2.1.1.7.11.12.1.2.1.1.1.6',
      hpLocalMemAllocBytes => '1.3.6.1.2.1.1.7.11.12.1.2.1.1.1.7',
      hpGlobalMemTable => '1.3.6.1.2.1.1.7.11.12.1.2.2.1',
      hpGlobalMemEntry => '1.3.6.1.2.1.1.7.11.12.1.2.2.1.1',
      hpGlobalMemSlotIndex => '1.3.6.1.2.1.1.7.11.12.1.2.2.1.1.1',
      hpGlobalMemSlabCnt => '1.3.6.1.2.1.1.7.11.12.1.2.2.1.1.2',
      hpGlobalMemFreeSegCnt => '1.3.6.1.2.1.1.7.11.12.1.2.2.1.1.3',
      hpGlobalMemAllocSegCnt => '1.3.6.1.2.1.1.7.11.12.1.2.2.1.1.4',
      hpGlobalMemTotalBytes => '1.3.6.1.2.1.1.7.11.12.1.2.2.1.1.5',
      hpGlobalMemFreeBytes => '1.3.6.1.2.1.1.7.11.12.1.2.2.1.1.6',
      hpGlobalMemAllocBytes => '1.3.6.1.2.1.1.7.11.12.1.2.2.1.1.7',
  },
  'NETSWITCH-MIB' => { #evt moderner
      hpLocalMemTable => '1.3.6.1.4.1.11.2.14.11.5.1.1.2.1.1',
      hpLocalMemEntry => '1.3.6.1.4.1.11.2.14.11.5.1.1.2.1.1.1',
      hpLocalMemSlotIndex => '1.3.6.1.4.1.11.2.14.11.5.1.1.2.1.1.1.1',
      hpLocalMemSlabCnt => '1.3.6.1.4.1.11.2.14.11.5.1.1.2.1.1.1.2',
      hpLocalMemFreeSegCnt => '1.3.6.1.4.1.11.2.14.11.5.1.1.2.1.1.1.3',
      hpLocalMemAllocSegCnt => '1.3.6.1.4.1.11.2.14.11.5.1.1.2.1.1.1.4',
      hpLocalMemTotalBytes => '1.3.6.1.4.1.11.2.14.11.5.1.1.2.1.1.1.5',
      hpLocalMemFreeBytes => '1.3.6.1.4.1.11.2.14.11.5.1.1.2.1.1.1.6',
      hpLocalMemAllocBytes => '1.3.6.1.4.1.11.2.14.11.5.1.1.2.1.1.1.7',
      hpGlobalMemTable => '1.3.6.1.4.1.11.2.14.11.5.1.1.2.2.1',
      hpGlobalMemEntry => '1.3.6.1.4.1.11.2.14.11.5.1.1.2.2.1.1',
      hpGlobalMemSlotIndex => '1.3.6.1.4.1.11.2.14.11.5.1.1.2.2.1.1.1',
      hpGlobalMemSlabCnt => '1.3.6.1.4.1.11.2.14.11.5.1.1.2.2.1.1.2',
      hpGlobalMemFreeSegCnt => '1.3.6.1.4.1.11.2.14.11.5.1.1.2.2.1.1.3',
      hpGlobalMemAllocSegCnt => '1.3.6.1.4.1.11.2.14.11.5.1.1.2.2.1.1.4',
      hpGlobalMemTotalBytes => '1.3.6.1.4.1.11.2.14.11.5.1.1.2.2.1.1.5',
      hpGlobalMemFreeBytes => '1.3.6.1.4.1.11.2.14.11.5.1.1.2.2.1.1.6',
      hpGlobalMemAllocBytes => '1.3.6.1.4.1.11.2.14.11.5.1.1.2.2.1.1.7',
  },
  'CHECKPOINT-MIB' => { #evt moderner
      svnProcGroup => '1.3.6.1.4.1.2620.1.6.7.2',
      procUsage => '1.3.6.1.4.1.2620.1.6.7.2.4.0',
      procQueue => '1.3.6.1.4.1.2620.1.6.7.2.5.0',

      haGroup => '1.3.6.1.4.1.2620.1.5',
      haProdName => '1.3.6.1.4.1.2620.1.5.1.0',
      haInstalled => '1.3.6.1.4.1.2620.1.5.2.0',
      haVerMajor => '1.3.6.1.4.1.2620.1.5.3.0',
      haVerMinor => '1.3.6.1.4.1.2620.1.5.4.0',
      haStarted => '1.3.6.1.4.1.2620.1.5.5.0',
      haState => '1.3.6.1.4.1.2620.1.5.6.0',
      haBlockState => '1.3.6.1.4.1.2620.1.5.7.0',
      haIdentifier => '1.3.6.1.4.1.2620.1.5.8.0',
      haProtoVersion => '1.3.6.1.4.1.2620.1.5.10.0',
      haWorkMode => '1.3.6.1.4.1.2620.1.5.11.0',
      haVersionSting => '1.3.6.1.4.1.2620.1.5.14.0',
      haStatCode => '1.3.6.1.4.1.2620.1.5.101.0',
      haStatShort => '1.3.6.1.4.1.2620.1.5.102.0',
      haStatLong => '1.3.6.1.4.1.2620.1.5.103.0',
      haServicePack => '1.3.6.1.4.1.2620.1.5.999.0',

      fwGroup => ' 1.3.6.1.4.1.2620.1.1',
      fwModuleState => '1.3.6.1.4.1.2620.1.1.1.0',
      fwFilterName => '1.3.6.1.4.1.2620.1.1.2.0',

      fwPolicyStatGroup => '1.3.6.1.4.1.2620.1.1.25',
      fwPolicyName => '1.3.6.1.4.1.2620.1.1.25.1.0',
      fwInstallTime => '1.3.6.1.4.1.2620.1.1.25.2.0',
      fwNumConn => '1.3.6.1.4.1.2620.1.1.25.3.0',
      fwPeakNumConn => '1.3.6.1.4.1.2620.1.1.25.4.0',

      svnStatShortDescr => '1.3.6.1.4.1.2620.1.6.102.0',
      svnStatLongDescr => '1.3.6.1.4.1.2620.1.6.103.0',

      mgStatShortDescr => '1.3.6.1.4.1.2620.1.7.102.0',
      mgStatLongDescr => '1.3.6.1.4.1.2620.1.7.103.0',
  },
};

$NWC::Device::definitions = {
  'CISCO-ENVMON-MIB' => {
     ciscoEnvMonState => {
       1 => 'normal',
       2 => 'warning',
       3 => 'critical',
       4 => 'shutdown',
       5 => 'notPresent',
       6 => 'notFunctioning',
     },
  },
  'CISCO-HSRP-MIB' => {
      HsrpState => {
        1 => 'initial',
        2 => 'learn',
        3 => 'listen',
        4 => 'speak',
        5 => 'standby',
        6 => 'active',
      },
  },
  'SNMPv2-TC-v1' => {
      'TruthValue' => {
        1 => 'true',
        2 => 'false',
      },
      'RowStatus' => {
        1 => 'active',
        2 => 'notInService',
        3 => 'notReady',
        4 => 'createAndGo',
        5 => 'createAndWait',
        6 => 'destroy',
      },
  },
  'CISCO-ENTITY-SENSOR-MIB' => {
      'SensorDataType' => {
        1 => 'other',
        2 => 'unknown',
        3 => 'voltsAC',
        4 => 'voltsDC',
        5 => 'amperes',
        6 => 'watts',
        7 => 'hertz',
        8 => 'celsius',
        9 => 'percentRH',
        10 => 'rpm',
        11 => 'cmm',
        12 => 'truthvalue',
        13 => 'specialEnum',
        14 => 'dBm',
      },
      'SensorStatus' => {
        1 => 'ok',
        2 => 'unavailable',
        3 => 'nonoperational',
      },
      'SensorDataScale' => {
        1 => 'yocto',
        2 => 'zepto',
        3 => 'atto',
        4 => 'femto',
        5 => 'pico',
        6 => 'nano',
        7 => 'micro',
        8 => 'milli',
        9 => 'units',
        10 => 'kilo',
        11 => 'mega',
        12 => 'giga',
        13 => 'tera',
        14 => 'exa',
        15 => 'peta',
        16 => 'zetta',
        17 => 'yotta',
      },
      'SensorThresholdSeverity' => {
        1 => 'other',
        10 => 'minor',
        20 => 'major',
        30 => 'critical',
      },
      'SensorThresholdRelation' => {
        1 => 'lessThan',
        2 => 'lessOrEqual',
        3 => 'greaterThan',
        4 => 'greaterOrEqual',
        5 => 'equalTo',
        6 => 'notEqualTo',
      },
  },
  'CISCO-L2L3-INTERFACE-CONFIG-MIB' => {
      'CL2L3InterfaceMode' => {
        1 => 'routed',
        2 => 'switchport',
      },
  },
  'F5-BIGIP-LOCAL-MIB' => {
    ltmPoolLbMode => {
      0 => 'roundRobin',
      1 => 'ratioMember',
      2 => 'leastConnMember',
      3 => 'observedMember',
      4 => 'predictiveMember',
      5 => 'ratioNodeAddress',
      6 => 'leastConnNodeAddress',
      7 => 'fastestNodeAddress',
      8 => 'observedNodeAddress',
      9 => 'predictiveNodeAddress',
      10 => 'dynamicRatio',
      11 => 'fastestAppResponse',
      12 => 'leastSessions',
      13 => 'dynamicRatioMember',
      14 => 'l3Addr',
      15 => 'weightedLeastConnMember',
      16 => 'weightedLeastConnNodeAddr',
      17 => 'ratioSession',
    },
    ltmPoolAvailabilityState => {
      0 => 'none',
      1 => 'green',
      2 => 'yellow',
      3 => 'red',
      4 => 'blue',
    },
    ltmPoolMemberMonitorState => {
      0 => 'unchecked',
      1 => 'checking',
      2 => 'inband',
      3 => 'forced-up',
      4 => 'up',
      19 => 'down',
      20 => 'forced-down',
      22 => 'irule-down',
      23 => 'inband-down',
      24 => 'down-manual-resume',
      25 => 'disabled',
    },
    ltmPoolMemberMonitorStatus => {
      0 => 'unchecked',
      1 => 'checking',
      2 => 'inband',
      3 => 'forced-up',
      4 => 'up',
      18 => 'addr-down',
      19 => 'down',
      20 => 'forced-down',
      21 => 'maint',
      22 => 'irule-down',
      23 => 'inband-down',
      24 => 'down-manual-resume',
    },
    ltmPoolMemberEnabledState => {
      0 => 'none',
      1 => 'enabled',
      2 => 'disabled',
      3 => 'disabledbyparent',
    },
    ltmPoolStatusAvailState => {
      0 => 'none',
      1 => 'green',
      2 => 'yellow',
      3 => 'red',
      4 => 'blue',
      5 => 'grey',
    },
    ltmPoolStatusEnabledState =>  {
      0 => 'none',
      1 => 'enabled',
      2 => 'disabled',
      3 => 'disabledbyparent',
    },
    ltmPoolMbrStatusAvailState => {
      0 => 'none',
      1 => 'green',
      2 => 'yellow',
      3 => 'red',
      4 => 'blue',
      5 => 'gray',
    },
    ltmPoolMbrStatusEnabledState => {
      0 => 'none',
      1 => 'enabled',
      2 => 'disabled',
      3 => 'disabledbyparent',
    },
    ltmPoolMemberMonitorState => {
      0 => 'unchecked',
      1 => 'checking',
      2 => 'inband',
      3 => 'forced-up',
      4 => 'up',
      19 => 'down',
      20 => 'forced-down',
      22 => 'irule-down',
      23 => 'inband-down',
      24 => 'down-manual-resume',
      25 => 'disabled',
    },
    ltmPoolMemberMonitorStatus => {
      0 => 'unchecked',
      1 => 'checking',
      2 => 'inband',
      3 => 'forced-up',
      4 => 'up',
      18 => 'addr-down',
      19 => 'down',
      20 => 'forced-down',
      21 => 'maint',
      22 => 'irule-down',
      23 => 'inband-down',
      24 => 'down-manual-resume',
    },
  },
};

package NWC::Device;

use strict;
use IO::File;
use File::Basename;
use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 };

{
  our $mode = undef;
  our $plugin = undef;
  our $blacklist = undef;
  our $session = undef;
  our $rawdata = {};
  our $info = [];
  our $extendedinfo = [];
  our $summary = [];
  our $statefilesdir = '/var/tmp/check_nwc_health';
  our $oidtrace = [];
}

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    productname => 'unknown',
  };
  bless $self, $class;
  if (! ($self->opts->hostname || $self->opts->snmpwalk)) {
    die "wie jetzt??!?!";
  } else {
    if ($self->opts->servertype && $self->opts->servertype eq 'linuxlocal') {
    } else {
      $self->check_snmp_and_model();
    }
    if ($self->opts->servertype) {
      $self->{productname} = 'cisco' if $self->opts->servertype eq 'cisco';
      $self->{productname} = 'huawei' if $self->opts->servertype eq 'huawei';
      $self->{productname} = 'hp' if $self->opts->servertype eq 'hp';
      $self->{productname} = 'brocade' if $self->opts->servertype eq 'brocade';
      $self->{productname} = 'netscreen' if $self->opts->servertype eq 'netscreen';
      $self->{productname} = 'linuxlocal' if $self->opts->servertype eq 'linuxlocal';
      $self->{productname} = 'procurve' if $self->opts->servertype eq 'procurve';
    }
    if (! $NWC::Device::plugin->check_messages()) {
      if ($self->opts->verbose && $self->opts->verbose) {
        printf "I am a %s\n", $self->{productname};
      }
      # Brocade 4100 SilkWorm also sold as IBM 2005-B32 & EMC DS-4100
      # Brocade 4900 Switch also sold as IBM 2005-B64(3) & EMC DS4900B
      # Brocade M4700 (McData name Sphereon 4700) also sold as IBM 2026-432 & EMC DS-4700M
      if ($self->{productname} =~ /Cisco/i) {
        bless $self, 'NWC::Cisco';
        $self->debug('using NWC::Cisco');
      } elsif ($self->{productname} =~ /NetScreen/i) {
        bless $self, 'NWC::NetScreen';
        $self->debug('using NWC::NetScreen');
      } elsif ($self->{productname} =~ /Nortel/i) {
        bless $self, 'NWC::Nortel';
        $self->debug('using NWC::Nortel');
      } elsif ($self->{productname} =~ /Allied Telesyn Ethernet Switch/i) {
        bless $self, 'NWC::AlliedTelesyn';
        $self->debug('using NWC::AlliedTelesyn');
      } elsif ($self->{productname} =~ /DS_4100/i) {
        bless $self, 'NWC::Brocade';
        $self->debug('using NWC::Brocade');
      } elsif ($self->{productname} =~ /Connectrix DS_4900B/i) {
        bless $self, 'NWC::Brocade';
        $self->debug('using NWC::Brocade');
      } elsif ($self->{productname} =~ /EMC\s*DS.*4700M/i) {
        bless $self, 'NWC::Brocade';
        $self->debug('using NWC::Brocade');
      } elsif ($self->{productname} =~ /EMC\s*DS-24M2/i) {
        bless $self, 'NWC::Brocade';
        $self->debug('using NWC::Brocade');
      } elsif ($self->{productname} =~ /Fibre Channel Switch/i) {
        bless $self, 'NWC::Brocade';
        $self->debug('using NWC::Brocade');
      } elsif ($self->{productname} =~ /^(GS|FS)/i) {
        bless $self, 'NWC::Netscreen';
        $self->debug('using NWC::Netscreen');
      } elsif ($self->{productname} =~ /SecureOS/i) {
        bless $self, 'NWC::SecureOS';
        $self->debug('using NWC::SecureOS');
      } elsif ($self->{productname} =~ /Linux.*((el6.f5.x86_64)|(el5.1.0.f5app)) .*/i) {
        bless $self, 'NWC::F5';
        $self->debug('using NWC::F5');
      } elsif ($self->{productname} =~ /Procurve/i) {
        bless $self, 'NWC::HP';
        $self->debug('using NWC::HP');
      } elsif ($self->{productname} =~ /Check\s*Point/i) {
        bless $self, 'NWC::CheckPoint';
        $self->debug('using NWC::CheckPoint');
      } elsif ($self->{productname} =~ /linuxlocal/i) {
        bless $self, 'Server::Linux';
        $self->debug('using Server::Linux');
      } else {
        $self->add_message(CRITICAL,
            sprintf('unknown device%s', $self->{productname} eq 'unknown' ?
                '' : '('.$self->{productname}.')'));
      }
      if ($self->mode =~ /device::walk/) {
        if ($self->can("trees")) {
          my @trees = $self->trees;
          my $name = $0;
          $name =~ s/.*\///g;
          $name = sprintf "/tmp/snmpwalk_%s_%s", $name, $self->opts->hostname;
          printf "rm -f %s\n", $name;
          foreach ($self->trees) {
            printf "snmpwalk -On -v%s -c %s %s %s >> %s\n", 
                $self->opts->protocol,
                $self->opts->community,
                $self->opts->hostname,
                $_, $name;
          }
        }
        exit 0;
      } elsif ($self->mode =~ /device::uptime/) {
        $self->{uptime} = $self->get_snmp_object('MIB-II', 'sysUpTime', 0);
        if ($self->{uptime} =~ /\((\d+)\)/) {
          # Timeticks: (20718727) 2 days, 9:33:07.27
          $self->{uptime} = $1 / 100;
        } elsif ($self->{uptime} =~ /(\d+)\s*days.*?(\d+):(\d+):(\d+)\.(\d+)/) {
          # Timeticks: 2 days, 9:33:07.27
          $self->{uptime} = $1 * 24 * 3600 + $2 * 3600 + $3 * 60 + $4;
        } elsif ($self->{uptime} =~ /(\d+):(\d+):(\d+)\.(\d+)/) {
          # Timeticks: 9:33:07.27
          $self->{uptime} = $1 * 3600 + $2 * 60 + $3;
        } elsif ($self->{uptime} =~ /(\d+)\s*hours.*?(\d+):(\d+)\.(\d+)/) {
          # Timeticks: 3 hours, 42:17.98
          $self->{uptime} = $1 * 3600 + $2 * 60 + $3;
        } elsif ($self->{uptime} =~ /(\d+)\s*minutes.*?(\d+)\.(\d+)/) {
          # Timeticks: 36 minutes, 01.96
          $self->{uptime} = $1 * 60 + $2;
        }
        $self->{uptime} /= 60;
        my $info = sprintf 'device is up since %d minutes', $self->{uptime};
        $self->add_info($info);
        $self->set_thresholds(warning => '15:', critical => '5:');
        $self->add_message($self->check_thresholds($self->{uptime}), $info);
        $self->add_perfdata(
            label => 'uptime',
            value => $self->{uptime},
            warning => $self->{warning},
            critical => $self->{critical},
        );
        my ($code, $message) = $self->check_messages(join => ', ', join_all => ', ');
        $NWC::Device::plugin->nagios_exit($code, $message);
      }
      $self->{method} = 'snmp';
    }
  }
  if ($self->opts->blacklist &&
      -f $self->opts->blacklist) {
    $self->opts->blacklist = do {
        local (@ARGV, $/) = $self->opts->blacklist; <> };
  }
  $NWC::Device::statefilesdir = $self->opts->statefilesdir;
  return $self;
}

sub check_snmp_and_model {
# uptime pruefen
# dann whoami
  my $self = shift;
  if ($self->opts->snmpwalk) {
    my $response = {};
    if (! -f $self->opts->snmpwalk) {
      $self->add_message(CRITICAL, 
          sprintf 'file %s not found',
          $self->opts->snmpwalk);
    } elsif (-x $self->opts->snmpwalk) {
      my $cmd = sprintf "%s -On -v%s -c%s %s 1.3.6.1.4.1.232 2>&1",
          $self->opts->snmpwalk,
          $self->opts->protocol,
          $self->opts->community,
          $self->opts->hostname;
      open(WALK, "$cmd |");
      while (<WALK>) {
        if (/^.*?\.(232\.[\d\.]+) = .*?: (\-*\d+)/) {
          $response->{'1.3.6.1.4.1.'.$1} = $2;
        } elsif (/^.*?\.(232\.[\d\.]+) = .*?: "(.*?)"/) {
          $response->{'1.3.6.1.4.1.'.$1} = $2;
          $response->{'1.3.6.1.4.1.'.$1} =~ s/\s+$//;
        }
      }
      close WALK;
    } else {
      $self->opts->override_opt('hostname', 'walkhost');
      open(MESS, $self->opts->snmpwalk);
      while(<MESS>) {
        # SNMPv2-SMI::enterprises.232.6.2.6.7.1.3.1.4 = INTEGER: 6
        if (/^([\d\.]+) = .*?INTEGER: .*\((\-*\d+)\)/) {
          # .1.3.6.1.2.1.2.2.1.8.1 = INTEGER: down(2)
          $response->{$1} = $2;
        } elsif (/^([\d\.]+) = .*?Opaque:.*?Float:.*?([\-\.\d]+)/) {
          # .1.3.6.1.4.1.2021.10.1.6.1 = Opaque: Float: 0.938965
          $response->{$1} = $2;
        } elsif (/^([\d\.]+) = STRING:\s*$/) {
          $response->{$1} = "";
        } elsif (/^([\d\.]+) = Network Address: (.*)/) {
          $response->{$1} = $2;
        } elsif (/^([\d\.]+) = Hex-STRING: (.*)/) {
          $response->{$1} = "0x".$2;
          $response->{$1} =~ s/\s+$//;
        } elsif (/^([\d\.]+) = \w+: (\-*\d+)/) {
          $response->{$1} = $2;
        } elsif (/^([\d\.]+) = \w+: "(.*?)"/) {
          $response->{$1} = $2;
          $response->{$1} =~ s/\s+$//;
        } elsif (/^([\d\.]+) = \w+: (.*)/) {
          $response->{$1} = $2;
          $response->{$1} =~ s/\s+$//;
        } elsif (/^([\d\.]+) = (\-*\d+)/) {
          $response->{$1} = $2;
        } elsif (/^([\d\.]+) = "(.*?)"/) {
          $response->{$1} = $2;
          $response->{$1} =~ s/\s+$//;
        }
      }
      close MESS;
    }
    foreach my $oid (keys %$response) {
      if ($oid =~ /^\./) {
        my $nodot = $oid;
        $nodot =~ s/^\.//g;
        $response->{$nodot} = $response->{$oid};
        delete $response->{$oid};
      }
    }
    map { $response->{$_} =~ s/^\s+//; $response->{$_} =~ s/\s+$//; }
        keys %$response;
    #printf "%s\n", Data::Dumper::Dumper($response);
    $self->set_rawdata($response);
    #if (! $self->get_snmp_object('MIB-II', 'sysDescr', 0)) {
    #  $self->add_rawdata('1.3.6.1.2.1.1.1.0', 'Cisco');
    #}
    $self->whoami();
  } else {
    if (eval "require Net::SNMP") {
      my %params = ();
      my $net_snmp_version = Net::SNMP->VERSION(); # 5.002000 or 6.000000
      #$params{'-translate'} = [
      #  -all => 0x0
      #];
      $params{'-timeout'} = $self->opts->timeout;
      $params{'-hostname'} = $self->opts->hostname;
      $params{'-version'} = $self->opts->protocol;
      if ($self->opts->port) {
        $params{'-port'} = $self->opts->port;
      }
      if ($self->opts->protocol eq '3') {
        $params{'-username'} = $self->opts->username;
        if ($self->opts->authpassword) {
          $params{'-authpassword'} = $self->opts->authpassword;
        }
        if ($self->opts->authprotocol) {
          $params{'-authprotocol'} = $self->opts->authprotocol;
        }
        if ($self->opts->privpassword) {
          $params{'-privpassword'} = $self->opts->privpassword;
        }
        if ($self->opts->privprotocol) {
          $params{'-privprotocol'} = $self->opts->privprotocol;
        }
      } else {
        $params{'-community'} = $self->opts->community;
      }
      my ($session, $error) = Net::SNMP->session(%params);
      if (! defined $session) {
        $self->add_message(CRITICAL, 
            sprintf 'cannot create session object: %s', $error);
        $self->debug(Data::Dumper::Dumper(\%params));
      } else {
        my $max_msg_size = $session->max_msg_size();
        $session->max_msg_size(4 * $max_msg_size);
        $NWC::Device::session = $session;
        my $sysUpTime = '1.3.6.1.2.1.1.3.0';
        if (my $uptime = $self->get_snmp_object('MIB-II', 'sysUpTime', 0)) {
          $self->debug(sprintf 'snmp agent answered: %s', $uptime);
          $self->whoami();
        } else {
          $self->add_message(CRITICAL,
              'could not contact snmp agent');
          #$session->close;
        }
      }
    } else {
      $self->add_message(CRITICAL,
          'could not find Net::SNMP module');
    }
  }
}

sub whoami {
  my $self = shift;
  my $productname = undef;
  my $sysDescr = '1.3.6.1.2.1.1.1.0';
  my $dummy = '1.3.6.1.2.1.1.5.0';
  if ($productname = $self->get_snmp_object('MIB-II', 'sysDescr', 0)) {
    $self->{productname} = $productname;
  } else {
    $self->add_message(CRITICAL,
        'snmpwalk returns no product name (sysDescr)');
    if (! $self->opts->snmpwalk) {
      $NWC::Device::session->close;
    }
  }
  $self->debug('whoami: '.$self->{productname});
}

sub get_snmp_object {
  my $self = shift;
  my $mib = shift;
  my $mo = shift;
  my $index = shift;
  if (exists $NWC::Device::mibs_and_oids->{$mib} &&
      exists $NWC::Device::mibs_and_oids->{$mib}->{$mo}) {
    my $oid = $NWC::Device::mibs_and_oids->{$mib}->{$mo}.
        (defined $index ? '.'.$index : '');
    my $response = $self->get_request(-varbindlist => [$oid]);
    if (defined $response->{$oid}) {
      if (my @symbols = $self->make_symbolic($mib, $response, [[$index]])) {
        $response->{$oid} = $symbols[0]->{$mo};
      }
    }
    return $response->{$oid};
  }
  return undef;
}

sub get_single_request_iq {
  my $self = shift;
  my %params = @_;
  my @oids = ();
  my $result = $self->get_request_iq(%params);
  foreach (keys %{$result}) {
    return $result->{$_};
  }
  return undef;
}

sub get_request_iq {
  my $self = shift;
  my %params = @_;
  my @oids = ();
  my $mib = $params{'-mib'};
  foreach my $oid (@{$params{'-molist'}}) {
    if (exists $NWC::Device::mibs_and_oids->{$mib} &&
        exists $NWC::Device::mibs_and_oids->{$mib}->{$oid}) {
      push(@oids, (exists $params{'-index'}) ?
          $NWC::Device::mibs_and_oids->{$mib}->{$oid}.'.'.$params{'-index'} :
          $NWC::Device::mibs_and_oids->{$mib}->{$oid});
    }
  }
  return $self->get_request(
      -varbindlist => \@oids);
}

sub valid_response {
  my $self = shift;
  my $mib = shift;
  my $oid = shift;
  my $index = shift;
  if (exists $NWC::Device::mibs_and_oids->{$mib} &&
      exists $NWC::Device::mibs_and_oids->{$mib}->{$oid}) {
    # make it numerical
    my $oid = $NWC::Device::mibs_and_oids->{$mib}->{$oid};
    if (defined $index) {
      $oid .= '.'.$index;
    }
    my $result = $self->get_request(
        -varbindlist => [$oid]
    );
    if (!defined($result) ||
        ! defined $result->{$oid} ||
        $result->{$oid} eq 'noSuchInstance' ||
        $result->{$oid} eq 'noSuchObject' ||
        $result->{$oid} eq 'endOfMibView') {
      return undef;
    } else {
      $self->add_rawdata($oid, $result->{$oid});
      return $result->{$oid};
    }
  } else {
    return undef;
  }
}

sub debug {
  my $self = shift;
  my $format = shift;
  $self->{trace} = -f "/tmp/check_nwc_health.trace" ? 1 : 0;
  if ($self->opts->verbose && $self->opts->verbose > 10) {
    printf("%s: ", scalar localtime);
    printf($format, @_);
    printf "\n";
  }
  if ($self->{trace}) {
    my $logfh = new IO::File;
    $logfh->autoflush(1);
    if ($logfh->open("/tmp/check_nwc_health.trace", "a")) {
      $logfh->printf("%s: ", scalar localtime);
      $logfh->printf($format, @_);
      $logfh->printf("\n");
      $logfh->close();
    }
  }
}

sub filter_name {
  my $self = shift;
  my $name = shift;
  if ($self->opts->name) {
    if ($self->opts->regexp) {
      my $pattern = $self->opts->name;
      if ($name =~ /$pattern/i) {
        return 1;
      }
    } else {
      if (lc $self->opts->name eq lc $name) {
        return 1;
      }
    }
  } else {
    return 1;
  }
  return 0;
}

sub blacklist {
  my $self = shift;
  my $type = shift;
  my $name = shift;
  $self->{blacklisted} = $self->is_blacklisted($type, $name);
}

sub add_blacklist {
  my $self = shift;
  my $list = shift;
  $NWC::Device::blacklist = join('/',
      (split('/', $self->opts->blacklist), $list));
}

sub is_blacklisted {
  my $self = shift;
  my $type = shift;
  my $name = shift;
  my $blacklisted = 0;
#  $name =~ s/\:/-/g;
  foreach my $bl_items (split(/\//, $self->opts->blacklist)) {
    if ($bl_items =~ /^(\w+):([\:\d\-,]+)$/) {
      my $bl_type = $1;
      my $bl_names = $2;
      foreach my $bl_name (split(/,/, $bl_names)) {
        if ($bl_type eq $type && $bl_name eq $name) {
          $blacklisted = 1;
        }
      }
    } elsif ($bl_items =~ /^(\w+)$/) {
      my $bl_type = $1;
      if ($bl_type eq $type) {
        $blacklisted = 1;
      }
    }
  }
  return $blacklisted;
}

sub mode {
  my $self = shift;
  return $NWC::Device::mode;
}

sub add_message {
  my $self = shift;
  my $level = shift;
  my $message = shift;
  $NWC::Device::plugin->add_message($level, $message) 
      unless $self->{blacklisted};
  if (exists $self->{failed}) {
    if ($level == UNKNOWN && $self->{failed} == OK) {
      $self->{failed} = $level;
    } elsif ($level > $self->{failed}) {
      $self->{failed} = $level;
    }
  }
}

sub check_messages {
  my $self = shift;
  return $NWC::Device::plugin->check_messages(@_);
}

sub clear_messages {
  my $self = shift;
  return $NWC::Device::plugin->clear_messages(@_);
}

sub add_perfdata {
  my $self = shift;
  $NWC::Device::plugin->add_perfdata(@_);
}

sub set_thresholds {
  my $self = shift;
  $NWC::Device::plugin->set_thresholds(@_);
}

sub check_thresholds {
  my $self = shift;
  my @params = @_;
  ($self->{warning}, $self->{critical}) =
      $NWC::Device::plugin->get_thresholds(@params);
  return $NWC::Device::plugin->check_thresholds(@params);
}

sub get_thresholds {
  my $self = shift;
  my @params = @_;
  my @thresholds = $NWC::Device::plugin->get_thresholds(@params);
  my($warning, $critical) = $NWC::Device::plugin->get_thresholds(@params);
  $self->{warning} = $thresholds[0];
  $self->{critical} = $thresholds[1];
  return @thresholds;
}

sub has_failed {
  my $self = shift;
  return $self->{failed};
}

sub add_info {
  my $self = shift;
  my $info = shift;
  $info = $self->{blacklisted} ? $info.' (blacklisted)' : $info;
  $self->{info} = $info;
  push(@{$NWC::Device::info}, $info);
}

sub annotate_info {
  my $self = shift;
  my $annotation = shift;
  my $lastinfo = pop(@{$NWC::Device::info});
  $lastinfo .= sprintf ' (%s)', $annotation;
  push(@{$NWC::Device::info}, $lastinfo);
}

sub add_extendedinfo {
  my $self = shift;
  my $info = shift;
  $self->{extendedinfo} = $info;
  return if ! $self->opts->extendedinfo;
  push(@{$NWC::Device::extendedinfo}, $info);
}

sub get_extendedinfo {
  my $self = shift;
  return join(' ', @{$NWC::Device::extendedinfo});
}

sub add_summary {
  my $self = shift;
  my $summary = shift;
  push(@{$NWC::Device::summary}, $summary);
}

sub get_summary {
  my $self = shift;
  return join(', ', @{$NWC::Device::summary});
}

sub opts {
  my $self = shift;
  return $NWC::Device::plugin->opts();
}

sub set_rawdata {
  my $self = shift;
  $NWC::Device::rawdata = shift;
}

sub add_rawdata {
  my $self = shift;
  my $oid = shift;
  my $value = shift;
  $NWC::Device::rawdata->{$oid} = $value;
}

sub rawdata {
  my $self = shift;
  return $NWC::Device::rawdata;
}

sub add_oidtrace {
  my $self = shift;
  my $oid = shift;
  $self->debug("cache: ".$oid);
  push(@{$NWC::Device::oidtrace}, $oid);
}

sub get_snmp_table_attributes {
  my $self = shift;
  my $mib = shift;
  my $table = shift;
  my $indices = shift || [];
  my @entries = ();
  my $augmenting_table;
  if ($table =~ /^(.*?)\+(.*)/) {
    $table = $1;
    $augmenting_table = $2;
  }
  my $entry = $table;
  $entry =~ s/Table/Entry/g;
  if (exists $NWC::Device::mibs_and_oids->{$mib} &&
      exists $NWC::Device::mibs_and_oids->{$mib}->{$table}) {
    my $toid = $NWC::Device::mibs_and_oids->{$mib}->{$table}.'.';
    my $toidlen = length($toid);
    my @columns = grep {
      substr($NWC::Device::mibs_and_oids->{$mib}->{$_}, 0, $toidlen) eq
          $NWC::Device::mibs_and_oids->{$mib}->{$table}.'.'
    } keys %{$NWC::Device::mibs_and_oids->{$mib}};
    if ($augmenting_table &&
        exists $NWC::Device::mibs_and_oids->{$mib}->{$augmenting_table}) {
      my $toid = $NWC::Device::mibs_and_oids->{$mib}->{$augmenting_table}.'.';
      my $toidlen = length($toid);
      push(@columns, grep {
        substr($NWC::Device::mibs_and_oids->{$mib}->{$_}, 0, $toidlen) eq
            $NWC::Device::mibs_and_oids->{$mib}->{$augmenting_table}.'.'
      } keys %{$NWC::Device::mibs_and_oids->{$mib}});
    }
    return @columns;
  } else {
    return ();
  }
}

sub get_request {
  my $self = shift;
  my %params = @_;
  my @notcached = ();
  foreach my $oid (@{$params{'-varbindlist'}}) {
    $self->add_oidtrace($oid);
    if (! exists NWC::Device::rawdata->{$oid}) {
      push(@notcached, $oid);
    }
  }
  if (! $self->opts->snmpwalk && (scalar(@notcached) > 0)) {
    my $result = ($NWC::Device::session->version() == 0) ?
        $NWC::Device::session->get_request(
            -varbindlist => \@notcached,
        )
        :
        $NWC::Device::session->get_request(  # get_bulk_request liefert next
            #-nonrepeaters => scalar(@notcached),
            -varbindlist => \@notcached,
        );
    foreach my $key (%{$result}) {
      $self->add_rawdata($key, $result->{$key});
    }
  }
  my $result = {};
  map { $result->{$_} = $NWC::Device::rawdata->{$_} }
      @{$params{'-varbindlist'}};
  return $result;
}

# Level1
# get_snmp_table_objects('MIB-Name', 'Table-Name', 'Table-Entry', [indices])
#
# returns array of hashrefs
# evt noch ein weiterer parameter fuer ausgewaehlte oids
#
sub get_snmp_table_objects {
  my $self = shift;
  my $mib = shift;
  my $table = shift;
  my $indices = shift || [];
  my @entries = ();
  my $augmenting_table;
  $self->debug(sprintf "get_snmp_table_objects %s %s", $mib, $table);
  if ($table =~ /^(.*?)\+(.*)/) {
    $table = $1;
    $augmenting_table = $2;
  }
  my $entry = $table;
  $entry =~ s/Table/Entry/g;
  if (scalar(@{$indices}) == 1) {
    if (exists $NWC::Device::mibs_and_oids->{$mib} &&
        exists $NWC::Device::mibs_and_oids->{$mib}->{$table}) {
      my $toid = $NWC::Device::mibs_and_oids->{$mib}->{$table}.'.';
      my $toidlen = length($toid);
      my @columns = map {
          $NWC::Device::mibs_and_oids->{$mib}->{$_}
      } grep {
        substr($NWC::Device::mibs_and_oids->{$mib}->{$_}, 0, $toidlen) eq
            $NWC::Device::mibs_and_oids->{$mib}->{$table}.'.'
      } keys %{$NWC::Device::mibs_and_oids->{$mib}};
      if ($augmenting_table && 
          exists $NWC::Device::mibs_and_oids->{$mib}->{$augmenting_table}) {
        my $toid = $NWC::Device::mibs_and_oids->{$mib}->{$augmenting_table}.'.';
        my $toidlen = length($toid);
        push(@columns, map {
            $NWC::Device::mibs_and_oids->{$mib}->{$_}
        } grep {
          substr($NWC::Device::mibs_and_oids->{$mib}->{$_}, 0, $toidlen) eq
              $NWC::Device::mibs_and_oids->{$mib}->{$augmenting_table}.'.'
        } keys %{$NWC::Device::mibs_and_oids->{$mib}});
      }
      my $result = $self->get_entries(
          -startindex => $indices->[0]->[0],
          -endindex => $indices->[0]->[0],
          -columns => \@columns,
      );
      @entries = $self->make_symbolic($mib, $result, $indices);
    }
  } elsif (scalar(@{$indices}) > 1) {
    # man koennte hier pruefen, ob die indices aufeinanderfolgen
    # und dann get_entries statt get_table aufrufen
    if (exists $NWC::Device::mibs_and_oids->{$mib} &&
        exists $NWC::Device::mibs_and_oids->{$mib}->{$table}) {
      my $result = {};
      $result = $self->get_table(
          -baseoid => $NWC::Device::mibs_and_oids->{$mib}->{$table});
      if ($augmenting_table && 
          exists $NWC::Device::mibs_and_oids->{$mib}->{$augmenting_table}) {
        my $augmented_result = $self->get_table(
            -baseoid => $NWC::Device::mibs_and_oids->{$mib}->{$augmenting_table});
        map { $result->{$_} = $augmented_result->{$_} }
            keys %{$augmented_result};
      }
      # now we have numerical_oid+index => value
      # needs to become symboic_oid => value
      #my @indices = 
      #    $self->get_indices($NWC::Device::mibs_and_oids->{$mib}->{$entry});
      @entries = $self->make_symbolic($mib, $result, $indices);
    }
  } else {
    if (exists $NWC::Device::mibs_and_oids->{$mib} &&
        exists $NWC::Device::mibs_and_oids->{$mib}->{$table}) {
      $self->debug(sprintf "get_snmp_table_objects calls get_table %s",
          $NWC::Device::mibs_and_oids->{$mib}->{$table});
      my $result = $self->get_table(
          -baseoid => $NWC::Device::mibs_and_oids->{$mib}->{$table});
      $self->debug(sprintf "get_snmp_table_objects get_table returns %d oids",
          scalar(keys %{$result}));
      # now we have numerical_oid+index => value
      # needs to become symboic_oid => value
      my @indices = 
          $self->get_indices(
              -baseoid => $NWC::Device::mibs_and_oids->{$mib}->{$entry},
              -oids => [keys %{$result}]);
      $self->debug(sprintf "get_snmp_table_objects get_table returns %d indices",
          scalar(@indices));
      @entries = $self->make_symbolic($mib, $result, \@indices);
      @entries = map { $_->{indices} = shift @indices; $_ } @entries;
    }
  }
  return @entries;
}

# make_symbolic
# mib is the name of a mib (must be in mibs_and_oids)
# result is a hash-key oid->value
# indices is a array ref of array refs. [[1],[2],...] or [[1,0],[1,1],[2,0]..
sub make_symbolic {
  my $self = shift;
  my $mib = shift;
  my $result = shift;
  my $indices = shift;
  my @entries = ();
  foreach my $index (@{$indices}) {
    # skip [], [[]], [[undef]]
    if (ref($index) eq "ARRAY") {
      if (scalar(@{$index}) == 0) {
        next;
      } elsif (!defined $index->[0]) {
        next;
      }
    }
    my $mo = {};
    my $idx = join('.', @{$index}); # index can be multi-level
    foreach my $symoid
        (keys %{$NWC::Device::mibs_and_oids->{$mib}}) {
      my $oid = $NWC::Device::mibs_and_oids->{$mib}->{$symoid};
      if (ref($oid) ne 'HASH') {
        my $fulloid = $oid . '.'.$idx;
        if (exists $result->{$fulloid}) {
          if (exists $NWC::Device::mibs_and_oids->{$mib}->{$symoid.'Definition'}) {
            if (ref($NWC::Device::mibs_and_oids->{$mib}->{$symoid.'Definition'}) eq 'HASH') {
              if (exists $NWC::Device::mibs_and_oids->{$mib}->{$symoid.'Definition'}->{$result->{$fulloid}}) {
                $mo->{$symoid} = $NWC::Device::mibs_and_oids->{$mib}->{$symoid.'Definition'}->{$result->{$fulloid}};
              } else {
                $mo->{$symoid} = 'unknown_'.$result->{$fulloid};
              }
            } elsif ($NWC::Device::mibs_and_oids->{$mib}->{$symoid.'Definition'} =~ /^(.*?)::(.*)/) {
              my $mib = $1;
              my $definition = $2;
              if  (exists $NWC::Device::definitions->{$mib} && exists $NWC::Device::definitions->{$mib}->{$definition}
                  && exists $NWC::Device::definitions->{$mib}->{$definition}->{$result->{$fulloid}}) {
                $mo->{$symoid} = $NWC::Device::definitions->{$mib}->{$definition}->{$result->{$fulloid}};
              } else {
                $mo->{$symoid} = 'unknown_'.$result->{$fulloid};
              }
            } else {
              $mo->{$symoid} = 'unknown_'.$result->{$fulloid};
              # oder $NWC::Device::mibs_and_oids->{$mib}->{$symoid.'Definition'}?
            }
          } else {
            $mo->{$symoid} = $result->{$fulloid};
          }
        }
      }
    }
    push(@entries, $mo);
  }
  return @entries;
}

# Level2
# - get_table from Net::SNMP
# - get all baseoid-matching oids from rawdata
sub get_table {
  my $self = shift;
  my %params = @_;
  $self->add_oidtrace($params{'-baseoid'});
  if (! $self->opts->snmpwalk) {
    my @notcached = ();
    $self->debug(sprintf "get_table %s", Data::Dumper::Dumper(\%params));
    my $result = $NWC::Device::session->get_table(%params);
    $self->debug(sprintf "get_table returned %d oids", scalar(keys %{$result}));
    if (scalar(keys %{$result}) == 0) {
      $self->debug(sprintf "get_table error: %s", 
          $NWC::Device::session->error());
      $self->debug("get_table error: try fallback");
      $params{'-maxrepetitions'} = 1;
      $self->debug(sprintf "get_table %s", Data::Dumper::Dumper(\%params));
      $result = $NWC::Device::session->get_table(%params);
      $self->debug(sprintf "get_table returned %d oids", scalar(keys %{$result}));
      if (scalar(keys %{$result}) == 0) {
        $self->debug(sprintf "get_table error: %s", 
            $NWC::Device::session->error());
        $self->debug("get_table error: no more fallbacks. Try --protocol 1");
      }
    }
    foreach my $key (keys %{$result}) {
      $self->add_rawdata($key, $result->{$key});
    }
  }
  return $self->get_matching_oids(
      -columns => [$params{'-baseoid'}]);
}

sub get_entries {
  my $self = shift;
  my %params = @_;
  # [-startindex]
  # [-endindex]
  # -columns
  my $result = {};
  if (! $self->opts->snmpwalk) {
    my %newparams = ();
    $newparams{'-startindex'} = $params{'-startindex'}
        if defined $params{'-startindex'};
    $newparams{'-endindex'} = $params{'-endindex'}     
        if defined $params{'-startindex'};
    $newparams{'-columns'} = $params{'-columns'};
    $result = $NWC::Device::session->get_entries(%newparams);
    foreach my $key (keys %{$result}) {
      $self->add_rawdata($key, $result->{$key});
    }
  } else {
    my $preresult = $self->get_matching_oids(
        -columns => $params{'-columns'});
    foreach (keys %{$preresult}) {
      $result->{$_} = $preresult->{$_};
    }
    my @to_del = ();
    if ($params{'-startindex'}) {
      foreach my $resoid (keys %{$result}) {
        foreach my $oid (@{$params{'-columns'}}) {
          my $poid = $oid.'.';
          my $lpoid = length($poid);
          if (substr($resoid, 0, $lpoid) eq $poid) {
            my $oidpattern = $poid;
            $oidpattern =~ s/\./\\./g;
            if ($resoid =~ /^$oidpattern.(.+)$/) {
              if ($1 < $params{'-startindex'}) {
                push(@to_del, $oid);
              }
            }
          }
        }
      }
    }
    if ($params{'-endindex'}) {
      foreach my $resoid (keys %{$result}) {
        foreach my $oid (@{$params{'-columns'}}) {
          my $poid = $oid.'.';
          my $lpoid = length($poid);
          if (substr($resoid, 0, $lpoid) eq $poid) {
            my $oidpattern = $poid;
            $oidpattern =~ s/\./\\./g;
            if ($resoid =~ /^$oidpattern.(.+)$/) {
              if ($1 > $params{'-endindex'}) {
                push(@to_del, $oid);
              }
            }
          }
        }
      } 
    }   
    foreach (@to_del) {
      delete $result->{$_};
    }
  }
  return $result;
}

# Level2
# helper function
sub get_matching_oids {
  my $self = shift;
  my %params = @_;
  my $result = {};
  $self->debug(sprintf "get_matching_oids %s", Data::Dumper::Dumper(\%params));
  foreach my $oid (@{$params{'-columns'}}) {
    my $oidpattern = $oid;
    $oidpattern =~ s/\./\\./g;
    map { $result->{$_} = $NWC::Device::rawdata->{$_} }
        grep /^$oidpattern(?=\.|$)/, keys %{$NWC::Device::rawdata};
  }
  $self->debug(sprintf "get_matching_oids returns %d from %d oids", 
      scalar(keys %{$result}), scalar(keys %{$NWC::Device::rawdata}));
  return $result;
}

sub valdiff {
  my $self = shift;
  my $pparams = shift;
  my %params = %{$pparams};
  my @keys = @_;
  my $now = time;
  my $last_values = $self->load_state(%params) || eval {
    my $empty_events = {};
    foreach (@keys) {
      $empty_events->{$_} = 0;
    }
    $empty_events->{timestamp} = 0;
    if ($self->opts->lookback) {
      $empty_events->{lookback_history} = {};
    }
    $empty_events;
  };
  foreach (@keys) {
    if ($self->opts->lookback) {
      # find a last_value in the history which fits lookback best
      # and overwrite $last_values->{$_} with historic data
      if (exists $last_values->{lookback_history}->{$_}) {
        foreach my $date (sort {$a <=> $b} keys %{$last_values->{lookback_history}->{$_}}) {
          if ($date >= ($now - $self->opts->lookback)) {
            $last_values->{$_} = $last_values->{lookback_history}->{$_}->{$date};
            $last_values->{timestamp} = $date;
            last;
          } else {
            delete $last_values->{lookback_history}->{$_}->{$date};
          }
        }
      }
    }
    $last_values->{$_} = 0 if ! exists $last_values->{$_};
    if ($self->{$_} >= $last_values->{$_}) {
      $self->{'delta_'.$_} = $self->{$_} - $last_values->{$_};
    } else {
      # vermutlich db restart und zaehler alle auf null
      $self->{'delta_'.$_} = $self->{$_};
    }
    $self->debug(sprintf "delta_%s %f", $_, $self->{'delta_'.$_});
  }
  $self->{'delta_timestamp'} = $now - $last_values->{timestamp};
  $params{save} = eval {
    my $empty_events = {};
    foreach (@keys) {
      $empty_events->{$_} = $self->{$_};
    }
    $empty_events->{timestamp} = $now;
    if ($self->opts->lookback) {
      $empty_events->{lookback_history} = $last_values->{lookback_history};
      foreach (@keys) {
        $empty_events->{lookback_history}->{$_}->{$now} = $self->{$_};
      }
    }
    $empty_events;
  };
  $self->save_state(%params);
}

sub create_statefilesdir {
  my $self = shift;
  if (! -d $NWC::Device::statefilesdir) {
    if (! -d dirname($NWC::Device::statefilesdir)) {
      mkdir dirname($NWC::Device::statefilesdir);
    }
    mkdir $NWC::Device::statefilesdir;
  } elsif (! -w $NWC::Device::statefilesdir) {
    $self->schimpf();
  }
}

sub schimpf {
  my $self = shift;
  printf "statefilesdir %s is not writable.\nYou didn't run this plugin as root, didn't you?\n", $NWC::Device::statefilesdir;
}

sub save_state {
  my $self = shift;
  my %params = @_;
  my $extension = "";
  $self->create_statefilesdir();
  mkdir $NWC::Device::statefilesdir unless -d $NWC::Device::statefilesdir;
  my $statefile = sprintf "%s/%s_%s", 
      $NWC::Device::statefilesdir, $self->opts->hostname, $self->opts->mode;
  #$extension .= $params{differenciator} ? "_".$params{differenciator} : "";
  $extension .= $params{name} ? '_'.$params{name} : '';
  $extension =~ s/\//_/g;
  $extension =~ s/\(/_/g;
  $extension =~ s/\)/_/g;
  $extension =~ s/\*/_/g;
  $extension =~ s/\s/_/g;
  $statefile .= $extension;
  $statefile = lc $statefile;
  open(STATE, ">$statefile");
  if ((ref($params{save}) eq "HASH") && exists $params{save}->{timestamp}) {
    $params{save}->{localtime} = scalar localtime $params{save}->{timestamp};
  } 
  printf STATE Data::Dumper::Dumper($params{save});
  close STATE; 
  $self->debug(sprintf "saved %s to %s",
      Data::Dumper::Dumper($params{save}), $statefile);
}

sub load_state {
  my $self = shift;
  my %params = @_;
  my $extension = "";
  my $statefile = sprintf "%s/%s_%s", 
      $NWC::Device::statefilesdir, $self->opts->hostname, $self->opts->mode;
  #$extension .= $params{differenciator} ? "_".$params{differenciator} : "";
  $extension .= $params{name} ? '_'.$params{name} : '';
  $extension =~ s/\//_/g;
  $extension =~ s/\(/_/g;
  $extension =~ s/\)/_/g;
  $extension =~ s/\*/_/g;
  $extension =~ s/\s/_/g;
  $statefile .= $extension;
  $statefile = lc $statefile;
  if ( -f $statefile) {
    our $VAR1;
    eval {
      require $statefile;
    };
    if($@) {
      printf "rumms\n";
    }
    $self->debug(sprintf "load %s", Data::Dumper::Dumper($VAR1));
    return $VAR1;
  } else { 
    return undef;
  }
}


sub dumper {
  my $self = shift;
  my $object = shift;
  my $run = $object->{runtime};
  delete $object->{runtime};
  printf STDERR "%s\n", Data::Dumper::Dumper($object);
  $object->{runtime} = $run;
}

sub no_such_mode {
  my $self = shift;
  my %params = @_;
  printf "Mode %s is not implemented for this type of device\n",
      $self->opts->mode;
  exit 0;
}

# get_cached_table_entries
#   get_table nur die table-basoid
#   mit liste von indices
#     get_entries -startindex x -endindex x konsekutive indices oder einzeln

sub get_table_entries {
  my $self = shift;
  my $mib = shift;
  my $table = shift;
  my $elements = shift;
  my $oids = {};
  my $entry;
  if (exists $NWC::Device::mibs_and_oids->{$mib} &&
      exists $NWC::Device::mibs_and_oids->{$mib}->{$table}) {
    foreach my $key (keys %{$NWC::Device::mibs_and_oids->{$mib}}) {
      if ($NWC::Device::mibs_and_oids->{$mib}->{$key} =~
          /^$NWC::Device::mibs_and_oids->{$mib}->{$table}/) {
        $oids->{$key} = $NWC::Device::mibs_and_oids->{$mib}->{$key};
      }
    }
  }
  ($entry = $table) =~ s/Table/Entry/g;
  return $self->get_entries($oids, $entry);
}


sub xget_entries {
  my $self = shift;
  my $oids = shift;
  my $entry = shift;
  my $fallback = shift;
  my @params = ();
  my @indices = $self->get_indices($oids->{$entry});
  foreach (@indices) {
    my @idx = @{$_};
    my %params = ();
    my $maxdimension = scalar(@idx) - 1;
    foreach my $idxnr (1..scalar(@idx)) {
      $params{'index'.$idxnr} = $_->[$idxnr - 1];
    }
    foreach my $oid (keys %{$oids}) {
      next if $oid =~ /Table$/;
      next if $oid =~ /Entry$/;
      # there may be scalar oids ciscoEnvMonTemperatureStatusValue = curr. temp.
      next if ($oid =~ /Value$/ && ref ($oids->{$oid}) eq 'HASH');
      if (exists $oids->{$oid.'Value'}) {
        $params{$oid} = $self->get_object_value(
            $oids->{$oid}, $oids->{$oid.'Value'}, @idx);
      } else {
        $params{$oid} = $self->get_object($oids->{$oid}, @idx);
      }
    }     
    push(@params, \%params);
  }
  if (! $fallback && scalar(@params) == 0) {
    if ($NWC::Device::session) {
      my $table = $entry;
      $table =~ s/(.*)\.\d+$/$1/;
      my $result = $self->get_table(
          -baseoid => $oids->{$table}
      );
      if ($result) {
        foreach my $key (keys %{$result}) {
          $self->add_rawdata($key, $result->{$key});
        }
        @params = $self->get_entries($oids, $entry, 1);
      }
      #printf "%s\n", Data::Dumper::Dumper($result);
    }
  }
  return @params;
}

sub get_indices {
  my $self = shift;
  my %params = @_;
  # -baseoid : entry
  # find all oids beginning with $entry
  # then skip one field for the sequence
  # then read the next numindices fields
  my $entrypat = $params{'-baseoid'};
  $entrypat =~ s/\./\\\./g;
  my @indices = map {
      /^$entrypat\.\d+\.(.*)/ && $1;
  } grep {
      /^$entrypat/
  } keys %{$NWC::Device::rawdata};
  my %seen = ();
  my @o = map {[split /\./]} sort grep !$seen{$_}++, @indices;
  return @o;
}

sub get_size {
  my $self = shift;
  my $entry = shift;
  my $entrypat = $entry;
  $entrypat =~ s/\./\\\./g;
  my @entries = grep {
      /^$entrypat/
  } keys %{$NWC::Device::rawdata};
  return scalar(@entries);
}

sub get_object {
  my $self = shift;
  my $object = shift;
  my @indices = @_;
  #my $oid = $object.'.'.join('.', @indices);
  my $oid = $object;
  $oid .= '.'.join('.', @indices) if (@indices);
  return $NWC::Device::rawdata->{$oid};
}

sub get_object_value {
  my $self = shift;
  my $object = shift;
  my $values = shift;
  my @indices = @_;
  my $key = $self->get_object($object, @indices);
  if (defined $key) {
    return $values->{$key};
  } else {
    return undef;
  }
}

#SNMP::Utils::counter([$idxs1, $idxs2], $idx1, $idx2),
# this flattens a n-dimensional array and returns the absolute position
# of the element at position idx1,idx2,...,idxn
# element 1,2 in table 0,0 0,1 0,2 1,0 1,1 1,2 2,0 2,1 2,2 is at pos 6
sub get_number {
  my $self = shift;
  my $indexlists = shift; #, zeiger auf array aus [1, 2]
  my @element = @_;
  my $dimensions = scalar(@{$indexlists->[0]});
  my @sorted = ();
  my $number = 0;
  if ($dimensions == 1) {
    @sorted =
        sort { $a->[0] <=> $b->[0] } @{$indexlists};
  } elsif ($dimensions == 2) {
    @sorted =
        sort { $a->[0] <=> $b->[0] || $a->[1] <=> $b->[1] } @{$indexlists};
  } elsif ($dimensions == 3) {
    @sorted =
        sort { $a->[0] <=> $b->[0] ||
               $a->[1] <=> $b->[1] ||
               $a->[2] <=> $b->[2] } @{$indexlists};
  }
  foreach (@sorted) {
    if ($dimensions == 1) {
      if ($_->[0] == $element[0]) {
        last;
      }
    } elsif ($dimensions == 2) {
      if ($_->[0] == $element[0] && $_->[1] == $element[1]) {
        last;
      }
    } elsif ($dimensions == 3) {
      if ($_->[0] == $element[0] &&
          $_->[1] == $element[1] &&
          $_->[2] == $element[2]) {
        last;
      }
    }
    $number++;
  }
  return ++$number;
}

sub mib {
  my $self = shift;
  my $mib = shift;
  my $condition = {
      0 => 'other',
      1 => 'ok',
      2 => 'degraded',
      3 => 'failed',
  };
  my $MibRevMajor = $mib.'.1.0';
  my $MibRevMinor = $mib.'.2.0';
  my $MibRevCondition = $mib.'.3.0';
  return (
      $self->SNMP::Utils::get_object($MibRevMajor),
      $self->SNMP::Utils::get_object($MibRevMinor),
      $self->SNMP::Utils::get_object_value($MibRevCondition, $condition));
};

;
package main;
#! /usr/bin/perl

use strict;

use vars qw ($PROGNAME $REVISION $CONTACT $TIMEOUT $STATEFILESDIR $needs_restart %commandline);

$PROGNAME = "check_nwc_health";
$REVISION = '$Revision: 1.7 $';
$CONTACT = 'gerhard.lausser@consol.de';
$TIMEOUT = 60;
$STATEFILESDIR = '/var/tmp/check_nwc_health';

use constant OK         => 0;
use constant WARNING    => 1;
use constant CRITICAL   => 2;
use constant UNKNOWN    => 3;
use constant DEPENDENT  => 4;

my @modes = (
  ['device::uptime',
      'uptime', undef,
      'Check the uptime of the device' ],
  ['device::hardware::health',
      'hardware-health', undef,
      'Check the status of environmental equipment (fans, temperatures, power)' ],
  ['device::hardware::load',
      'cpu-load', ['cpu-usage'],
      'Check the CPU load of the device' ],
  ['device::hardware::memory',
      'memory-usage', undef,
      'Check the memory usage of the device' ],
#  ['device::interfaces::traffic',
#      'interface-traffic', undef,
#      'Check the in- and outgoing traffic on interfaces' ],
  ['device::interfaces::usage',
      'interface-usage', undef,
      'Check the utilization of interfaces' ],
  ['device::interfaces::errors',
      'interface-errors', undef,
      'Check the error-rate of interfaces (errors+discards per sec)' ],
  ['device::interfaces::operstatus',
      'interface-status', undef,
      'Check the status of interfaces' ],
  ['device::interfaces::list',
      'list-interfaces', undef,
      'Show the interfaces of the device and update the name cache' ],
  ['device::interfaces::listdetail',
      'list-interfaces-detail', undef,
      'Show the interfaces of the device and some details' ],
  ['device::shinken::interface',
      'create-shinken-service', undef,
      'Create a Shinken service definition' ],
  ['device::hsrp::state',
      'hsrp-state', undef,
      'Check the state in a HSRP group' ],
  ['device::hsrp::failover',
      'hsrp-failover', undef,
      'Check if a HSRP group\'s nodes have changed their roles' ],
  ['device::hsrp::list',
      'list-hsrp-groups', undef,
      'Show the HSRP groups configured on this device' ],
  ['device::ha::role',
      'ha-role', undef,
      'Check the role in a ha group' ],
  ['device::svn::status',
      'svn-status', undef,
      'Check the status of the svn subsystem' ],
  ['device::mngmt::status',
      'mngmt-status', undef,
      'Check the status of the management subsystem' ],
  ['device::fw::policy::installed',
      'fw-policy', undef,
      'Check the installed firewall policy' ],
  ['device::fw::policy::connections',
      'fw-connections', undef,
      'Check the number of firewall policy connections' ],
  ['device::lb::pool::completeness',
      'pool-completeness', undef,
      'Check the members of a load balancer pool' ],
  ['device::lb::pool::list',
      'list-pools', undef,
      'List load balancer pools' ],
  ['device::walk',
      'walk', undef,
      'Show snmpwalk command with the oids necessary for a simulation' ],
  # todo device::modules::{filtering,management,ha,svn}
);
my $modestring = "";
my $longest = length ((reverse sort {length $a <=> length $b} map { $_->[1] } @modes)[0]);
my $format = "       %-".
  (length ((reverse sort {length $a <=> length $b} map { $_->[1] } @modes)[0])).
  "s\t(%s)\n";
foreach (@modes) {
  $modestring .= sprintf $format, $_->[1], $_->[3];
}
$modestring .= sprintf "\n";


my $plugin = Nagios::MiniPlugin->new(
    shortname => '',
    usage => 'Usage: %s [ -v|--verbose ] [ -t <timeout> ] '.
        '--hostname <network-component> --community <snmp-community>'.
        '  ...]',
    version => $REVISION,
    blurb => 'This plugin checks various parameters of network components ',
    url => 'http://labs.consol.de/nagios/check_nwc_health',
    timeout => 60,
    shortname => '',
);
$plugin->add_arg(
    spec => 'blacklist|b=s',
    help => '--blacklist
   Blacklist some (missing/failed) components',
    required => 0,
    default => '',
);
#$plugin->add_arg(
#    spec => 'customthresholds|c=s',
#    help => '--customthresholds
#   Use custom thresholds for certain temperatures',
#    required => 0,
#);
#$plugin->add_arg(
#    spec => 'perfdata=s',
#    help => '--perfdata=[short]
#   Output performance data. If your performance data string becomes
#   too long and is truncated by Nagios, then you can use --perfdata=short
#   instead. This will output temperature tags without location information',
#    required => 0,
#);
$plugin->add_arg(
    spec => 'hostname|H=s',
    help => '--hostname
   Hostname or IP-address of the switch or router',
    required => 0,
);
$plugin->add_arg(
    spec => 'port=i',
    help => '--port
   The SNMP port to use (default: 161)',
    required => 0,
    default => 161,
);
$plugin->add_arg(
    spec => 'protocol|P=s',
    help => '--protocol
   The SNMP protocol to use (default: 2c, other possibilities: 1,3)',
    required => 0,
    default => '2c',
);
$plugin->add_arg(
    spec => 'community|C=s',
    help => '--community
   SNMP community of the server (SNMP v1/2 only)',
    required => 0,
    default => 'public',
);
$plugin->add_arg(
    spec => 'username=s',
    help => '--username
   The securityName for the USM security model (SNMPv3 only)',
    required => 0,
);
$plugin->add_arg(
    spec => 'authpassword=s',
    help => '--authpassword
   The authentication password for SNMPv3',
    required => 0,
);
$plugin->add_arg(
    spec => 'authprotocol=s',
    help => '--authprotocol
   The authentication protocol for SNMPv3 (md5|sha)',
    required => 0,
);
$plugin->add_arg(
    spec => 'privpassword=s',
    help => '--privpassword
   The password for authPriv security level',
    required => 0,
);
$plugin->add_arg(
    spec => 'privprotocol=s',
    help => '--privprotocol
   The private protocol for SNMPv3 (des|aes|aes128|3des|3desde)',
    required => 0,
);
$plugin->add_arg(
    spec => 'warning=s',
    help => '--warning
   The warning threshold',
    required => 0,
);
$plugin->add_arg(
    spec => 'mode=s',
    help => "--mode
   A keyword which tells the plugin what to do
$modestring",
    required => 1,
);
$plugin->add_arg(
    spec => 'name=s',
    help => "--name
   The name of an interface",
    required => 0,
);
$plugin->add_arg(
    spec => 'regexp',
    help => "--regexp
   A flag indicating that --name is a regular expression",
    required => 0,
);
$plugin->add_arg(
    spec => 'ifspeedin=i',
    help => "--ifspeedin
   Override the ifspeed oid of an interface (only inbound)",
    required => 0,
);
$plugin->add_arg(
    spec => 'ifspeedout=i',
    help => "--ifspeedout
   Override the ifspeed oid of an interface (only outbound)",
    required => 0,
);
$plugin->add_arg(
    spec => 'ifspeed=i',
    help => "--ifspeed
   Override the ifspeed oid of an interface",
    required => 0,
);
$plugin->add_arg(
    spec => 'units=s',
    help => "--units
   One of %, B, KB, MB, GB, Bit, KBi, MBi, GBi. (used for e.g. mode interface-usage)",
    required => 0,
);
$plugin->add_arg(
    spec => 'role=s',
    help => "--role
   The role of this device in a hsrp group (active/standby/listen)",
    required => 0,
);
$plugin->add_arg(
    spec => 'lookback=s',
    help => "--lookback
   The amount of time you want to look back when calculating average rates.
   Use it for mode interface-errors or interface-usage. Without --lookback
   the time between two runs of check_nwc_health is the base for calculations.
   If you want your checkresult to be based for example on the past hour,
   use --lookback 3600. ",
    required => 0,
);
$plugin->add_arg(
    spec => 'critical=s',
    help => '--critical
   The critical threshold',
    required => 0,
);
$plugin->add_arg(
    spec => 'mitigation=s',
    help => "--mitigation
   The parameter allows you to change a critical error to a warning.",
    required => 0,
);
$plugin->add_arg(
    spec => 'servertype=s',
    help => '--servertype
   The type of the network device: cisco (default). Use it if auto-detection
   is not possible',
    required => 0,
);
$plugin->add_arg(
    spec => 'statefilesdir=s',
    help => '--statefilesdir
   An alternate directory where the plugin can save files',
    required => 0,
);
$plugin->add_arg(
    spec => 'snmpwalk=s',
    help => '--snmpwalk
   A file with the output of a snmpwalk (used for simulation)
   Use it instead of --hostname',
    required => 0,
);
$plugin->add_arg(
    spec => 'snmphelp',
    help => '--snmphelp
   Output the list of OIDs you need to walk for a simulation file',
    required => 0,
);
$plugin->add_arg(
    spec => 'multiline',
    help => '--multiline
   Multiline output',
    required => 0,
);

$plugin->getopts();
if ($plugin->opts->multiline) {
  $ENV{NRPE_MULTILINESUPPORT} = 1;
} else {
  $ENV{NRPE_MULTILINESUPPORT} = 0;
}
if ($plugin->opts->snmphelp) {
  my @subtrees = ("1");
  foreach my $mib (keys %{$NWC::Device::mibs_and_oids}) {
    foreach my $table (grep {/Table$/} keys %{$NWC::Device::mibs_and_oids->{$mib}}) {
      push(@subtrees, $NWC::Device::mibs_and_oids->{$mib}->{$table});
    }
  }
  printf "snmpwalk -On ... %s\n", join(" ", @subtrees);
  printf "snmpwalk -On ... %s\n", join(" ", @subtrees);
  exit 0;
}
if ($plugin->opts->community) {
  if ($plugin->opts->community =~ /^snmpv3(.)(.+)/) {
    my $separator = $1;
    my ($authprotocol, $authpassword, $privprotocol, $privpassword, $username) =
        split(/$separator/, $2);
    $plugin->override_opt('authprotocol', $authprotocol) 
        if defined($authprotocol) && $authprotocol;
    $plugin->override_opt('authpassword', $authpassword) 
        if defined($authpassword) && $authpassword;
    $plugin->override_opt('privprotocol', $privprotocol) 
        if defined($privprotocol) && $privprotocol;
    $plugin->override_opt('privpassword', $privpassword) 
        if defined($privpassword) && $privpassword;
    $plugin->override_opt('username', $username) 
        if defined($username) && $username;
    $plugin->override_opt('protocol', '3') ;
  }
}
if ($plugin->opts->snmpwalk) {
  $plugin->override_opt('hostname', 'snmpwalk.file') 
}
if (! $plugin->opts->statefilesdir) {
  if (exists $ENV{OMD_ROOT}) {
    $plugin->override_opt('statefilesdir', $ENV{OMD_ROOT}."/var/tmp/check_nwc_health");
  } else {
    $plugin->override_opt('statefilesdir', $STATEFILESDIR);
  }
}


$plugin->{messages}->{unknown} = []; # wg. add_message(UNKNOWN,...)

$plugin->{info} = []; # gefrickel

if ($plugin->opts->mode =~ /^my-([^\-.]+)/) {
  my $param = $plugin->opts->mode;
  $param =~ s/\-/::/g;
  push(@modes, [$param, $plugin->opts->mode, undef, 'my extension']);
} elsif ($plugin->opts->mode eq 'encode') {
  my $input = <>;
  chomp $input;
  $input =~ s/([^A-Za-z0-9])/sprintf("%%%02X", ord($1))/seg;
  printf "%s\n", $input;
  exit 0;
} elsif ((! grep { $plugin->opts->mode eq $_ } map { $_->[1] } @modes) &&
    (! grep { $plugin->opts->mode eq $_ } map { defined $_->[2] ? @{$_->[2]} : () } @modes)) {
  printf "UNKNOWN - mode %s\n", $plugin->opts->mode;
  $plugin->opts->print_help();
  exit 3;
}
if ($plugin->opts->name && $plugin->opts->name =~ /(%22)|(%27)/) {
  my $name = $plugin->opts->name;
  $name =~ s/\%([A-Fa-f0-9]{2})/pack('C', hex($1))/seg;
  $plugin->override_opt('name', $name);
}

$SIG{'ALRM'} = sub {
  printf "UNKNOWN - check_nwc_health timed out after %d seconds\n", 
      $plugin->opts->timeout;
  exit $ERRORS{UNKNOWN};
};
alarm($plugin->opts->timeout);

$NWC::Device::plugin = $plugin;
$NWC::Device::mode = (
    map { $_->[0] }
    grep {
       ($plugin->opts->mode eq $_->[1]) ||
       ( defined $_->[2] && grep { $plugin->opts->mode eq $_ } @{$_->[2]})
    } @modes
)[0];
my $server = NWC::Device->new( runtime => {

    plugin => $plugin,
    options => {
        servertype => $plugin->opts->servertype,
        verbose => $plugin->opts->verbose,
        customthresholds => $plugin->opts->get('customthresholds'),
        blacklist => $plugin->opts->blacklist,
#        celsius => $CELSIUS,
#        perfdata => $PERFDATA,
#        extendedinfo => $EXTENDEDINFO,
#        hwinfo => $HWINFO,
#        noinstlevel => $NOINSTLEVEL,
    },
},);
#$server->dumper();
if (! $plugin->check_messages()) {
  $server->init();
  if (! $plugin->check_messages()) {
    $plugin->add_message(OK, $server->get_summary()) 
        if $server->get_summary();
    $plugin->add_message(OK, $server->get_extendedinfo()) 
        if $server->get_extendedinfo();
  } 
} else {
  $plugin->add_message(CRITICAL, 'wrong device');
}
my ($code, $message) = $plugin->opts->multiline ? 
    $plugin->check_messages(join => "\n", join_all => ', ') :
    $plugin->check_messages(join => ', ', join_all => ', ');
$message .= sprintf "\n%s\n", join("\n", @{$NWC::Device::info})
    if $plugin->opts->verbose >= 1;
#printf "%s\n", Data::Dumper::Dumper($plugin->{info});
$plugin->nagios_exit($code, $message);

