#! /usr/bin/perl -w
# nagios: -epn

package Monitoring::GLPlugin::Commandline::Extraopts;
use strict;
use File::Basename;
use strict;

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    file => $params{file},
    commandline => $params{commandline},
    config => {},
    section => 'default_no_section',
  };
  bless $self, $class;
  $self->prepare_file_and_section();
  $self->init();
  return $self;
}

sub prepare_file_and_section {
  my $self = shift;
  if (! defined $self->{file}) {
    # ./check_stuff --extra-opts
    $self->{section} = basename($0);
    $self->{file} = $self->get_default_file();
  } elsif ($self->{file} =~ /^[^@]+$/) {
    # ./check_stuff --extra-opts=special_opts
    $self->{section} = $self->{file};
    $self->{file} = $self->get_default_file();
  } elsif ($self->{file} =~ /^@(.*)/) {
    # ./check_stuff --extra-opts=@/etc/myconfig.ini
    $self->{section} = basename($0);
    $self->{file} = $1;
  } elsif ($self->{file} =~ /^(.*?)@(.*)/) {
    # ./check_stuff --extra-opts=special_opts@/etc/myconfig.ini
    $self->{section} = $1;
    $self->{file} = $2;
  }
}

sub get_default_file {
  my $self = shift;
  foreach my $default (qw(/etc/nagios/plugins.ini
      /usr/local/nagios/etc/plugins.ini
      /usr/local/etc/nagios/plugins.ini
      /etc/opt/nagios/plugins.ini
      /etc/nagios-plugins.ini
      /usr/local/etc/nagios-plugins.ini
      /etc/opt/nagios-plugins.ini)) {
    if (-f $default) {
      return $default;
    }
  }
  return undef;
}

sub init {
  my $self = shift;
  if (! defined $self->{file}) {
    $self->{errors} = sprintf 'no extra-opts file specified and no default file found';
  } elsif (! -f $self->{file}) {
    $self->{errors} = sprintf 'could not open %s', $self->{file};
  } else {
    my $data = do { local (@ARGV, $/) = $self->{file}; <> };
    my $in_section = 'default_no_section';
    foreach my $line (split(/\n/, $data)) {
      if ($line =~ /\[(.*)\]/) {
        $in_section = $1;
      } elsif ($line =~ /(.*?)\s*=\s*(.*)/) {
        $self->{config}->{$in_section}->{$1} = $2;
      }
    }
  }
}

sub is_valid {
  my $self = shift;
  return ! exists $self->{errors};
}

sub overwrite {
  my $self = shift;
  if (scalar(keys %{$self->{config}->{default_no_section}}) > 0) {
    foreach (keys %{$self->{config}->{default_no_section}}) {
      $self->{commandline}->{$_} = $self->{config}->{default_no_section}->{$_};
    }
  }
  if (exists $self->{config}->{$self->{section}}) {
    foreach (keys %{$self->{config}->{$self->{section}}}) {
      $self->{commandline}->{$_} = $self->{config}->{$self->{section}}->{$_};
    }
  }
}

sub errors {
  my $self = shift;
  return $self->{errors} || "";
}



package Monitoring::GLPlugin::Commandline::Getopt;
use strict;
use File::Basename;
use Getopt::Long qw(:config no_ignore_case bundling);

# Standard defaults
our %DEFAULT = (
  timeout => 15,
  verbose => 0,
  license =>
"This monitoring 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
our @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
our %DEFER_ARGS = map { $_ => 1 } qw(timeout verbose);

sub _init {
  my ($self, %params) = @_;
  # Check params
  my %attr = (
    usage => 1,
    version => 0,
    url => 0,
    plugin => undef,
    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 $self->{_attr}->{$_} if exists $self->{_attr}->{$_};
  }

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

  $self
}

sub new {
  my ($class, @params) = @_;
  require Monitoring::GLPlugin::Commandline::Extraopts
      if ! grep /BEGIN/, keys %Monitoring::GLPlugin::Commandline::Extraopts::;
  my $self = bless {}, $class;
  $self->_init(@params);
}

sub decode_rfc3986 {
  my ($self, $password) = @_;
  if ($password && $password =~ /^rfc3986:\/\/(.*)/) {
    $password = $1;
    $password =~ s/%([A-Za-z0-9]{2})/chr(hex($1))/seg;
  }
  return $password;
}

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

sub mod_arg {
  my ($self, $argname, %arg) = @_;
  foreach my $old_arg (@{$self->{_args}}) {
    next unless $old_arg->{spec} =~ /(\w+).*/ && $argname eq $1;
    foreach my $key (keys %arg) {
      $old_arg->{$key} = $arg{$key};
    }
  }
}

sub getopts {
  my ($self) = @_;
  my %commandline = ();
  $self->{opts}->{all_my_opts} = {};
  my @params = map { $_->{spec} } @{$self->{_args}};
  if (! GetOptions(\%commandline, @params)) {
    $self->print_help();
    exit 3;
  } else {
    no strict 'refs';
    no warnings 'redefine';
    if (exists $commandline{'extra-opts'}) {
      # read the extra file and overwrite other parameters
      my $extras = Monitoring::GLPlugin::Commandline::Extraopts->new(
          file => $commandline{'extra-opts'},
          commandline => \%commandline
      );
      if (! $extras->is_valid()) {
        printf "UNKNOWN - extra-opts are not valid: %s\n", $extras->errors();
        exit 3;
      } else {
        $extras->overwrite();
      }
    }
    do { $self->print_help(); exit 0; } if $commandline{help};
    do { $self->print_version(); exit 0 } if $commandline{version};
    do { $self->print_usage(); exit 3 } if $commandline{usage};
    foreach (map { $_->{spec} =~ /^([\w\-]+)/; $1; } @{$self->{_args}}) {
      my $field = $_;
      *{"$field"} = sub {
        return $self->{opts}->{$field};
      };
    }
    *{"all_my_opts"} = sub {
      return $self->{opts}->{all_my_opts};
    };
    foreach (@{$self->{_args}}) {
      $_->{spec} =~ /^([\w\-]+)/;
      my $spec = $1;
      my $envname = uc $spec;
      $envname =~ s/\-/_/g;
      if (! exists $commandline{$spec}) {
        # Kommandozeile hat oberste Prioritaet
        # Also: --option ueberschreibt NAGIOS__HOSTOPTION
        # Aaaaber: extra-opts haben immer noch Vorrang vor allem anderen.
        # https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-envvars.html
        # beschreibt das anders, Posix-Tools verhalten sich auch entsprechend.
        # Irgendwann wird das hier daher umgeschrieben, so dass extra-opts
        # die niedrigste Prioritaet erhalten.
        if (exists $ENV{'NAGIOS__SERVICE'.$envname}) {
          $commandline{$spec} = $ENV{'NAGIOS__SERVICE'.$envname};
        } elsif (exists $ENV{'NAGIOS__HOST'.$envname}) {
          $commandline{$spec} = $ENV{'NAGIOS__HOST'.$envname};
        }
      }
      $self->{opts}->{$spec} = $_->{default};
    }
    foreach (map { $_->{spec} =~ /^([\w\-]+)/; $1; }
        grep { exists $_->{required} && $_->{required} } @{$self->{_args}}) {
      do { $self->print_usage(); exit 3 } if ! exists $commandline{$_};
    }
    foreach (grep { exists $_->{default} } @{$self->{_args}}) {
      $_->{spec} =~ /^([\w\-]+)/;
      my $spec = $1;
      $self->{opts}->{$spec} = $_->{default};
    }
    foreach (keys %commandline) {
      $self->{opts}->{$_} = $commandline{$_};
      $self->{opts}->{all_my_opts}->{$_} = $commandline{$_};
    }
    foreach (grep { exists $_->{env} } @{$self->{_args}}) {
      $_->{spec} =~ /^([\w\-]+)/;
      my $spec = $1;
      if (exists $ENV{'NAGIOS__HOST'.$_->{env}}) {
        $self->{opts}->{$spec} = $ENV{'NAGIOS__HOST'.$_->{env}};
      }
      if (exists $ENV{'NAGIOS__SERVICE'.$_->{env}}) {
        $self->{opts}->{$spec} = $ENV{'NAGIOS__SERVICE'.$_->{env}};
      }
    }
    foreach (grep { exists $_->{aliasfor} } @{$self->{_args}}) {
      my $field = $_->{aliasfor};
      $_->{spec} =~ /^([\w\-]+)/;
      my $aliasfield = $1;
      next if $self->{opts}->{$field};
      $self->{opts}->{$field} = $self->{opts}->{$aliasfield};
      *{"$field"} = sub {
        return $self->{opts}->{$field};
      };
    }
    foreach (grep { exists $_->{decode} } @{$self->{_args}}) {
      my $decoding = $_->{decode};
      $_->{spec} =~ /^([\w\-]+)/;
      my $spec = $1;
      if (exists $self->{opts}->{$spec}) {
        if ($decoding eq "rfc3986") {
	  $self->{opts}->{$spec} =
	      $self->decode_rfc3986($self->{opts}->{$spec});
	}
      }
    }
  }
}

sub create_opt {
  my ($self, $key) = @_;
  no strict 'refs';
  *{"$key"} = sub {
      return $self->{opts}->{$key};
  };
}

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

sub get {
  my ($self, $opt) = @_;
  return $self->{opts}->{$opt};
}

sub print_help {
  my ($self) = @_;
  $self->print_version();
  printf "\n%s\n", $self->{_attr}->{license};
  printf "\n%s\n\n", $self->{_attr}->{blurb};
  $self->print_usage();
  foreach (grep {
      ! (exists $_->{hidden} && $_->{hidden}) 
  } @{$self->{_args}}) {
    printf " %s\n", $_->{help};
  }
}

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

sub print_version {
  my ($self) = @_;
  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) = @_;
  printf "%s\n", $self->{_attr}->{license};
  print "\n";
}



package Monitoring::GLPlugin::Commandline;
use strict;
use IO::File;
use constant { OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3, DEPENDENT => 4 };
our %ERRORS = (
    'OK'        => OK,
    'WARNING'   => WARNING,
    'CRITICAL'  => CRITICAL,
    'UNKNOWN'   => UNKNOWN,
    'DEPENDENT' => DEPENDENT,
);

our %STATUS_TEXT = reverse %ERRORS;
our $AUTOLOAD;


sub new {
  my ($class, %params) = @_;
  require Monitoring::GLPlugin::Commandline::Getopt
      if ! grep /BEGIN/, keys %Monitoring::GLPlugin::Commandline::Getopt::;
  my $self = {
       perfdata => [],
       messages => {
         ok => [],
         warning => [],
         critical => [],
         unknown => [],
       },
       args => [],
       opts => Monitoring::GLPlugin::Commandline::Getopt->new(%params),
       modes => [],
       statefilesdir => undef,
  };
  foreach (qw(shortname usage version url plugin blurb extra
      license timeout)) {
    $self->{$_} = $params{$_};
  }
  bless $self, $class;
  $self->{name} = $self->{plugin};

  $self
}

sub AUTOLOAD {
  my ($self, @params) = @_;
  return if ($AUTOLOAD =~ /DESTROY/);
  $self->debug("AUTOLOAD %s\n", $AUTOLOAD)
        if $self->{opts}->verbose >= 2;
  if ($AUTOLOAD =~ /^.*::(add_arg|override_opt|create_opt)$/) {
    $self->{opts}->$1(@params);
  }
}

sub DESTROY {
  my ($self) = @_;
  # ohne dieses DESTROY rennt nagios_exit in obiges AUTOLOAD rein
  # und fliegt aufs Maul, weil {opts} bereits nicht mehr existiert.
  # Unerklaerliches Verhalten.
}

sub debug {
  my ($self, $format, @message) = @_;
  if ($self->opts->verbose && $self->opts->verbose > 10) {
    printf("%s: ", scalar localtime);
    printf($format, @message);
    printf "\n";
  }
  if ($Monitoring::GLPlugin::tracefile) {
    my $logfh = IO::File->new();
    $logfh->autoflush(1);
    if ($logfh->open($Monitoring::GLPlugin::tracefile, "a")) {
      $logfh->printf("%s: ", scalar localtime);
      $logfh->printf($format, @message);
      $logfh->printf("\n");
      $logfh->close();
    }
  }
}

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

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

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

sub selected_perfdata {
  my ($self, $label) = @_;
  if ($self->opts->can("selectedperfdata") && $self->opts->selectedperfdata) {
    my $pattern = $self->opts->selectedperfdata;
    return ($label =~ /$pattern/i) ? 1 : 0;
  } else {
    return 1;
  }
}

sub add_perfdata {
  my ($self, %args) = @_;
#printf "add_perfdata %s\n", Data::Dumper::Dumper(\%args);
#printf "add_perfdata %s\n", Data::Dumper::Dumper($self->{thresholds});
#
# wenn warning, critical, dann wird von oben ein expliziter wert mitgegeben
# wenn thresholds
#  wenn label in 
#    warningx $self->{thresholds}->{$label}->{warning} existiert
#  dann nimm $self->{thresholds}->{$label}->{warning}
#  ansonsten thresholds->default->warning
#

  my $label = $args{label};
  my $value = $args{value};
  my $uom = $args{uom} || "";
  my $format = '%d';

  if ($self->opts->can("morphperfdata") && $self->opts->morphperfdata) {
    # 'Intel [R] Interface (\d+) usage'='nic$1'
    foreach my $key (keys %{$self->opts->morphperfdata}) {
      if ($label =~ /$key/) {
        my $replacement = '"'.$self->opts->morphperfdata->{$key}.'"';
        my $oldlabel = $label;
        $label =~ s/$key/$replacement/ee;
        if (exists $self->{thresholds}->{$oldlabel}) {
          %{$self->{thresholds}->{$label}} = %{$self->{thresholds}->{$oldlabel}};
        }
      }
    }
  }
  if ($value =~ /\./) {
    if (defined $args{places}) {
      $value = sprintf '%.'.$args{places}.'f', $value;
    } else {
      $value = sprintf "%.2f", $value;
    }
  } else {
    $value = sprintf "%d", $value;
  }
  my $warn = "";
  my $crit = "";
  my $min = defined $args{min} ? $args{min} : "";
  my $max = defined $args{max} ? $args{max} : "";
  if ($args{thresholds} || (! exists $args{warning} && ! exists $args{critical})) {
    if (exists $self->{thresholds}->{$label}->{warning} &&
        defined $self->{thresholds}->{$label}->{warning}) {
      $warn = $self->{thresholds}->{$label}->{warning};
    } elsif (exists $self->{thresholds}->{default}->{warning}) {
      $warn = $self->{thresholds}->{default}->{warning};
    }
    if (exists $self->{thresholds}->{$label}->{critical} &&
        defined $self->{thresholds}->{$label}->{critical}) {
      $crit = $self->{thresholds}->{$label}->{critical};
    } elsif (exists $self->{thresholds}->{default}->{critical}) {
      $crit = $self->{thresholds}->{default}->{critical};
    }
  } else {
    if ($args{warning}) {
      $warn = $args{warning};
    }
    if ($args{critical}) {
      $crit = $args{critical};
    }
  }
  if ($uom eq "%") {
    $min = 0 if $min eq "";
    $max = 100 if $max eq "";
  }
  if (defined $args{places}) {
    # cut off excessive decimals which may be the result of a division
    # length = places*2, no trailing zeroes
    if ($warn ne "") {
      $warn = join("", map {
          s/\.0+$//; $_
      } map {
          s/(\.[1-9]+)0+$/$1/; $_
      } map {
          /[\+\-\d\.]+/ ? sprintf '%.'.2*$args{places}.'f', $_ : $_;
      } split(/([\+\-\d\.]+)/, $warn));
    }
    if ($crit ne "") {
      $crit = join("", map {
          s/\.0+$//; $_
      } map {
          s/(\.[1-9]+)0+$/$1/; $_
      } map {
          /[\+\-\d\.]+/ ? sprintf '%.'.2*$args{places}.'f', $_ : $_;
      } split(/([\+\-\d\.]+)/, $crit));
    }
    if ($min ne "") {
      $min = join("", map {
          s/\.0+$//; $_
      } map {
          s/(\.[1-9]+)0+$/$1/; $_
      } map {
          /[\+\-\d\.]+/ ? sprintf '%.'.2*$args{places}.'f', $_ : $_;
      } split(/([\+\-\d\.]+)/, $min));
    }
    if ($max ne "") {
      $max = join("", map {
          s/\.0+$//; $_
      } map {
          s/(\.[1-9]+)0+$/$1/; $_
      } map {
          /[\+\-\d\.]+/ ? sprintf '%.'.2*$args{places}.'f', $_ : $_;
      } split(/([\+\-\d\.]+)/, $max));
    }
  }
  push @{$self->{perfdata}}, sprintf("'%s'=%s%s;%s;%s;%s;%s",
      $label, $value, $uom, $warn, $crit, $min, $max)
      if $self->selected_perfdata($label);
}

sub add_pandora {
  my ($self, %args) = @_;
  my $label = $args{label};
  my $value = $args{value};

  if ($args{help}) {
    push @{$self->{pandora}}, sprintf("# HELP %s %s", $label, $args{help});
  }
  if ($args{type}) {
    push @{$self->{pandora}}, sprintf("# TYPE %s %s", $label, $args{type});
  }
  if ($args{labels}) {
    push @{$self->{pandora}}, sprintf("%s{%s} %s", $label,
        join(",", map {
            sprintf '%s="%s"', $_, $args{labels}->{$_};
        } keys %{$args{labels}}),
        $value);
  } else {
    push @{$self->{pandora}}, sprintf("%s %s", $label, $value);
  }
}

sub add_html {
  my ($self, $line) = @_;
  push @{$self->{html}}, $line;
}

sub suppress_messages {
  my ($self) = @_;
  $self->{suppress_messages} = 1;
}

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

sub reduce_messages_short {
  my ($self, $message) = @_;
  $message ||= "no problems";
  if ($self->opts->report && $self->opts->report eq "short") {
    $self->clear_messages(OK);
    $self->add_message(OK, $message) if ! $self->check_messages();
  }
}

sub reduce_messages {
  my ($self, $message) = @_;
  $message ||= "no problems";
  $self->clear_messages(OK);
  $self->add_message(OK, $message) if ! $self->check_messages();
}

sub check_messages {
  my ($self, %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 status_code {
  my ($self, $code) = @_;
  $code = (qw(ok warning critical unknown))[$code] if $code =~ /^\d+$/;
  $code = uc $code;
  $code = $ERRORS{$code} if defined $code && exists $ERRORS{$code};
  $code = UNKNOWN unless defined $code && exists $STATUS_TEXT{$code};
  return "$STATUS_TEXT{$code}";
}

sub perfdata_string {
  my ($self) = @_;
  if (scalar (@{$self->{perfdata}})) {
    return join(" ", @{$self->{perfdata}});
  } else {
    return "";
  }
}

sub metrics_string {
  my ($self) = @_;
  if (scalar (@{$self->{metrics}})) {
    return join("\n", @{$self->{metrics}});
  } else {
    return "";
  }
}

sub html_string {
  my ($self) = @_;
  if (scalar (@{$self->{html}})) {
    return join(" ", @{$self->{html}});
  } else {
    return "";
  }
}

sub nagios_exit {
  my ($self, $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;
  }
  if ($self->opts->negate) {
    my $original_code = $code;
    foreach my $from (keys %{$self->opts->negate}) {
      if ((uc $from) =~ /^(OK|WARNING|CRITICAL|UNKNOWN)$/ &&
          (uc $self->opts->negate->{$from}) =~ /^(OK|WARNING|CRITICAL|UNKNOWN)$/) {
        if ($original_code == $ERRORS{uc $from}) {
          $code = $ERRORS{uc $self->opts->negate->{$from}};
        }
      }
    }
  }
  my $output = "$STATUS_TEXT{$code}";
  $output .= " - $message" if defined $message && $message ne '';
  if ($self->opts->can("morphmessage") && $self->opts->morphmessage) {
    # 'Intel [R] Interface (\d+) usage'='nic$1'
    # '^OK.*'="alles klar"   '^CRITICAL.*'="alles hi"
    foreach my $key (keys %{$self->opts->morphmessage}) {
      if ($output =~ /$key/) {
        my $replacement = '"'.$self->opts->morphmessage->{$key}.'"';
        $output =~ s/$key/$replacement/ee;
      }
    }
  }
  if ($self->opts->negate) {
    # negate again: --negate "UNKNOWN - no peers"=ok
    my $original_code = $code;
    foreach my $from (keys %{$self->opts->negate}) {
      if ((uc $from) !~ /^(OK|WARNING|CRITICAL|UNKNOWN)$/ &&
          (uc $self->opts->negate->{$from}) =~ /^(OK|WARNING|CRITICAL|UNKNOWN)$/) {
        if ($output =~ /$from/) {
          $code = $ERRORS{uc $self->opts->negate->{$from}};
          $output =~ s/^.*? -/$STATUS_TEXT{$code} -/;
        }
      }
    }
  }
  $output =~ s/\|/!/g if $output;
  if (scalar (@{$self->{perfdata}})) {
    $output .= " | ".$self->perfdata_string();
  }
  $output .= "\n";
  if ($self->opts->can("isvalidtime") && ! $self->opts->isvalidtime) {
    $code = OK;
    $output = "OK - outside valid timerange. check results are not relevant now. original message was: ".
        $output;
  }
  if (! exists $self->{suppress_messages}) {
    $output =~ s/[^[:ascii:]]//g;
    print $output;
  }
  exit $code;
}

sub set_thresholds {
  my ($self, %params) = @_;
  if (exists $params{metric}) {
    my $metric = $params{metric};
    # erst die hartcodierten defaultschwellwerte
    $self->{thresholds}->{$metric}->{warning} = $params{warning};
    $self->{thresholds}->{$metric}->{critical} = $params{critical};
    # dann die defaultschwellwerte von der kommandozeile
    if (defined $self->opts->warning) {
      $self->{thresholds}->{$metric}->{warning} = $self->opts->warning;
    }
    if (defined $self->opts->critical) {
      $self->{thresholds}->{$metric}->{critical} = $self->opts->critical;
    }
    # dann die ganz spezifischen schwellwerte von der kommandozeile
    if ($self->opts->warningx) { # muss nicht auf defined geprueft werden, weils ein hash ist
      # Erst schauen, ob einer * beinhaltet. Von denen wird vom Laengsten
      # bis zum Kuerzesten probiert, ob die matchen. Der laengste Match
      # gewinnt.
      my @keys = keys %{$self->opts->warningx};
      my @stringkeys = ();
      my @regexkeys = ();
      foreach my $key (sort { length($b) > length($a) } @keys) {
        if ($key =~ /\*/) {
          push(@regexkeys, $key);
        } else {
          push(@stringkeys, $key);
        }
      }
      foreach my $key (@regexkeys) {
        next if $metric !~ /$key/;
        $self->{thresholds}->{$metric}->{warning} = $self->opts->warningx->{$key};
        last;
      }
      # Anschliessend nochmal schauen, ob es einen nicht-Regex-Volltreffer gibt
      foreach my $key (@stringkeys) {
        next if $key ne $metric;
        $self->{thresholds}->{$metric}->{warning} = $self->opts->warningx->{$key};
        last;
      }
    }
    if ($self->opts->criticalx) {
      my @keys = keys %{$self->opts->criticalx};
      my @stringkeys = ();
      my @regexkeys = ();
      foreach my $key (sort { length($b) > length($a) } @keys) {
        if ($key =~ /\*/) {
          push(@regexkeys, $key);
        } else {
          push(@stringkeys, $key);
        }
      }
      foreach my $key (@regexkeys) {
        next if $metric !~ /$key/;
        $self->{thresholds}->{$metric}->{critical} = $self->opts->criticalx->{$key};
        last;
      }
      # Anschliessend nochmal schauen, ob es einen nicht-Regex-Volltreffer gibt
      foreach my $key (@stringkeys) {
        next if $key ne $metric;
        $self->{thresholds}->{$metric}->{critical} = $self->opts->criticalx->{$key};
        last;
      }
    }
  } else {
    $self->{thresholds}->{default}->{warning} =
        defined $self->opts->warning ? $self->opts->warning : defined $params{warning} ? $params{warning} : 0;
    $self->{thresholds}->{default}->{critical} =
        defined $self->opts->critical ? $self->opts->critical : defined $params{critical} ? $params{critical} : 0;
  }
}

sub force_thresholds {
  my ($self, %params) = @_;
  if (exists $params{metric}) {
    my $metric = $params{metric};
    $self->{thresholds}->{$metric}->{warning} = $params{warning} || 0;
    $self->{thresholds}->{$metric}->{critical} = $params{critical} || 0;
  } else {
    $self->{thresholds}->{default}->{warning} = $params{warning} || 0;
    $self->{thresholds}->{default}->{critical} = $params{critical} || 0;
  }
}

sub get_thresholds {
  my ($self, @params) = @_;
  if (scalar(@params) > 1) {
    my %params = @params;
    my $metric = $params{metric};
    return ($self->{thresholds}->{$metric}->{warning},
        $self->{thresholds}->{$metric}->{critical});
  } else {
    return ($self->{thresholds}->{default}->{warning},
        $self->{thresholds}->{default}->{critical});
  }
}

sub check_thresholds {
  my ($self, @params) = @_;
  my $level = $ERRORS{OK};
  my $warningrange;
  my $criticalrange;
  my $value;
  if (scalar(@params) > 1) {
    my %params = @params;
    $value = $params{value};
    my $metric = $params{metric};
    if ($metric ne 'default') {
      $warningrange = defined $params{warning} ? $params{warning} :
          (exists $self->{thresholds}->{$metric}->{warning} ?
              $self->{thresholds}->{$metric}->{warning} :
              $self->{thresholds}->{default}->{warning});
      $criticalrange = defined $params{critical} ? $params{critical} :
          (exists $self->{thresholds}->{$metric}->{critical} ?
              $self->{thresholds}->{$metric}->{critical} :
              $self->{thresholds}->{default}->{critical});
    } else {
      $warningrange = (defined $params{warning}) ?
          $params{warning} : $self->{thresholds}->{default}->{warning};
      $criticalrange = (defined $params{critical}) ?
          $params{critical} : $self->{thresholds}->{default}->{critical};
    }
  } else {
    $value = $params[0];
    $warningrange = $self->{thresholds}->{default}->{warning};
    $criticalrange = $self->{thresholds}->{default}->{critical};
  }
  if (! defined $warningrange) {
    # there was no set_thresholds for defaults, no --warning, no --warningx
  } elsif ($warningrange =~ /^([-+]?[0-9]*\.?[0-9]+)$/) {
    # warning = 10, warn if > 10 or < 0
    $level = $ERRORS{WARNING}
        if ($value > $1 || $value < 0);
  } elsif ($warningrange =~ /^([-+]?[0-9]*\.?[0-9]+):$/) {
    # warning = 10:, warn if < 10
    $level = $ERRORS{WARNING}
        if ($value < $1);
  } elsif ($warningrange =~ /^~:([-+]?[0-9]*\.?[0-9]+)$/) {
    # warning = ~:10, warn if > 10
    $level = $ERRORS{WARNING}
        if ($value > $1);
  } elsif ($warningrange =~ /^([-+]?[0-9]*\.?[0-9]+):([-+]?[0-9]*\.?[0-9]+)$/) {
    # warning = 10:20, warn if < 10 or > 20
    $level = $ERRORS{WARNING}
        if ($value < $1 || $value > $2);
  } elsif ($warningrange =~ /^@([-+]?[0-9]*\.?[0-9]+):([-+]?[0-9]*\.?[0-9]+)$/) {
    # warning = @10:20, warn if >= 10 and <= 20
    $level = $ERRORS{WARNING}
        if ($value >= $1 && $value <= $2);
  }
  if (! defined $criticalrange) {
    # there was no set_thresholds for defaults, no --critical, no --criticalx
  } elsif ($criticalrange =~ /^([-+]?[0-9]*\.?[0-9]+)$/) {
    # critical = 10, crit if > 10 or < 0
    $level = $ERRORS{CRITICAL}
        if ($value > $1 || $value < 0);
  } elsif ($criticalrange =~ /^([-+]?[0-9]*\.?[0-9]+):$/) {
    # critical = 10:, crit if < 10
    $level = $ERRORS{CRITICAL}
        if ($value < $1);
  } elsif ($criticalrange =~ /^~:([-+]?[0-9]*\.?[0-9]+)$/) {
    # critical = ~:10, crit if > 10
    $level = $ERRORS{CRITICAL}
        if ($value > $1);
  } elsif ($criticalrange =~ /^([-+]?[0-9]*\.?[0-9]+):([-+]?[0-9]*\.?[0-9]+)$/) {
    # critical = 10:20, crit if < 10 or > 20
    $level = $ERRORS{CRITICAL}
        if ($value < $1 || $value > $2);
  } elsif ($criticalrange =~ /^@([-+]?[0-9]*\.?[0-9]+):([-+]?[0-9]*\.?[0-9]+)$/) {
    # critical = @10:20, crit if >= 10 and <= 20
    $level = $ERRORS{CRITICAL}
        if ($value >= $1 && $value <= $2);
  }
  return $level;
}

sub mod_threshold {
  # this method can be used to modify/multiply thresholds or upper and lower
  # limit of a threshold range. For example, we have thresholds for an
  # interface usage together with the maximum bandwidth and want to
  # create thresholds for bitrates.
  my ($self, $threshold, $func) = @_;
  if (! $threshold) {
    return "";
  } elsif ($threshold =~ /^([-+]?[0-9]*\.?[0-9]+)$/) {
    # 10
    return &{$func}($1);
  } elsif ($threshold =~ /^([-+]?[0-9]*\.?[0-9]+):$/) {
    # 10:
    return &{$func}($1).":";
  } elsif ($threshold =~ /^~:([-+]?[0-9]*\.?[0-9]+)$/) {
    # ~:10
    return "~:".&{$func}($1);
  } elsif ($threshold =~ /^([-+]?[0-9]*\.?[0-9]+):([-+]?[0-9]*\.?[0-9]+)$/) {
    # 10:20
    return &{$func}($1).":".&{$func}($2);
  } elsif ($threshold =~ /^@([-+]?[0-9]*\.?[0-9]+):([-+]?[0-9]*\.?[0-9]+)$/) {
    # @10:20
    return "@".&{$func}($1).":".&{$func}($2);
  } else {
    return $threshold."scheise";
  }
}

sub strequal {
  my($self, $str1, $str2) = @_;
  return 1 if ! defined $str1 && ! defined $str2;
  return 0 if ! defined $str1 && defined $str2;
  return 0 if defined $str1 && ! defined $str2;
  return 1 if $str1 eq $str2;
  return 0;
}



package Monitoring::GLPlugin;

=head1 Monitoring::GLPlugin

Monitoring::GLPlugin - infrastructure functions to build a monitoring plugin

=cut

use strict;
use IO::File;
use File::Basename;
use Digest::MD5 qw(md5_hex);
use Errno;
use JSON;
use File::Slurp qw(read_file);
use Data::Dumper;
$Data::Dumper::Indent = 1;
eval {
  # avoid "used only once" because older Data::Dumper don't have this
  # use OMD please because OMD has everything!
  no warnings 'all';
  $Data::Dumper::Sparseseen = 1;
};
our $AUTOLOAD;
*VERSION = \'5.36';

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

{
  our $mode = undef;
  our $plugin = undef;
  our $pluginname = undef;
  our $blacklist = undef;
  our $info = [];
  our $extendedinfo = [];
  our $summary = [];
  our $variables = {};
  our $survive_sudo_env = ["LD_LIBRARY_PATH", "SHLIB_PATH"];
}

sub new {
  my ($class, %params) = @_;
  my $self = {};
  bless $self, $class;
  require Monitoring::GLPlugin::Commandline
      if ! grep /BEGIN/, keys %Monitoring::GLPlugin::Commandline::;
  require Monitoring::GLPlugin::Item
      if ! grep /BEGIN/, keys %Monitoring::GLPlugin::Item::;
  require Monitoring::GLPlugin::TableItem
      if ! grep /BEGIN/, keys %Monitoring::GLPlugin::TableItem::;
  $params{plugin} ||= basename($ENV{'NAGIOS_PLUGIN'} || $0);
  $Monitoring::GLPlugin::pluginname = $params{plugin};
  $Monitoring::GLPlugin::plugin = Monitoring::GLPlugin::Commandline->new(%params);
  return $self;
}

sub rebless {
  my ($self, $class) = @_;
  bless $self, $class;
  $self->debug('using '.$class);
  # gilt nur fuer "echte" Fabrikate mit "Classes::" vorndran
  $self->{classified_as} = ref($self) if $class !~ /^Monitoring::GLPlugin/;
}

sub init {
  my ($self) = @_;
  if ($self->opts->can("blacklist") && $self->opts->blacklist &&
      -f $self->opts->blacklist) {
    $self->opts->blacklist = do {
        local (@ARGV, $/) = $self->opts->blacklist; <> };
  }
}

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

sub no_such_mode {
  my ($self) = @_;
  $self->nagios_exit(3,
      sprintf "Mode %s is not implemented for this type of device",
      $self->opts->mode
  );
  exit 3;
}

#########################################################
# framework-related. setup, options
#
sub add_default_args {
  my ($self) = @_;
  $self->add_arg(
      spec => 'mode=s',
      help => "--mode
   A keyword which tells the plugin what to do",
      required => 1,
  );
  $self->add_arg(
      spec => 'regexp',
      help => "--regexp
   Parameter name/name2/name3 will be interpreted as (perl) regular expression",
      required => 0,);
  $self->add_arg(
      spec => 'warning=s',
      help => "--warning
   The warning threshold",
      required => 0,);
  $self->add_arg(
      spec => 'critical=s',
      help => "--critical
   The critical threshold",
      required => 0,);
  $self->add_arg(
      spec => 'warningx=s%',
      help => '--warningx
   The extended warning thresholds
   e.g. --warningx db_msdb_free_pct=6: to override the threshold for a
   specific item ',
      required => 0,
  );
  $self->add_arg(
      spec => 'criticalx=s%',
      help => '--criticalx
   The extended critical thresholds',
      required => 0,
  );
  $self->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,
  );
  $self->add_arg(
      spec => 'name=s',
      help => "--name
   The name of a specific component to check",
      required => 0,
      decode => "rfc3986",
  );
  $self->add_arg(
      spec => 'name2=s',
      help => "--name2
   The secondary name of a component",
      required => 0,
      decode => "rfc3986",
  );
  $self->add_arg(
      spec => 'name3=s',
      help => "--name3
   The tertiary name of a component",
      required => 0,
      decode => "rfc3986",
  );
  $self->add_arg(
      spec => 'extra-opts=s',
      help => "--extra-opts
   read command line arguments from an external file",
      required => 0,
  );
  $self->add_arg(
      spec => 'blacklist|b=s',
      help => '--blacklist
   Blacklist some (missing/failed) components',
      required => 0,
      default => '',
  );
  $self->add_arg(
      spec => 'mitigation=s',
      help => "--mitigation
   The parameter allows you to change a critical error to a warning.
   It works only for specific checks. Which ones? Try it out or look in the code.
   --mitigation warning ranks an error as warning which by default would be critical.",
      required => 0,
  );
  $self->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,
  );
  $self->add_arg(
      spec => 'environment|e=s%',
      help => "--environment
   Add a variable to the plugin's environment",
      required => 0,
  );
  $self->add_arg(
      spec => 'negate=s%',
      help => "--negate
   Emulate the negate plugin. --negate warning=critical --negate unknown=critical",
      required => 0,
  );
  $self->add_arg(
      spec => 'morphmessage=s%',
      help => '--morphmessage
   Modify the final output message',
      required => 0,
      decode => "rfc3986",
  );
  $self->add_arg(
      spec => 'morphperfdata=s%',
      help => "--morphperfdata
   The parameter allows you to change performance data labels.
   It's a perl regexp and a substitution.
   Example: --morphperfdata '(.*)ISATAP(.*)'='\$1patasi\$2'",
      required => 0,
      decode => "rfc3986",
  );
  $self->add_arg(
      spec => 'selectedperfdata=s',
      help => "--selectedperfdata
   The parameter allows you to limit the list of performance data. It's a perl regexp.
   Only matching perfdata show up in the output",
      required => 0,
  );
  $self->add_arg(
      spec => 'report=s',
      help => "--report
   Can be used to shorten the output",
      required => 0,
      default => 'long',
  );
  $self->add_arg(
      spec => 'multiline',
      help => '--multiline
   Multiline output',
      required => 0,
  );
  $self->add_arg(
      spec => 'with-mymodules-dyn-dir=s',
      help => "--with-mymodules-dyn-dir
   Add-on modules for the my-modes will be searched in this directory",
      required => 0,
  );
  $self->add_arg(
      spec => 'statefilesdir=s',
      help => '--statefilesdir
   An alternate directory where the plugin can save files',
      required => 0,
      env => 'STATEFILESDIR',
  );
  $self->add_arg(
      spec => 'isvalidtime=i',
      help => '--isvalidtime
   Signals the plugin to return OK if now is not a valid check time',
      required => 0,
      default => 1,
  );
  $self->add_arg(
      spec => 'reset',
      help => "--reset
   remove the state file",
      required => 0,
      hidden => 1,
  );
  $self->add_arg(
      spec => 'runas=s',
      help => "--runas
   run as a different user",
      required => 0,
      hidden => 1,
  );
  $self->add_arg(
      spec => 'shell',
      help => "--shell
   forget what you see",
      required => 0,
      hidden => 1,
  );
  $self->add_arg(
      spec => 'drecksptkdb=s',
      help => "--drecksptkdb
   This parameter must be used instead of --name, because Devel::ptkdb is stealing the latter from the command line",
      aliasfor => "name",
      required => 0,
      hidden => 1,
  );
  $self->add_arg(
      spec => 'tracefile=s',
      help => "--tracefile
   Write debugging-info to this file (if it exists)",
      required => 0,
      hidden => 1,
  );
}

sub add_default_modes {
  my ($self) = @_;
  $self->add_mode(
      internal => 'encode',
      spec => 'encode',
      alias => undef,
      help => 'encode stdin',
      hidden => 1,
  );
  $self->add_mode(
      internal => 'decode',
      spec => 'decode',
      alias => undef,
      help => 'decode stdin or --name',
      hidden => 1,
  );
}

sub add_modes {
  my ($self, $modes) = @_;
  my $modestring = "";
  my @modes = @{$modes};
  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";
  $Monitoring::GLPlugin::plugin->{modestring} = $modestring;
}

sub add_arg {
  my ($self, %args) = @_;
  if ($args{help} =~ /^--mode/) {
    $args{help} .= "\n".$Monitoring::GLPlugin::plugin->{modestring};
  }
  $Monitoring::GLPlugin::plugin->{opts}->add_arg(%args);
}

sub mod_arg {
  my ($self, @arg) = @_;
  $Monitoring::GLPlugin::plugin->{opts}->mod_arg(@arg);
}

sub add_mode {
  my ($self, %args) = @_;
  push(@{$Monitoring::GLPlugin::plugin->{modes}}, \%args);
  my $longest = length ((reverse sort {length $a <=> length $b} map { $_->{spec} } @{$Monitoring::GLPlugin::plugin->{modes}})[0]);
  my $format = "       %-".
      (length ((reverse sort {length $a <=> length $b} map { $_->{spec} } @{$Monitoring::GLPlugin::plugin->{modes}})[0])).
      "s\t(%s)\n";
  $Monitoring::GLPlugin::plugin->{modestring} = "";
  foreach (@{$Monitoring::GLPlugin::plugin->{modes}}) {
    $Monitoring::GLPlugin::plugin->{modestring} .= sprintf $format, $_->{spec}, $_->{help};
  }
  $Monitoring::GLPlugin::plugin->{modestring} .= "\n";
}

sub validate_args {
  my ($self) = @_;
  if ($self->opts->mode =~ /^my-([^\-.]+)/) {
    my $param = $self->opts->mode;
    $param =~ s/\-/::/g;
    $self->add_mode(
        internal => $param,
        spec => $self->opts->mode,
        alias => undef,
        help => 'my extension',
    );
  } elsif ($self->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 ($self->opts->mode eq 'decode') {
    if (! -t STDIN) {
      my $input = <>;
      chomp $input;
      $input =~ s/%([A-Za-z0-9]{2})/chr(hex($1))/seg;
      printf "%s\n", $input;
      exit OK;
    } else {
      if ($self->opts->name) {
        my $input = $self->opts->name;
        $input =~ s/%([A-Za-z0-9]{2})/chr(hex($1))/seg;
        printf "%s\n", $input;
        exit OK;
      } else {
        printf "i can't find your encoded statement. use --name or pipe it in my stdin\n";
        exit UNKNOWN;
      }
    }
  } elsif ((! grep { $self->opts->mode eq $_ } map { $_->{spec} } @{$Monitoring::GLPlugin::plugin->{modes}}) &&
      (! grep { $self->opts->mode eq $_ } map { defined $_->{alias} ? @{$_->{alias}} : () } @{$Monitoring::GLPlugin::plugin->{modes}})) {
    printf "UNKNOWN - mode %s\n", $self->opts->mode;
    $self->opts->print_help();
    exit 3;
  }
  if ($self->opts->name && $self->opts->name =~ /(%22)|(%27)/) {
    my $name = $self->opts->name;
    $name =~ s/\%([A-Fa-f0-9]{2})/pack('C', hex($1))/seg;
    $self->override_opt('name', $name);
  }
  $Monitoring::GLPlugin::mode = (
      map { $_->{internal} }
      grep {
         ($self->opts->mode eq $_->{spec}) ||
         ( defined $_->{alias} && grep { $self->opts->mode eq $_ } @{$_->{alias}})
      } @{$Monitoring::GLPlugin::plugin->{modes}}
  )[0];
  if ($self->opts->multiline) {
    $ENV{NRPE_MULTILINESUPPORT} = 1;
  } else {
    $ENV{NRPE_MULTILINESUPPORT} = 0;
  }
  if ($self->opts->can("statefilesdir") && ! $self->opts->statefilesdir) {
    if ($^O =~ /MSWin/) {
      if (defined $ENV{TEMP}) {
        $self->override_opt('statefilesdir', $ENV{TEMP}."/".$Monitoring::GLPlugin::plugin->{name});
      } elsif (defined $ENV{TMP}) {
        $self->override_opt('statefilesdir', $ENV{TMP}."/".$Monitoring::GLPlugin::plugin->{name});
      } elsif (defined $ENV{windir}) {
        $self->override_opt('statefilesdir', File::Spec->catfile($ENV{windir}, 'Temp')."/".$Monitoring::GLPlugin::plugin->{name});
      } else {
        $self->override_opt('statefilesdir', "C:/".$Monitoring::GLPlugin::plugin->{name});
      }
    } elsif (exists $ENV{OMD_ROOT}) {
      $self->override_opt('statefilesdir', $ENV{OMD_ROOT}."/var/tmp/".$Monitoring::GLPlugin::plugin->{name});
    } else {
      $self->override_opt('statefilesdir', "/var/tmp/".$Monitoring::GLPlugin::plugin->{name});
    }
  }
  $Monitoring::GLPlugin::plugin->{statefilesdir} = $self->opts->statefilesdir
      if $self->opts->can("statefilesdir");
  if ($self->opts->can("warningx") && $self->opts->warningx) {
    foreach my $key (keys %{$self->opts->warningx}) {
      $self->set_thresholds(metric => $key,
          warning => $self->opts->warningx->{$key});
    }
  }
  if ($self->opts->can("criticalx") && $self->opts->criticalx) {
    foreach my $key (keys %{$self->opts->criticalx}) {
      $self->set_thresholds(metric => $key,
          critical => $self->opts->criticalx->{$key});
    }
  }
  $self->set_timeout_alarm() if ! $SIG{'ALRM'};
}

sub set_timeout_alarm {
  my ($self, $timeout, $handler) = @_;
  $timeout ||= $self->opts->timeout;
  $handler ||= sub {
    $self->nagios_exit(UNKNOWN,
        sprintf("%s timed out after %d seconds\n",
            $Monitoring::GLPlugin::plugin->{name}, $self->opts->timeout)
    );
  };
  use POSIX ':signal_h';
  if ($^O =~ /MSWin/) {
    local $SIG{'ALRM'} = $handler;
  } else {
    my $mask = POSIX::SigSet->new( SIGALRM );
    my $action = POSIX::SigAction->new(
        $handler, $mask
    );   
    my $oldaction = POSIX::SigAction->new();
    sigaction(SIGALRM ,$action ,$oldaction );
  }    
  alarm(int($timeout)); # 1 second before the global unknown timeout
}

#########################################################
# global helpers
#
sub set_variable {
  my ($self, $key, $value) = @_;
  $Monitoring::GLPlugin::variables->{$key} = $value;
}

sub get_variable {
  my ($self, $key, $fallback) = @_;
  return exists $Monitoring::GLPlugin::variables->{$key} ?
      $Monitoring::GLPlugin::variables->{$key} : $fallback;
}

sub debug {
  my ($self, $format, @message) = @_;
  if ($self->get_variable("verbose") &&
      $self->get_variable("verbose") > $self->get_variable("verbosity", 10)) {
    printf("%s: ", scalar localtime);
    printf($format, @message);
    printf "\n";
  }
  if ($Monitoring::GLPlugin::tracefile) {
    my $logfh = IO::File->new();
    $logfh->autoflush(1);
    if ($logfh->open($Monitoring::GLPlugin::tracefile, "a")) {
      $logfh->printf("%s: ", scalar localtime);
      $logfh->printf($format, @message);
      $logfh->printf("\n");
      $logfh->close();
    }
  }
}

sub filter_namex {
  my ($self, $opt, $name) = @_;
  if ($opt) {
    if ($self->opts->regexp) {
      if ($name =~ /$opt/i) {
        return 1;
      }
    } else {
      if (lc $opt eq lc $name) {
        return 1;
      }
    }
  } else {
    return 1;
  }
  return 0;
}

sub filter_name {
  my ($self, $name) = @_;
  return $self->filter_namex($self->opts->name, $name);
}

sub filter_name2 {
  my ($self, $name) = @_;
  return $self->filter_namex($self->opts->name2, $name);
}

sub filter_name3 {
  my ($self, $name) = @_;
  return $self->filter_namex($self->opts->name3, $name);
}

sub version_is_minimum {
  my ($self, $version) = @_;
  my $installed_version;
  my $newer = 1;
  if ($self->get_variable("version")) {
    $installed_version = $self->get_variable("version");
  } elsif (exists $self->{version}) {
    $installed_version = $self->{version};
  } else {
    return 0;
  }
  my @v1 = map { $_ eq "x" ? 0 : $_ } split(/\./, $version);
  my @v2 = split(/\./, $installed_version);
  if (scalar(@v1) > scalar(@v2)) {
    push(@v2, (0) x (scalar(@v1) - scalar(@v2)));
  } elsif (scalar(@v2) > scalar(@v1)) {
    push(@v1, (0) x (scalar(@v2) - scalar(@v1)));
  }
  foreach my $pos (0..$#v1) {
    if ($v2[$pos] > $v1[$pos]) {
      $newer = 1;
      last;
    } elsif ($v2[$pos] < $v1[$pos]) {
      $newer = 0;
      last;
    }
  }
  return $newer;
}

sub accentfree {
  my ($self, $text) = @_;
  # thanks mycoyne who posted this accent-remove-algorithm
  # http://www.experts-exchange.com/Programming/Languages/Scripting/Perl/Q_23275533.html#a21234612
  my @transformed;
  my %replace = (
    '9a' => 's', '9c' => 'oe', '9e' => 'z', '9f' => 'Y', 'c0' => 'A', 'c1' => 'A',
    'c2' => 'A', 'c3' => 'A', 'c4' => 'A', 'c5' => 'A', 'c6' => 'AE', 'c7' => 'C',
    'c8' => 'E', 'c9' => 'E', 'ca' => 'E', 'cb' => 'E', 'cc' => 'I', 'cd' => 'I',
    'ce' => 'I', 'cf' => 'I', 'd0' => 'D', 'd1' => 'N', 'd2' => 'O', 'd3' => 'O',
    'd4' => 'O', 'd5' => 'O', 'd6' => 'O', 'd8' => 'O', 'd9' => 'U', 'da' => 'U',
    'db' => 'U', 'dc' => 'U', 'dd' => 'Y', 'e0' => 'a', 'e1' => 'a', 'e2' => 'a',
    'e3' => 'a', 'e4' => 'a', 'e5' => 'a', 'e6' => 'ae', 'e7' => 'c', 'e8' => 'e',
    'e9' => 'e', 'ea' => 'e', 'eb' => 'e', 'ec' => 'i', 'ed' => 'i', 'ee' => 'i',
    'ef' => 'i', 'f0' => 'o', 'f1' => 'n', 'f2' => 'o', 'f3' => 'o', 'f4' => 'o',
    'f5' => 'o', 'f6' => 'o', 'f8' => 'o', 'f9' => 'u', 'fa' => 'u', 'fb' => 'u',
    'fc' => 'u', 'fd' => 'y', 'ff' => 'y',
    '8a' => 'S', '8c' => 'CE', '9a' => 's', '9c' => 'oe', '9f' => 'Y', 'a2' => 'o', 'aa' => 'a',
    'b2' => '2', 'b3' => '3', 'b9' => '1', 'bc' => '1/4', 'bd' => '1/2', 'be' => '3/4',
    'c0' => 'A', 'c1' => 'A', 'c2' => 'A', 'c3' => 'A', 'c4' => 'A', 'c5' => 'A', 'c6' => 'AE',
    'c7' => 'C', 'c8' => 'E', 'c9' => 'E', 'ca' => 'E', 'cb' => 'E',
    'cc' => 'I', 'cd' => 'I', 'ce' => 'I', 'cf' => 'I', 'd0' => 'D', 'd1' => 'N',
    'd2' => 'O', 'd3' => 'O', 'd4' => 'O', 'd5' => 'O', 'd6' => 'O',
    'd8' => 'O', 'd9' => 'U', 'da' => 'U', 'db' => 'U', 'dc' => 'U', 'dd' => 'Y',
    'df' => 'ss', 'e0' => 'a', 'e1' => 'a', 'e2' => 'a', 'e3' => 'a', 'e4' => 'a', 'e5' => 'a',
    'e6' => 'ae', 'e7' => 'c', 'e8' => 'e', 'e9' => 'e', 'ea' => 'e', 'eb' => 'e',
    'ec' => 'i', 'ed' => 'i', 'ee' => 'i', 'ef' => 'i', 'f1' => 'n',
    'f2' => 'o', 'f3' => 'o', 'f4' => 'o', 'f5' => 'o', 'f6' => 'o', 'f8' => 'o',
    'f9' => 'u', 'fa' => 'u', 'fb' => 'u', 'fc' => 'u', 'fd' => 'y', 'ff' => 'yy',
  );
  my @letters = split //, $text;;
  for (my $i = 0; $i <= $#letters; $i++) {
    my $hex = sprintf "%x", ord($letters[$i]);
    $letters[$i] = $replace{$hex} if (exists $replace{$hex});
  }
  push @transformed, @letters;
  $text = join '', @transformed;
  $text =~ s/[[:^ascii:]]//g;
  return $text;
}

sub dump {
  my ($self, $indent) = @_;
  $indent = $indent ? " " x $indent : "";
  if ($self->can("internal_name")) {
    printf "%s[%s]\n", $indent, $self->internal_name();
  } else {
    my $class = ref($self);
    $class =~ s/^.*:://;
    printf "%s[%s]\n", $indent, uc $class;
  }
  foreach (grep !/^(info|trace|warning|critical|blacklisted|extendedinfo|flat_indices|indices)$/, sort keys %{$self}) {
    printf "%s%s: %s\n", $indent, $_, $self->{$_} if defined $self->{$_} && ref($self->{$_}) ne "ARRAY";
  }
  if ($self->{info}) {
    printf "%sinfo: %s\n", $indent, $self->{info};
  }
  foreach (grep !/^(info|trace|warning|critical|blacklisted|extendedinfo|flat_indices|indices)$/, sort keys %{$self}) {
    if (defined $self->{$_} && ref($self->{$_}) eq "ARRAY") {
      my $have_flat_indices = 1;
      foreach my $obj (@{$self->{$_}}) {
        $have_flat_indices = 0 if (ref($obj) ne "HASH" || ! exists $obj->{flat_indices});
      }
      if ($have_flat_indices) {
        foreach my $obj (sort {
            join('', map { sprintf("%30d",$_) } split( /\./, $a->{flat_indices})) cmp
            join('', map { sprintf("%30d",$_) } split( /\./, $b->{flat_indices}))
        } @{$self->{$_}}) {
          $obj->dump();
        }
      } else {
        foreach my $obj (@{$self->{$_}}) {
          $obj->dump() if UNIVERSAL::can($obj, "isa") && $obj->can("dump");
        }
      }
    } elsif (defined $self->{$_} && ref($self->{$_}) =~ /^Classes::/) {
      $self->{$_}->dump(2) if UNIVERSAL::can($self->{$_}, "isa") && $self->{$_}->can("dump");
    }
  }
  printf "\n";
}

sub table_ascii {
  my ($self, $table, $titles) = @_;
  my $text = "";
  my $column_length = {};
  my $column = 0;
  foreach (@{$titles}) {
    $column_length->{$column++} = length($_);
  }
  foreach my $tr (@{$table}) {
    @{$tr} = map { ref($_) eq "ARRAY" ? $_->[0] : $_; } @{$tr};
    $column = 0;
    foreach my $td (@{$tr}) {
      if (length($td) > $column_length->{$column}) {
        $column_length->{$column} = length($td);
      }
      $column++;
    }
  }
  $column = 0;
  foreach (@{$titles}) {
    $column_length->{$column} = "%".($column_length->{$column} + 3)."s";
    $column++;
  }
  $column = 0;
  foreach (@{$titles}) {
    $text .= sprintf $column_length->{$column++}, $_;
  }
  $text .= "\n";
  foreach my $tr (@{$table}) {
    $column = 0;
    foreach my $td (@{$tr}) {
      $text .= sprintf $column_length->{$column++}, $td;
    }
    $text .= "\n";
  }
  return $text;
}

sub table_html {
  my ($self, $table, $titles) = @_;
  my $text = "";
  $text .= "<table style=\"border-collapse:collapse; border: 1px solid black;\">";
  $text .= "<tr>";
  foreach (@{$titles}) {
    $text .= sprintf "<th style=\"text-align: left; padding-left: 4px; padding-right: 6px;\">%s</th>", $_;
  }
  $text .= "</tr>";
  foreach my $tr (@{$table}) {
    $text .= "<tr>";
    foreach my $td (@{$tr}) {
      my $class = "statusOK";
      if (ref($td) eq "ARRAY") {
        $class = {
          0 => "statusOK",
          1 => "statusWARNING",
          2 => "statusCRITICAL",
          3 => "statusUNKNOWN",
        }->{$td->[1]};
        $td = $td->[0];
      }
      $text .= sprintf "<td style=\"text-align: left; padding-left: 4px; padding-right: 6px;\" class=\"%s\">%s</td>", $class, $td;
    }
    $text .= "</tr>";
  }
  $text .= "</table>";
  return $text;
}

sub load_my_extension {
  my ($self) = @_;
  if ($self->opts->mode =~ /^my-([^-.]+)/) {
    my $class = $1;
    my $loaderror = undef;
    substr($class, 0, 1) = uc substr($class, 0, 1);
    if (! $self->opts->get("with-mymodules-dyn-dir")) {
      $self->override_opt("with-mymodules-dyn-dir", "");
    }
    my $plugin_name = $Monitoring::GLPlugin::pluginname;
    $plugin_name =~ /check_(.*?)_health/;
    my $deprecated_class = "DBD::".(uc $1)."::Server";
    $plugin_name = "Check".uc(substr($1, 0, 1)).substr($1, 1)."Health";
    foreach my $libpath (split(":", $self->opts->get("with-mymodules-dyn-dir"))) {
      foreach my $extmod (glob $libpath."/".$plugin_name."*.pm") {
        my $stderrvar;
        *SAVEERR = *STDERR;
        open OUT ,'>',\$stderrvar;
        *STDERR = *OUT;
        eval {
          $self->debug(sprintf "loading module %s", $extmod);
          require $extmod;
        };
        *STDERR = *SAVEERR;
        if ($@) {
          $loaderror = $extmod;
          $self->debug(sprintf "failed loading module %s: %s", $extmod, $@);
        }
      }
    }
    my $original_class = ref($self);
    my $original_init = $self->can("init");
    $self->compatibility_class() if $self->can('compatibility_class');
    bless $self, "My$class";
    $self->compatibility_methods() if $self->can('compatibility_methods') &&
        $self->isa($deprecated_class);
    if ($self->isa("Monitoring::GLPlugin")) {
      my $new_init = $self->can("init");
      if ($new_init == $original_init) {
          $self->add_unknown(
              sprintf "Class %s needs an init() method", ref($self));
      } else {
        # now go back to check_*_health.pl where init() will be called
      }
    } else {
      bless $self, $original_class;
      $self->add_unknown(
          sprintf "Class %s is not a subclass of Monitoring::GLPlugin%s",
              "My$class",
              $loaderror ? sprintf " (syntax error in %s?)", $loaderror : "" );
      my ($code, $message) = $self->check_messages(join => ', ', join_all => ', ');
      $self->nagios_exit($code, $message);
    }
  }
}

sub number_of_bits {
  my ($self, $unit) = @_;
  # https://en.wikipedia.org/wiki/Data_rate_units
  my $bits = {
    'bit' => 1,			# Bit per second
    'B' => 8,			# Byte per second, 8 bits per second
    'kbit' => 1000,		# Kilobit per second, 1,000 bits per second
    'kb' => 1000,		# Kilobit per second, 1,000 bits per second
    'Kibit' => 1024,		# Kibibit per second, 1,024 bits per second
    'kB' => 8000,		# Kilobyte per second, 8,000 bits per second
    'KiB' => 8192,		# Kibibyte per second, 1,024 bytes per second
    'Mbit' => 1000000,		# Megabit per second, 1,000,000 bits per second
    'Mb' => 1000000,		# Megabit per second, 1,000,000 bits per second
    'Mibit' => 1048576,		# Mebibit per second, 1,024 kibibits per second
    'MB' => 8000000,		# Megabyte per second, 1,000 kilobytes per second
    'MiB' => 8388608,		# Mebibyte per second, 1,024 kibibytes per second
    'Gbit' => 1000000000,	# Gigabit per second, 1,000 megabits per second
    'Gb' => 1000000000,		# Gigabit per second, 1,000 megabits per second
    'Gibit' => 1073741824,	# Gibibit per second, 1,024 mebibits per second
    'GB' => 8000000000,		# Gigabyte per second, 1,000 megabytes per second
    'GiB' => 8589934592,	# Gibibyte per second, 8192 mebibits per second
    'Tbit' => 1000000000000,	# Terabit per second, 1,000 gigabits per second
    'Tb' => 1000000000000,	# Terabit per second, 1,000 gigabits per second
    'Tibit' => 1099511627776,	# Tebibit per second, 1,024 gibibits per second
    'TB' => 8000000000000,	# Terabyte per second, 1,000 gigabytes per second
    # eigene kreationen
    'Bits' => 1,
    'Bit' => 1,			# Bit per second
    'KB' => 1024,		# Kilobyte (like disk kilobyte)
    'KBi' => 1024,		# -"-
    'MBi' => 1024 * 1024,	# Megabyte (like disk megabyte)
    'GBi' => 1024 * 1024 * 1024, # Gigybate (like disk gigybyte)
  };
  if (exists $bits->{$unit}) {
    return $bits->{$unit};
  } else {
    return 0;
  }
}


#########################################################
# runtime methods
#
sub mode : lvalue {
  my ($self) = @_;
  $Monitoring::GLPlugin::mode;
}

sub statefilesdir {
  my ($self) = @_;
  return $Monitoring::GLPlugin::plugin->{statefilesdir};
}

sub opts { # die beiden _nicht_ in AUTOLOAD schieben, das kracht!
  my ($self) = @_;
  return $Monitoring::GLPlugin::plugin->opts();
}

sub getopts {
  my ($self, $envparams) = @_;
  $envparams ||= [];
  my $needs_restart = 0;
  my @restart_opts = ();
  $Monitoring::GLPlugin::plugin->getopts();
  # es kann sein, dass beim aufraeumen zum schluss als erstes objekt
  # das $Monitoring::GLPlugin::plugin geloescht wird. in anderen destruktoren
  # (insb. fuer dbi disconnect) steht dann $self->opts->verbose
  # nicht mehr zur verfuegung bzw. $Monitoring::GLPlugin::plugin->opts ist undef.
  $self->set_variable("verbose", $self->opts->verbose);
  $Monitoring::GLPlugin::tracefile = $self->opts->tracefile ?
      $self->opts->tracefile :
      $self->system_tmpdir()."/".$Monitoring::GLPlugin::pluginname.".trace";
  if (! -f $Monitoring::GLPlugin::tracefile) {
    $Monitoring::GLPlugin::tracefile = undef;
  }
  #
  # die gueltigkeit von modes wird bereits hier geprueft und nicht danach
  # in validate_args. (zwischen getopts und validate_args wird
  # normalerweise classify aufgerufen, welches bereits eine verbindung
  # zum endgeraet herstellt. bei falschem mode waere das eine verschwendung
  # bzw. durch den exit3 ein evt. unsauberes beenden der verbindung.
  if ((! grep { $self->opts->mode eq $_ } map { $_->{spec} } @{$Monitoring::GLPlugin::plugin->{modes}}) &&
      (! grep { $self->opts->mode eq $_ } map { defined $_->{alias} ? @{$_->{alias}} : () } @{$Monitoring::GLPlugin::plugin->{modes}})) {
    if ($self->opts->mode !~ /^my-/) {
      printf "UNKNOWN - mode %s\n", $self->opts->mode;
      $self->opts->print_help();
      exit 3;
    }
  }
  if ($self->opts->environment) {
    # wenn die gewuenschten Environmentvariablen sich von den derzeit
    # gesetzten unterscheiden, dann restart. Denn $ENV aendert
    # _nicht_ das Environment des laufenden Prozesses. 
    # $ENV{ZEUGS} = 1 bedeutet lediglich, dass $ENV{ZEUGS} bei weiterer
    # Verwendung 1 ist, bedeutet aber _nicht_, dass diese Variable 
    # im Environment des laufenden Prozesses existiert.
    foreach (keys %{$self->opts->environment}) {
      if ((! $ENV{$_}) || ($ENV{$_} ne $self->opts->environment->{$_})) {
        $needs_restart = 1;
        $ENV{$_} = $self->opts->environment->{$_};
        $self->debug(sprintf "new %s=%s forces restart\n", $_, $ENV{$_});
      }
    }
  }
  if ($self->opts->runas) {
    # exec sudo $0 ... und dann ohne --runas
    $needs_restart = 1;
    # wenn wir environmentvariablen haben, die laut survive_sudo_env als
    # wichtig erachtet werden, dann muessen wir die ueber einen moeglichen
    # sudo-aufruf rueberretten, also in zusaetzliche --environment umwandenln.
    # sudo putzt das Environment naemlich aus.
    foreach my $survive_env (@{$Monitoring::GLPlugin::survive_sudo_env}) {
      if ($ENV{$survive_env} && ! scalar(grep { /^$survive_env=/ }
          keys %{$self->opts->environment})) {
        $self->opts->environment->{$survive_env} = $ENV{$survive_env};
        printf STDERR "add important --environment %s=%s\n",
            $survive_env, $ENV{$survive_env} if $self->opts->verbose >= 2;
        push(@restart_opts, '--environment');
        push(@restart_opts, sprintf '%s=%s',
            $survive_env, $ENV{$survive_env});
      }
    }
  }
  if ($needs_restart) {
    foreach my $option (keys %{$self->opts->all_my_opts}) {
      # der fliegt raus, sonst gehts gleich wieder in needs_restart rein
      next if $option eq "runas";
      foreach my $spec (map { $_->{spec} } @{$Monitoring::GLPlugin::plugin->opts->{_args}}) {
        if ($spec =~ /^([\-\w]+)[\?\+:\|\w+]*=(.*)/) {
          if ($1 eq $option && $2 =~ /s%/) {
            foreach (keys %{$self->opts->$option()}) {
              push(@restart_opts, sprintf "--%s", $option);
              push(@restart_opts, sprintf "%s=%s", $_, $self->opts->$option()->{$_});
            }
          } elsif ($1 eq $option) {
            push(@restart_opts, sprintf "--%s", $option);
            push(@restart_opts, sprintf "%s", $self->opts->$option());
          }
        } elsif ($spec eq $option) {
          push(@restart_opts, sprintf "--%s", $option);
        }
      }
    }
    if ($self->opts->runas && ($> == 0)) {
      # Ja, es gibt so Narrische, die gehen mit check_by_ssh als root
      # auf Datenbankmaschinen drauf und lassen dann dort check_oracle_health
      # laufen. Damit OPS$-Anmeldung dann funktioniert, wird mit --runas
      # auf eine andere Kennung umgeschwenkt. Diese Kennung gleich fuer
      # ssh zu verwenden geht aus Sicherheitsgruenden nicht. Narrische halt.
      exec "su", "-c", sprintf("%s %s", $0, join(" ", @restart_opts)), "-", $self->opts->runas;
    } elsif ($self->opts->runas) {
      exec "sudo", "-S", "-u", $self->opts->runas, $0, @restart_opts;
    } else {
      exec $0, @restart_opts;
      # dadurch werden SHLIB oder LD_LIBRARY_PATH sauber gesetzt, damit beim
      # erneuten Start libclntsh.so etc. gefunden werden.
    }
    exit;
  }
  if ($self->opts->shell) {
    # So komme ich bei den Narrischen zu einer root-Shell.
    system("/bin/sh");
  }
}


sub add_ok {
  my ($self, $message) = @_;
  $message ||= $self->{info};
  $self->add_message(OK, $message);
}

sub add_warning {
  my ($self, $message) = @_;
  $message ||= $self->{info};
  $self->add_message(WARNING, $message);
}

sub add_critical {
  my ($self, $message) = @_;
  $message ||= $self->{info};
  $self->add_message(CRITICAL, $message);
}

sub add_unknown {
  my ($self, $message) = @_;
  $message ||= $self->{info};
  $self->add_message(UNKNOWN, $message);
}

sub add_ok_mitigation {
  my ($self, $message) = @_;
  if (defined $self->opts->mitigation()) {
    $self->add_message($self->opts->mitigation(), $message);
  } else {
    $self->add_ok($message);
  }
}

sub add_warning_mitigation {
  my ($self, $message) = @_;
  if (defined $self->opts->mitigation()) {
    $self->add_message($self->opts->mitigation(), $message);
  } else {
    $self->add_warning($message);
  }
}

sub add_critical_mitigation {
  my ($self, $message) = @_;
  if (defined $self->opts->mitigation()) {
    $self->add_message($self->opts->mitigation(), $message);
  } else {
    $self->add_critical($message);
  }
}

sub add_unknown_mitigation {
  my ($self, $message) = @_;
  if (defined $self->opts->mitigation()) {
    $self->add_message($self->opts->mitigation(), $message);
  } else {
    $self->add_unknown($message);
  }
}

sub add_message {
  my ($self, $level, $message) = @_;
  $message ||= $self->{info};
  $Monitoring::GLPlugin::plugin->add_message($level, $message)
      unless $self->is_blacklisted();
  if (exists $self->{failed}) {
    if ($level == UNKNOWN && $self->{failed} == OK) {
      $self->{failed} = $level;
    } elsif ($level > $self->{failed}) {
      $self->{failed} = $level;
    }
  }
}

sub clear_ok {
  my ($self) = @_;
  $self->clear_messages(OK);
}

sub clear_warning {
  my ($self) = @_;
  $self->clear_messages(WARNING);
}

sub clear_critical {
  my ($self) = @_;
  $self->clear_messages(CRITICAL);
}

sub clear_unknown {
  my ($self) = @_;
  $self->clear_messages(UNKNOWN);
}

sub clear_all { # deprecated, use clear_messages
  my ($self) = @_;
  $self->clear_ok();
  $self->clear_warning();
  $self->clear_critical();
  $self->clear_unknown();
}

sub set_level {
  my ($self, $code) = @_;
  $code = (qw(ok warning critical unknown))[$code] if $code =~ /^\d+$/;
  $code = lc $code;
  if (! exists $self->{tmp_level}) {
    $self->{tmp_level} = {
      ok => 0,
      warning => 0,
      critical => 0,
      unknown => 0,
    };
  }
  $self->{tmp_level}->{$code}++;
}

sub get_level {
  my ($self) = @_;
  return OK if ! exists $self->{tmp_level};
  my $code = OK;
  return CRITICAL if $self->{tmp_level}->{critical};
  return WARNING  if $self->{tmp_level}->{warning};
  return UNKNOWN  if $self->{tmp_level}->{unknown};
  return $code;
}

sub worst_level {
  my ($self, @levels) = @_;
  my $level = 0;
  foreach (@levels) {
    if ($_ == 2) {
      $level = 2;
    } elsif ($_ == 1) {
      if ($level == 0 || $level == 3) {
        $level = 1;
      }
    } elsif ($_ == 3) {
      if ($level == 0) {
        $level = 3;
      }
    }
  }
  return $level;
}

#########################################################
# blacklisting
#
sub blacklist {
  my ($self) = @_;
  $self->{blacklisted} = 1;
}

sub add_blacklist {
  my ($self, $list) = @_;
  $Monitoring::GLPlugin::blacklist = join('/',
      (split('/', $self->opts->blacklist), $list));
}

sub is_blacklisted {
  my ($self) = @_;
  if (! $self->opts->can("blacklist")) {
    return 0;
  }
  if (! exists $self->{blacklisted}) {
    $self->{blacklisted} = 0;
  }
  if (exists $self->{blacklisted} && $self->{blacklisted}) {
    return $self->{blacklisted};
  }
  # FAN:459,203/TEMP:102229/ENVSUBSYSTEM
  # FAN_459,FAN_203,TEMP_102229,ENVSUBSYSTEM
  # ALERT:(The Storage Center is not able to access Tiebreaker)/TEMP:102229
  if ($self->opts->blacklist =~ /_/) {
    foreach my $bl_item (split(/,/, $self->opts->blacklist)) {
      if ($bl_item eq $self->internal_name()) {
        $self->{blacklisted} = 1;
      }
    }
  } else {
    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."_".$bl_name eq $self->internal_name()) {
            $self->{blacklisted} = 1;
          }
        }
      } elsif ($bl_items =~ /^(\w+):\((.*)\)$/ and $self->can("internal_content")) {
        my $bl_type = $1;
        my $bl_pattern = qr/$2/;
        if ($self->internal_name() =~ /^${bl_type}_/) {
          if ($self->internal_content() =~ /$bl_pattern/) {
            $self->{blacklisted} = 1;
          }
        }
      } elsif ($bl_items =~ /^(\w+)$/) {
        if ($bl_items eq $self->internal_name()) {
          $self->{blacklisted} = 1;
        }
      }
    }
  }
  return $self->{blacklisted};
}

#########################################################
# additional info
#
sub add_info {
  my ($self, $info) = @_;
  $info = $self->is_blacklisted() ? $info.' (blacklisted)' : $info;
  $self->{info} = $info;
  push(@{$Monitoring::GLPlugin::info}, $info);
}

sub annotate_info {
  my ($self, $annotation) = @_;
  my $lastinfo = pop(@{$Monitoring::GLPlugin::info});
  $lastinfo .= sprintf ' (%s)', $annotation;
  $self->{info} = $lastinfo;
  push(@{$Monitoring::GLPlugin::info}, $lastinfo);
}

sub add_extendedinfo {  # deprecated
  my ($self, $info) = @_;
  $self->{extendedinfo} = $info;
  return if ! $self->opts->extendedinfo;
  push(@{$Monitoring::GLPlugin::extendedinfo}, $info);
}

sub get_info {
  my ($self, $separator) = @_;
  $separator ||= ' ';
  return join($separator , @{$Monitoring::GLPlugin::info});
}

sub get_last_info {
  my ($self) = @_;
  return pop(@{$Monitoring::GLPlugin::info});
}

sub get_extendedinfo {
  my ($self, $separator) = @_;
  $separator ||= ' ';
  return join($separator, @{$Monitoring::GLPlugin::extendedinfo});
}

sub add_summary {  # deprecated
  my ($self, $summary) = @_;
  push(@{$Monitoring::GLPlugin::summary}, $summary);
}

sub get_summary {
  my ($self) = @_;
  return join(', ', @{$Monitoring::GLPlugin::summary});
}

#########################################################
# persistency
#
sub valdiff {
  my ($self, $pparams, @keys) = @_;
  my %params = %{$pparams};
  my $now = time;
  my $newest_history_set = {};
  $params{freeze} = 0 if ! $params{freeze};
  my $mode = "normal";
  if ($self->opts->lookback && $self->opts->lookback == 99999 && $params{freeze} == 0) {
    $mode = "lookback_freeze_chill";
  } elsif ($self->opts->lookback && $self->opts->lookback == 99999 && $params{freeze} == 1) {
    $mode = "lookback_freeze_shockfrost";
  } elsif ($self->opts->lookback && $self->opts->lookback == 99999 && $params{freeze} == 2) {
    $mode = "lookback_freeze_defrost";
  } elsif ($self->opts->lookback) {
    $mode = "lookback";
  }
  # lookback=99999, freeze=0(default)
  #  nimm den letzten lauf und schreib ihn nach {cold}
  #  vergleich dann
  #    wenn es frozen gibt, vergleich frozen und den letzten lauf
  #    sonst den letzten lauf und den aktuellen lauf
  # lookback=99999, freeze=1
  #  wird dann aufgerufen,wenn nach dem freeze=0 ein problem festgestellt wurde
  #     (also als 2.valdiff hinterher)
  #  schreib cold nach frozen
  # lookback=99999, freeze=2
  #  wird dann aufgerufen,wenn nach dem freeze=0 wieder alles ok ist
  #     (also als 2.valdiff hinterher)
  #  loescht frozen
  #
  my $last_values = $self->load_state(%params) || eval {
    my $empty_events = {};
    foreach (@keys) {
      if (ref($self->{$_}) eq "ARRAY") {
        $empty_events->{$_} = [];
      } else {
        $empty_events->{$_} = 0;
      }
    }
    $empty_events->{timestamp} = 0;
    if ($mode eq "lookback") {
      $empty_events->{lookback_history} = {};
    } elsif ($mode eq "lookback_freeze_chill") {
      $empty_events->{cold} = {};
      $empty_events->{frozen} = {};
    }
    $empty_events;
  };
  $self->{'delta_timestamp'} = $now - $last_values->{timestamp};
  foreach (@keys) {
    if ($mode eq "lookback_freeze_chill") {
      # die werte vom letzten lauf wegsichern.
      # vielleicht gibts gleich einen freeze=1, dann muessen die eingefroren werden
      if (exists $last_values->{$_}) {
        if (ref($self->{$_}) eq "ARRAY") {
          $last_values->{cold}->{$_} = [];
          foreach my $value (@{$last_values->{$_}}) {
            push(@{$last_values->{cold}->{$_}}, $value);
          }
        } else {
          $last_values->{cold}->{$_} = $last_values->{$_};
        }
      } else {
        if (ref($self->{$_}) eq "ARRAY") {
          $last_values->{cold}->{$_} = [];
        } else {
          $last_values->{cold}->{$_} = 0;
        }
      }
      # es wird so getan, als sei der frozen wert vom letzten lauf
      if (exists $last_values->{frozen}->{$_}) {
        if (ref($self->{$_}) eq "ARRAY") {
          $last_values->{$_} = [];
          foreach my $value (@{$last_values->{frozen}->{$_}}) {
            push(@{$last_values->{$_}}, $value);
          }
        } else {
          $last_values->{$_} = $last_values->{frozen}->{$_};
        }
      }
    } elsif ($mode eq "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}->{$_}}) {
            $newest_history_set->{$_} = $last_values->{lookback_history}->{$_}->{$date};
            $newest_history_set->{timestamp} = $date;
        }
        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;
            $self->{'delta_timestamp'} = $now - $last_values->{timestamp};
            if (ref($last_values->{$_}) eq "ARRAY") {
              $self->debug(sprintf "oldest value of %s within lookback is size %s (age %d)",
                  $_, scalar(@{$last_values->{$_}}), $now - $date);
            } else {
              $self->debug(sprintf "oldest value of %s within lookback is %s (age %d)",
                  $_, $last_values->{$_}, $now - $date);
            }
            last;
          } else {
            $self->debug(sprintf "deprecate %s of age %d", $_, time - $date);
            delete $last_values->{lookback_history}->{$_}->{$date};
          }
        }
      }
    }
    if ($mode eq "normal" || $mode eq "lookback" || $mode eq "lookback_freeze_chill") {
      if (exists $self->{$_} && defined $self->{$_} && $self->{$_} =~ /^\d+\.*\d*$/) {
        # $VAR1 = { 'sysStatTmSleepCycles' => '',
        # no idea why this happens, but we can repair it.
        $last_values->{$_} = $self->{$_} if ! (exists $last_values->{$_} && defined $last_values->{$_} && $last_values->{$_} ne "");
        if ($self->{$_} >= $last_values->{$_}) {
          $self->{'delta_'.$_} = $self->{$_} - $last_values->{$_};
        } elsif ($self->{$_} eq $last_values->{$_}) {
          # dawischt! in einem fall wurde 131071.999023438 >= 131071.999023438 da oben nicht erkannt
          # subtrahieren ging auch daneben, weil ein winziger negativer wert rauskam.
          $self->{'delta_'.$_} = 0;
        } else {
          if ($mode =~ /lookback_freeze/) {
            # hier koennen delta-werte auch negativ sein, wenn z.b. peers verschwinden
            $self->{'delta_'.$_} = $self->{$_} - $last_values->{$_};
          } elsif (exists $params{lastarray}) {
            $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->{$_.'_per_sec'} = $self->{'delta_timestamp'} ?
            $self->{'delta_'.$_} / $self->{'delta_timestamp'} : 0;
      } elsif (ref($self->{$_}) eq "ARRAY") {
        if ((! exists $last_values->{$_} || ! defined $last_values->{$_}) && exists $params{lastarray}) {
          # innerhalb der lookback-zeit wurde nichts in der lookback_history
          # gefunden. allenfalls irgendwas aelteres. normalerweise
          # wuerde jetzt das array als [] initialisiert.
          # d.h. es wuerde ein delta geben, @found s.u.
          # wenn man das nicht will, sondern einfach aktuelles array mit
          # dem array des letzten laufs vergleichen will, setzt man lastarray
          $last_values->{$_} = %{$newest_history_set} ?
              $newest_history_set->{$_} : []
        } elsif ((! exists $last_values->{$_} || ! defined $last_values->{$_}) && ! exists $params{lastarray}) {
          $last_values->{$_} = [] if ! exists $last_values->{$_};
        } elsif (exists $last_values->{$_} && ! defined $last_values->{$_}) {
          # $_ kann es auch ausserhalb des lookback_history-keys als normalen
          # key geben. der zeigt normalerweise auf den entspr. letzten
          # lookback_history eintrag. wurde der wegen ueberalterung abgeschnitten
          # ist der hier auch undef.
          $last_values->{$_} = %{$newest_history_set} ?
              $newest_history_set->{$_} : []
        }
        my %saved = map { $_ => 1 } @{$last_values->{$_}};
        my %current = map { $_ => 1 } @{$self->{$_}};
        my @found = grep(!defined $saved{$_}, @{$self->{$_}});
        my @lost = grep(!defined $current{$_}, @{$last_values->{$_}});
        $self->{'delta_found_'.$_} = \@found;
        $self->{'delta_lost_'.$_} = \@lost;
      } else {
        # nicht ganz sauber, aber das artet aus, wenn man jedem uninitialized hinterherstochert.
        # wem das nicht passt, der kann gerne ein paar tage debugging beauftragen.
        # das kostet aber mehr als drei kugeln eis.
        $last_values->{$_} = 0 if ! (exists $last_values->{$_} && defined $last_values->{$_} && $last_values->{$_} ne "");
        $self->{$_} = 0 if ! (exists $self->{$_} && defined $self->{$_} && $self->{$_} ne "");
        $self->{'delta_'.$_} = 0;
      }
    }
  }
  $params{save} = eval {
    my $empty_events = {};
    foreach (@keys) {
      $empty_events->{$_} = $self->{$_};
      if ($mode =~ /lookback_freeze/) {
        if (exists $last_values->{frozen}->{$_}) {
          if (ref($last_values->{frozen}->{$_}) eq "ARRAY") {
            @{$empty_events->{cold}->{$_}} = @{$last_values->{frozen}->{$_}};
          } else {
            $empty_events->{cold}->{$_} = $last_values->{frozen}->{$_};
          }
        } else {
          if (ref($last_values->{cold}->{$_}) eq "ARRAY") {
            @{$empty_events->{cold}->{$_}} = @{$last_values->{cold}->{$_}};
          } else {
            $empty_events->{cold}->{$_} = $last_values->{cold}->{$_};
          }
        }
        $empty_events->{cold}->{timestamp} = $last_values->{cold}->{timestamp};
      }
      if ($mode eq "lookback_freeze_shockfrost") {
        if (ref($empty_events->{cold}->{$_}) eq "ARRAY") {
          @{$empty_events->{frozen}->{$_}} = @{$empty_events->{cold}->{$_}};
        } else {
          $empty_events->{frozen}->{$_} = $empty_events->{cold}->{$_};
        }
        $empty_events->{frozen}->{timestamp} = $now;
      }
    }
    $empty_events->{timestamp} = $now;
    if ($mode eq "lookback") {
      $empty_events->{lookback_history} = $last_values->{lookback_history};
      foreach (@keys) {
        if (ref($self->{$_}) eq "ARRAY") {
          @{$empty_events->{lookback_history}->{$_}->{$now}} = @{$self->{$_}};
        } else {
          $empty_events->{lookback_history}->{$_}->{$now} = $self->{$_};
        }
      }
    }
    if ($mode eq "lookback_freeze_defrost") {
      delete $empty_events->{freeze};
    }
    $empty_events;
  };
  $self->save_state(%params);
}

sub create_statefilesdir {
  my ($self) = @_;
  if (! -d $self->statefilesdir()) {
    eval {
      use File::Path;
      mkpath $self->statefilesdir();
    };
    if ($@ || ! -w $self->statefilesdir()) {
      $self->add_message(UNKNOWN,
        sprintf "cannot create status dir %s! check your filesystem (permissions/usage/integrity) and disk devices", $self->statefilesdir());
    }
  } elsif (! -w $self->statefilesdir()) {
    $self->add_message(UNKNOWN,
        sprintf "cannot write status dir %s! check your filesystem (permissions/usage/integrity) and disk devices", $self->statefilesdir());
  }
}

sub create_statefile {
  my ($self, %params) = @_;
  my $extension = "";
  $extension .= $params{name} ? '_'.$params{name} : '';
  $extension =~ s/\//_/g;
  $extension =~ s/\(/_/g;
  $extension =~ s/\)/_/g;
  $extension =~ s/\*/_/g;
  $extension =~ s/\s/_/g;
  return sprintf "%s/%s%s", $self->statefilesdir(),
      $self->clean_path($self->mode), $self->clean_path(lc $extension);
}

sub clean_path {
  my ($self, $path) = @_;
  if ($^O =~ /MSWin/) {
    $path =~ s/:/_/g;
  }
  return $path;
}

sub schimpf {
  my ($self) = @_;
  printf "statefilesdir %s is not writable.\nYou didn't run this plugin as root, didn't you?\n", $self->statefilesdir();
}

# $self->protect_value('1.1-flat_index', 'cpu_busy', 'percent');
sub protect_value {
  my ($self, $ident, $key, $validfunc) = @_;
  if (ref($validfunc) ne "CODE" && $validfunc eq "percent") {
    $validfunc = sub {
      my $value = shift;
      return 0 if ! defined $value;
      return 0 if $value !~ /^[-+]?([0-9]+(\.[0-9]+)?|\.[0-9]+)$/;
      return ($value < 0 || $value > 100) ? 0 : 1;
    };
  } elsif (ref($validfunc) ne "CODE" && $validfunc eq "positive") {
    $validfunc = sub {
      my $value = shift;
      return 0 if ! defined $value;
      return 0 if $value !~ /^[-+]?([0-9]+(\.[0-9]+)?|\.[0-9]+)$/;
      return ($value < 0) ? 0 : 1;
    };
  }
  if (&$validfunc($self->{$key})) {
    $self->save_state(name => 'protect_'.$ident.'_'.$key, save => {
        $key => $self->{$key},
        exception => 0,
    });
  } else {
    # if the device gives us an clearly wrong value, simply use the last value.
    my $laststate = $self->load_state(name => 'protect_'.$ident.'_'.$key) || {
        exception => 0,
    };
    $self->debug(sprintf "self->{%s} is %s and invalid for the %dth time",
        $key, defined $self->{$key} ? $self->{$key} : "<undef>",
         $laststate->{exception} + 1);
    if ($laststate->{exception} <= 5) {
      # but only 5 times.
      # if the error persists, somebody has to check the device.
      $self->{$key} = $laststate->{$key};
    }
    $self->save_state(name => 'protect_'.$ident.'_'.$key, save => {
        $key => $laststate->{$key},
        exception => ++$laststate->{exception},
    });
  }
}

sub save_state {
  my ($self, %params) = @_;
  $self->create_statefilesdir();
  my $statefile = $self->create_statefile(%params);
  my $tmpfile = $statefile.$$.rand();
  if ((ref($params{save}) eq "HASH") && exists $params{save}->{timestamp}) {
    $params{save}->{localtime} = scalar localtime $params{save}->{timestamp};
  }
  my $seekfh = IO::File->new();
  if ($seekfh->open($tmpfile, "w")) {
    my $coder = JSON::XS->new->ascii->pretty->allow_nonref;
    my $jsonscalar = $coder->encode($params{save});
    $seekfh->print($jsonscalar);
    # the very time-consuming old way.
    # $seekfh->printf("%s", Data::Dumper::Dumper($params{save}));
    $seekfh->flush();
    $seekfh->close();
    $self->debug(sprintf "saved %s to %s",
        Data::Dumper::Dumper($params{save}), $statefile);
  }
  if (! rename $tmpfile, $statefile) {
    $self->add_message(UNKNOWN,
        sprintf "cannot write status file %s! check your filesystem (permissions/usage/integrity) and disk devices", $statefile);
  }
}

sub load_state {
  my ($self, %params) = @_;
  my $statefile = $self->create_statefile(%params);
  if ( -f $statefile) {
    our $VAR1;
    eval {
      delete $INC{$statefile} if exists $INC{$statefile}; # else unit tests fail
      my $jsonscalar = read_file($statefile);
      my $coder = JSON::XS->new->ascii->pretty->allow_nonref;
      $VAR1 = $coder->decode($jsonscalar);
    };
    if($@) {
      $self->debug(sprintf "json load from %s failed. fallback", $statefile);
      eval {
        require $statefile;
      };
      if($@) {
        printf "FATAL: Could not load old state in perl format!\n";
      }
    }
    $self->debug(sprintf "load %s from %s", Data::Dumper::Dumper($VAR1), $statefile);
    return $VAR1;
  } else {
    return undef;
  }
}

#########################################################
# daemon mode
#
sub check_pidfile {
  my ($self) = @_;
  my $fh = IO::File->new();
  if ($fh->open($self->{pidfile}, "r")) {
    my $pid = $fh->getline();
    $fh->close();
    if (! $pid) {
      $self->debug("Found pidfile %s with no valid pid. Exiting.",
          $self->{pidfile});
      return 0;
    } else {
      $self->debug("Found pidfile %s with pid %d", $self->{pidfile}, $pid);
      kill 0, $pid;
      if ($! == Errno::ESRCH) {
        $self->debug("This pidfile is stale. Writing a new one");
        $self->write_pidfile();
        return 1;
      } else {
        $self->debug("This pidfile is held by a running process. Exiting");
        return 0;
      }
    }
  } else {
    $self->debug("Found no pidfile. Writing a new one");
    $self->write_pidfile();
    return 1;
  }
}

sub write_pidfile {
  my ($self) = @_;
  if (! -d dirname($self->{pidfile})) {
    eval "require File::Path;";
    if (defined(&File::Path::mkpath)) {
      import File::Path;
      eval { mkpath(dirname($self->{pidfile})); };
    } else {
      my @dirs = ();
      map {
          push @dirs, $_;
          mkdir(join('/', @dirs))
              if join('/', @dirs) && ! -d join('/', @dirs);
      } split(/\//, dirname($self->{pidfile}));
    }
  }
  my $fh = IO::File->new();
  $fh->autoflush(1);
  if ($fh->open($self->{pidfile}, "w")) {
    $fh->printf("%s", $$);
    $fh->close();
  } else {
    $self->debug("Could not write pidfile %s", $self->{pidfile});
    die "pid file could not be written";
  }
}

sub system_vartmpdir {
  my ($self) = @_;
  if ($^O =~ /MSWin/) {
    return $self->system_tmpdir();
  } else {
    return "/var/tmp/".$Monitoring::GLPlugin::pluginname;
  }
}

sub system_tmpdir {
  my ($self) = @_;
  if ($^O =~ /MSWin/) {
    return $ENV{TEMP} if defined $ENV{TEMP};
    return $ENV{TMP} if defined $ENV{TMP};
    return File::Spec->catfile($ENV{windir}, 'Temp')
        if defined $ENV{windir};
    return 'C:\Temp';
  } else {
    return "/tmp";
  }
}

sub convert_scientific_numbers {
  my ($self, $n) = @_;
  # mostly used to convert numbers in scientific notation
  if ($n =~ /^\s*\d+\s*$/) {
    return $n;
  } elsif ($n =~ /^\s*([-+]?)(\d*[\.,]*\d*)[eE]{1}([-+]?)(\d+)\s*$/) {
    my ($vor, $num, $sign, $exp) = ($1, $2, $3, $4);
    $n =~ s/E/e/g;
    $n =~ s/,/\./g;
    $num =~ s/,/\./g;
    my $sig = $sign eq '-' ? "." . ($exp - 1 + length $num) : '';
    my $dec = sprintf "%${sig}f", $n;
    $dec =~ s/\.[0]+$//g;
    return $dec;
  } elsif ($n =~ /^\s*([-+]?)(\d+)[\.,]*(\d*)\s*$/) {
    return $1.$2.".".$3;
  } elsif ($n =~ /^\s*(.*?)\s*$/) {
    return $1;
  } else {
    return $n;
  }
}

sub compatibility_methods {
  my ($self) = @_;
  # add_perfdata
  # add_message
  # nagios_exit
  # ->{warningrange}
  # ->{criticalrange}
  # ...
  $self->{warningrange} = ($self->get_thresholds())[0];
  $self->{criticalrange} = ($self->get_thresholds())[1];
  my $old_init = $self->can('init');
  my %params = (
    'mode' => join('::', split(/-/, $self->opts->mode)),
    'name' => $self->opts->name,
    'name2' => $self->opts->name2,
  );
  {
    no strict 'refs';
    no warnings 'redefine';
    *{ref($self).'::init'} = sub {
      $self->$old_init(%params);
      $self->nagios(%params);
    };
    *{ref($self).'::add_nagios'} = \&{"Monitoring::GLPlugin::add_message"};
    *{ref($self).'::add_nagios_ok'} = \&{"Monitoring::GLPlugin::add_ok"};
    *{ref($self).'::add_nagios_warning'} = \&{"Monitoring::GLPlugin::add_warning"};
    *{ref($self).'::add_nagios_critical'} = \&{"Monitoring::GLPlugin::add_critical"};
    *{ref($self).'::add_nagios_unknown'} = \&{"Monitoring::GLPlugin::add_unknown"};
    *{ref($self).'::add_perfdata'} = sub {
      my $self = shift;
      my $message = shift;
      foreach my $perfdata (split(/\s+/, $message)) {
      my ($label, $perfstr) = split(/=/, $perfdata);
      my ($value, $warn, $crit, $min, $max) = split(/;/, $perfstr);
      $value =~ /^([\d\.\-\+]+)(.*)$/;
      $value = $1;
      my $uom = $2;
      $Monitoring::GLPlugin::plugin->add_perfdata(
        label => $label,
        value => $value,
        uom => $uom,
        warn => $warn,
        crit => $crit,
        min => $min,
        max => $max,
      );
      }
    };
    *{ref($self).'::check_thresholds'} = sub {
      my $self = shift;
      my $value = shift;
      my $defaultwarningrange = shift;
      my $defaultcriticalrange = shift;
      $Monitoring::GLPlugin::plugin->set_thresholds(
          metric => 'default',
          warning => $defaultwarningrange,
          critical => $defaultcriticalrange,
      );
      $self->{warningrange} = ($self->get_thresholds())[0];
      $self->{criticalrange} = ($self->get_thresholds())[1];
      return $Monitoring::GLPlugin::plugin->check_thresholds(
          metric => 'default',
          value => $value,
          warning => $defaultwarningrange,
          critical => $defaultcriticalrange,
      );
    };
  }
}

sub AUTOLOAD {
  my ($self, @params) = @_;
  return if ($AUTOLOAD =~ /DESTROY/);
  $self->debug("AUTOLOAD %s\n", $AUTOLOAD)
        if $self->opts->verbose >= 2;
  if ($AUTOLOAD =~ /^(.*)::analyze_and_check_(.*)_subsystem$/) {
    my $class = $1;
    my $subsystem = $2;
    my $analyze = sprintf "analyze_%s_subsystem", $subsystem;
    my $check = sprintf "check_%s_subsystem", $subsystem;
    if (@params) {
      # analyzer class
      my $subsystem_class = shift @params;
      $self->{components}->{$subsystem.'_subsystem'} = $subsystem_class->new();
      $self->debug(sprintf "\$self->{components}->{%s_subsystem} = %s->new()",
          $subsystem, $subsystem_class);
    } else {
      $self->$analyze();
      $self->debug("call %s()", $analyze);
    }
    $self->$check();
  } elsif ($AUTOLOAD =~ /^(.*)::check_(.*)_subsystem$/) {
    my $class = $1;
    my $subsystem = sprintf "%s_subsystem", $2;
    $self->{components}->{$subsystem}->check();
    $self->{components}->{$subsystem}->dump()
        if $self->opts->verbose >= 2;
  } elsif ($AUTOLOAD =~ /^.*::(status_code|check_messages|nagios_exit|html_string|perfdata_string|selected_perfdata|check_thresholds|get_thresholds|mod_threshold|opts|pandora_string|strequal)$/) {
    return $Monitoring::GLPlugin::plugin->$1(@params);
  } elsif ($AUTOLOAD =~ /^.*::(reduce_messages|reduce_messages_short|clear_messages|suppress_messages|add_html|add_perfdata|override_opt|create_opt|set_thresholds|force_thresholds|add_pandora)$/) {
    $Monitoring::GLPlugin::plugin->$1(@params);
  } elsif ($AUTOLOAD =~ /^.*::mod_arg_(.*)$/) {
    return $Monitoring::GLPlugin::plugin->mod_arg($1, @params);
  } else {
    $self->debug("AUTOLOAD: class %s has no method %s\n",
        ref($self), $AUTOLOAD);
  }
}



package Monitoring::GLPlugin::Item;
our @ISA = qw(Monitoring::GLPlugin);

use strict;

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

sub check {
  my ($self, $lists) = @_;
  my @lists = $lists ? @{$lists} : grep { ref($self->{$_}) eq "ARRAY" } keys %{$self};
  foreach my $list (@lists) {
    $self->add_info('checking '.$list);
    foreach my $element (@{$self->{$list}}) {
      $element->blacklist() if $self->is_blacklisted();
      $element->check();
    }
  }
}

sub init_subsystems {
  my ($self, $subsysref) = @_;
  foreach (@{$subsysref}) {
    my ($subsys, $class) = @{$_};
    $self->{$subsys} = $class->new()
        if (! $self->opts->subsystem || grep {
            $_ eq $subsys;
        } map {
            s/^\s+|\s+$//g;
            $_;
        } split /,/, $self->opts->subsystem);
  }
}

sub check_subsystems {
  my ($self) = @_;
  my @subsystems = grep { $_ =~ /.*_subsystem$/ } keys %{$self};
  foreach (@subsystems) {
    $self->{$_}->check();
  }
  $self->reduce_messages_short(join(", ",
      map {
          sprintf "%s working fine", $_;
      } map {
          s/^\s+|\s+$//g;
          $_;
      } split /,/, $self->opts->subsystem
  )) if $self->opts->subsystem;
}

sub summarize_subsystems {
  my ($self) = @_;
  my @subsystems = grep { $_ =~ /.*_subsystem$/ } keys %{$self};
  my @subsystem_summary = ();
  foreach (@subsystems) {
    if ($self->{$_}->{subsystem_summary}) {
      push(@subsystem_summary, $self->{$_}->{subsystem_summary});
    }
  }
  return join(", ", @subsystem_summary);
}

sub dump_subsystems {
  my ($self) = @_;
  my @subsystems = grep { $_ =~ /.*_subsystem$/ } keys %{$self};
  foreach (@subsystems) {
    $self->{$_}->dump();
  }
}

sub subsystem_summary {
  my ($self, $summary) = @_;
  $self->{subsystem_summary} = $summary;
}



package Monitoring::GLPlugin::TableItem;
our @ISA = qw(Monitoring::GLPlugin::Item);

use strict;

sub new {
  my ($class, %params) = @_;
  my $self = {};
  bless $self, $class;
  foreach (keys %params) {
    $self->{$_} = $params{$_};
  }
  if ($self->can("finish")) {
    $self->finish(%params);
  }
  return $self;
}

sub check {
  my ($self) = @_;
  # some tableitems are not checkable, they are only used to enhance other
  # items (e.g. sensorthresholds enhance sensors)
  # normal tableitems should have their own check-method
}



package Monitoring::GLPlugin::SNMP;
our @ISA = qw(Monitoring::GLPlugin);
# ABSTRACT: helper functions to build a snmp-based monitoring plugin

use strict;
use File::Basename;
use Digest::MD5 qw(md5_hex);
use JSON;
use File::Slurp qw(read_file);
use Module::Load;
use AutoLoader;
our $AUTOLOAD;

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 $tablecache = {};
  our $info = [];
  our $extendedinfo = [];
  our $summary = [];
  our $oidtrace = [];
  our $uptime = 0;
}

sub new {
  my ($class, %params) = @_;
  require Monitoring::GLPlugin
      if ! grep /BEGIN/, keys %Monitoring::GLPlugin::;
  require Monitoring::GLPlugin::SNMP::MibsAndOids
      if ! grep /BEGIN/, keys %Monitoring::GLPlugin::SNMP::MibsAndOids::;
  require Monitoring::GLPlugin::SNMP::CSF
      if ! grep /BEGIN/, keys %Monitoring::GLPlugin::SNMP::CSF::;
  require Monitoring::GLPlugin::SNMP::Item
      if ! grep /BEGIN/, keys %Monitoring::GLPlugin::SNMP::Item::;
  require Monitoring::GLPlugin::SNMP::TableItem
      if ! grep /BEGIN/, keys %Monitoring::GLPlugin::SNMP::TableItem::;
  my $self = Monitoring::GLPlugin->new(%params);
  bless $self, $class;
  return $self;
}

sub v2tov3 {
  my ($self) = @_;
  if ($self->opts->community && $self->opts->community =~ /^snmpv3(.)(.+)/) {
    my $separator = $1;
    my ($authprotocol, $authpassword, $privprotocol, $privpassword,
        $username, $contextengineid, $contextname) = split(/$separator/, $2);
    $self->override_opt('authprotocol', $authprotocol)
        if defined($authprotocol) && $authprotocol;
    $self->override_opt('authpassword', $authpassword)
        if defined($authpassword) && $authpassword;
    $self->override_opt('privprotocol', $privprotocol)
        if defined($privprotocol) && $privprotocol;
    $self->override_opt('privpassword', $privpassword)
        if defined($privpassword) && $privpassword;
    $self->override_opt('username', $username) 
        if defined($username) && $username;
    $self->override_opt('contextengineid', $contextengineid)
        if defined($contextengineid) && $contextengineid;
    $self->override_opt('contextname', $contextname)
        if defined($contextname) && $contextname;
    $self->override_opt('community', undef) ;
    $self->override_opt('protocol', '3') ;
  }
  if (($self->opts->authpassword || $self->opts->authprotocol ||
      $self->opts->privpassword || $self->opts->privprotocol) && 
      $self->opts->protocol ne '3') {
    $self->override_opt('protocol', '3') ;
  }
  if ($self->opts->community2 && $self->opts->community2 =~ /^snmpv3(.)(.+)/) {
    my $separator = $1;
    $self->create_opt('authprotocol2');
    $self->create_opt('authpassword2');
    $self->create_opt('privprotocol2');
    $self->create_opt('privpassword2');
    $self->create_opt('username2');
    $self->create_opt('contextengineid2');
    $self->create_opt('contextname2');
    my ($authprotocol, $authpassword, $privprotocol, $privpassword,
        $username, $contextengineid, $contextname) = split(/$separator/, $2);
    $self->override_opt('authprotocol2', $authprotocol)
        if defined($authprotocol) && $authprotocol;
    $self->override_opt('authpassword2', $authpassword)
        if defined($authpassword) && $authpassword;
    $self->override_opt('privprotocol2', $privprotocol)
        if defined($privprotocol) && $privprotocol;
    $self->override_opt('privpassword2', $privpassword)
        if defined($privpassword) && $privpassword;
    $self->override_opt('username2', $username)
        if defined($username) && $username;
    $self->override_opt('contextengineid2', $contextengineid)
        if defined($contextengineid) && $contextengineid;
    $self->override_opt('contextname2', $contextname)
        if defined($contextname) && $contextname;
    $self->override_opt('community2', undef);
  }
}

sub add_snmp_modes {
  my ($self) = @_;
  $self->add_mode(
      internal => 'device::uptime',
      spec => 'uptime',
      alias => undef,
      help => 'Check the uptime of the device',
  );
  $self->add_mode(
      internal => 'device::walk',
      spec => 'walk',
      alias => undef,
      help => 'Show snmpwalk command with the oids necessary for a simulation',
  );
  $self->add_mode(
      internal => 'device::walkbulk',
      spec => 'bulkwalk',
      alias => undef,
      help => 'Show snmpbulkwalk command with the oids necessary for a simulation',
      hidden => 1,
  );
  $self->add_mode(
      internal => 'device::supportedmibs',
      spec => 'supportedmibs',
      alias => undef,
      help => 'Shows the names of the mibs which this devices has implemented (only lausser may run this command)',
  );
  $self->add_mode(
      internal => 'device::supportedoids',
      spec => 'supportedoids',
      alias => undef,
      help => 'Shows the names of the oids which this devices has implemented (only lausser may run this command)',
  );
}

sub add_snmp_args {
  my ($self) = @_;
  $self->add_arg(
      spec => 'hostname|H=s',
      help => '--hostname
   Hostname or IP-address of the switch or router',
      required => 0,
      env => 'HOSTNAME',
  );
  $self->add_arg(
      spec => 'port=i',
      help => '--port
   The SNMP port to use (default: 161)',
      required => 0,
      default => 161,
  );
  $self->add_arg(
      spec => 'domain=s',
      help => '--domain
   The transport domain to use (default: udp/ipv4, other possible values: udp6, udp/ipv6, tcp, tcp4, tcp/ipv4, tcp6, tcp/ipv6)',
      required => 0,
      default => 'udp',
  );
  $self->add_arg(
      spec => 'protocol|P=s',
      help => '--protocol
   The SNMP protocol to use (default: 2c, other possibilities: 1,3)',
      required => 0,
      default => '2c',
  );
  $self->add_arg(
      spec => 'community|C=s',
      help => '--community
   SNMP community of the server (SNMP v1/2 only)',
      required => 0,
      default => 'public',
      decode => "rfc3986",
  );
  $self->add_arg(
      spec => 'username:s',
      help => '--username
   The securityName for the USM security model (SNMPv3 only)',
      required => 0,
  );
  $self->add_arg(
      spec => 'authpassword:s',
      help => '--authpassword
   The authentication password for SNMPv3',
      required => 0,
      decode => "rfc3986",
  );
  $self->add_arg(
      spec => 'authprotocol:s',
      help => '--authprotocol
   The authentication protocol for SNMPv3 (md5|sha)',
      required => 0,
  );
  $self->add_arg(
      spec => 'privpassword:s',
      help => '--privpassword
   The password for authPriv security level',
      required => 0,
      decode => "rfc3986",
  );
  $self->add_arg(
      spec => 'privprotocol=s',
      help => '--privprotocol
   The private protocol for SNMPv3 (des|aes|aes128|3des|3desde)',
      required => 0,
  );
  $self->add_arg(
      spec => 'contextengineid=s',
      help => '--contextengineid
   The context engine id for SNMPv3 (10 to 64 hex characters)',
      required => 0,
  );
  $self->add_arg(
      spec => 'contextname=s',
      help => '--contextname
   The context name for SNMPv3 (empty represents the "default" context)',
      required => 0,
  );
  $self->add_arg(
      spec => 'community2=s',
      help => '--community2
   SNMP community which can be used to switch the context during runtime',
      required => 0,
      decode => "rfc3986",
  );
  $self->add_arg(
      spec => '--join-communities',
      help => '--join-communities
   It --community2 is used, run the query with community, then community2
   and add up both results. The default is to use community for the initial
   handshake and community2 for querying (bgp and ospf)',
      required => 0,
      default => 0,
  );
  $self->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,
      env => 'SNMPWALK',
  );
  $self->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,
  );
  $self->add_arg(
      spec => 'oids=s',
      help => '--oids
   A list of oids which are downloaded and written to a cache file.
   Use it together with --mode oidcache',
      required => 0,
  );
  $self->add_arg(
      spec => 'offline:i',
      help => '--offline
   The maximum number of seconds since the last update of cache file before
   it is considered too old',
      required => 0,
      env => 'OFFLINE',
  );
}

sub validate_args {
  my ($self) = @_;
  $self->SUPER::validate_args();
  if ($self->opts->mode =~ /^(bulk)*walk/) {
    if ($self->opts->snmpwalk && $self->opts->hostname) {
      if ($self->check_messages == CRITICAL) {
        # gemecker vom super-validierer, der sicherstellt, dass die datei
        # snmpwalk existiert. in diesem fall wird sie aber erst neu angelegt,
        # also schnauze.
        my ($code, $message) = $self->check_messages;
        if ($message eq sprintf("file %s not found", $self->opts->snmpwalk)) {
          $self->clear_critical;
        }
      }
      # snmp agent wird abgefragt, die ergebnisse landen in einem file
      # opts->snmpwalk ist der filename. da sich die ganzen get_snmp_table/object-aufrufe
      # an das walkfile statt an den agenten halten wuerden, muss opts->snmpwalk geloescht
      # werden. stattdessen wird opts->snmpdump als traeger des dateinamens mitgegeben.
      # nur sinnvoll mit mode=walk
      $self->create_opt('snmpdump');
      $self->override_opt('snmpdump', $self->opts->snmpwalk);
      $self->override_opt('snmpwalk', undef);
    } elsif (! $self->opts->snmpwalk && $self->opts->hostname) {
      # snmp agent wird abgefragt, die ergebnisse landen in einem file, dessen name
      # nicht vorgegeben ist
      $self->create_opt('snmpdump');
    }
  } else {    
    if ($self->opts->snmpwalk && ! $self->opts->hostname) {
      # normaler aufruf, mode != walk, oid-quelle ist eine datei
      $self->override_opt('hostname', 'snmpwalk.file'.md5_hex($self->opts->snmpwalk))
    } elsif ($self->opts->snmpwalk && $self->opts->hostname) {
      # snmpwalk hat vorrang
      $self->override_opt('hostname', undef);
    }
  }
}

sub init {
  my ($self) = @_;
  if ($self->mode =~ /device::(bulk)*walk/) {
    my @trees = ();
    my $name = $Monitoring::GLPlugin::pluginname;
    $name =~ s/.*\///g;
    $name = sprintf "/tmp/snmpwalk_%s_%s", $name, $self->opts->hostname;
    if ($self->opts->oids) {
      # create pid filename
      # already running?;x
      @trees = split(",", $self->opts->oids);

    } elsif ($self->can("trees")) {
      @trees = $self->trees;
      push(@trees, "1.3.6.1.2.1.1");
    } else {
      @trees = ("1.3.6.1.2.1", "1.3.6.1.4.1");
    }
    if ($self->opts->snmpdump) {
      $name = $self->opts->snmpdump;
    }
    $self->opts->override_opt("protocol", $1) if $self->opts->protocol =~ /^v(.*)/;
    if (defined $self->opts->offline) {
      $self->{pidfile} = $name.".pid";
      if (! $self->check_pidfile()) {
        $self->debug("Exiting because another walk is already running");
        printf STDERR "Exiting because another walk is already running\n";
        exit 3;
      }
      $self->write_pidfile();
      my $timedout = 0;
      my $snmpwalkpid = 0;
      $SIG{'ALRM'} = sub {
        $timedout = 1;
        printf "UNKNOWN - %s timed out after %d seconds\n",
            $Monitoring::GLPlugin::plugin->{name}, $self->opts->timeout;
        kill 9, $snmpwalkpid;
      };
      alarm($self->opts->timeout);
      unlink $name.".partial";
      while (! $timedout && @trees) {
        my $tree = shift @trees;
        $SIG{CHLD} = 'IGNORE';
        my $cmd = sprintf "%s -ObentU -v%s -c %s %s %s >> %s",
            ($self->mode =~ /bulk/) ? "snmpbulkwalk" : "snmpwalk",
            $self->opts->protocol,
            $self->opts->community,
            $self->opts->hostname,
            $tree, $name.".partial";
        $self->debug($cmd);
        $snmpwalkpid = fork;
        if (not $snmpwalkpid) {
          exec($cmd);
        } else {
          wait();
        }
      }
      rename $name.".partial", $name if ! $timedout;
      -f $self->{pidfile} && unlink $self->{pidfile};
      if ($timedout) {
        printf "CRITICAL - timeout. There are still %d snmpwalks left\n", scalar(@trees);
        exit 3;
      } else {
        printf "OK - all requested oids are in %s\n", $name;
      }
    } else {
      my @credentials = ();
      my $credmapping = {
        "-community" => "-c",
        "-privpassword" => "-X",
        "-privprotocol" => "-x",
        "-authpassword" => "-A",
        "-authprotocol" => "-a",
        "-username" => "-u",
        "-context" => "-n",
        "-version" => "-v",
      };
      foreach (keys %{$Monitoring::GLPlugin::SNMP::session_params}) {
        if (exists $credmapping->{$_}) {
          push(@credentials, sprintf "%s '%s'",
              $credmapping->{$_},
              $Monitoring::GLPlugin::SNMP::session_params->{$_}
          );
        }
      }
      if (grep(/-X/, @credentials) and grep(/-A/, @credentials)) {
        push(@credentials, "-l authPriv");
      } elsif (grep(/-A/, @credentials)) {
        push(@credentials, "-l authNoPriv");
      } elsif (! grep(/-c/, @credentials)) {
        push(@credentials, "-l noAuthNoPriv");
      }
      my $credentials = join(" ", @credentials);
      $credentials =~ s/-v2 /-v2c /g;
      printf "rm -f %s\n", $name;
      foreach (@trees) {
        printf "%s -ObentU %s %s %s >> %s\n",
            ($self->mode =~ /bulk/) ? "snmpbulkwalk -t 15 -r 20" : "snmpwalk",
            $credentials,
            $self->opts->hostname,
            $_, $name;
      }
    }
    exit 0;
  } elsif ($self->mode =~ /device::uptime/) {
    $self->add_info(sprintf 'device is up since %s',
        $self->human_timeticks($self->{uptime}));
    $self->set_thresholds(warning => '15:', critical => '5:');
    $self->add_message($self->check_thresholds($self->{uptime} / 60));
    $self->add_perfdata(
        label => 'uptime',
        value => $self->{uptime} / 60,
        places => 0,
    );
    if ($self->opts->report ne 'short') {
      $self->add_ok($self->pretty_sysdesc($self->{productname}));
    }
    my ($code, $message) = $self->check_messages(join => ', ', join_all => ', ');
    $self->nagios_exit($code, $message);
  } elsif ($self->mode =~ /device::supportedmibs/) {
    our $mibdepot = [];
    my $unknowns = {};
    my @outputlist = ();
    %{$unknowns} = %{$self->rawdata};
    if ($self->opts->name && -f $self->opts->name) {
      eval { require $self->opts->name };
      $self->add_critical($@) if $@;
    } elsif ($self->opts->name && ! -f $self->opts->name) {
      $self->add_unknown("where is --name mibdepotfile?");
    }
    push(@{$mibdepot}, ['1.3.6.1.2.1.60', 'ietf', 'v2', 'ACCOUNTING-CONTROL-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.10.238', 'ietf', 'v2', 'ADSL2-LINE-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.10.238.2', 'ietf', 'v2', 'ADSL2-LINE-TC-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.10.94.3', 'ietf', 'v2', 'ADSL-LINE-EXT-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.10.94', 'ietf', 'v2', 'ADSL-LINE-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.10.94.2', 'ietf', 'v2', 'ADSL-TC-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.74', 'ietf', 'v2', 'AGENTX-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.3.123', 'ietf', 'v2', 'AGGREGATE-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.118', 'ietf', 'v2', 'ALARM-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.16.23', 'ietf', 'v2', 'APM-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.34.3', 'ietf', 'v2', 'APPC-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.13.1', 'ietf', 'v1', 'APPLETALK-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.27', 'ietf', 'v2', 'APPLICATION-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.62', 'ietf', 'v2', 'APPLICATION-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.34.5', 'ietf', 'v2', 'APPN-DLUR-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.34.4', 'ietf', 'v2', 'APPN-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.34.4', 'ietf', 'v2', 'APPN-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.34.4.0', 'ietf', 'v2', 'APPN-TRAP-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.10.49', 'ietf', 'v2', 'APS-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.117', 'ietf', 'v2', 'ARC-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.37.1.14', 'ietf', 'v2', 'ATM2-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.59', 'ietf', 'v2', 'ATM-ACCOUNTING-INFORMATION-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.37', 'ietf', 'v2', 'ATM-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.37', 'ietf', 'v2', 'ATM-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.37.3', 'ietf', 'v2', 'ATM-TC-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.15', 'ietf', 'v2', 'BGP4-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.15', 'ietf', 'v2', 'BGP4-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.3.122', 'ietf', 'v2', 'BLDG-HVAC-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.17.1', 'ietf', 'v1', 'BRIDGE-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.17', 'ietf', 'v2', 'BRIDGE-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.19', 'ietf', 'v2', 'CHARACTER-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.94', 'ietf', 'v2', 'CIRCUIT-IF-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.3.1.1', 'ietf', 'v1', 'CLNS-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.3.1.1', 'ietf', 'v1', 'CLNS-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.10.132', 'ietf', 'v2', 'COFFEE-POT-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.89', 'ietf', 'v2', 'COPS-CLIENT-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.18.1', 'ietf', 'v1', 'DECNET-PHIV-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.10.21', 'ietf', 'v2', 'DIAL-CONTROL-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.108', 'ietf', 'v2', 'DIFFSERV-CONFIG-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.97', 'ietf', 'v2', 'DIFFSERV-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.66', 'ietf', 'v2', 'DIRECTORY-SERVER-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.88', 'ietf', 'v2', 'DISMAN-EVENT-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.90', 'ietf', 'v2', 'DISMAN-EXPRESSION-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.82', 'ietf', 'v2', 'DISMAN-NSLOOKUP-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.82', 'ietf', 'v2', 'DISMAN-NSLOOKUP-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.80', 'ietf', 'v2', 'DISMAN-PING-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.80', 'ietf', 'v2', 'DISMAN-PING-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.63', 'ietf', 'v2', 'DISMAN-SCHEDULE-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.63', 'ietf', 'v2', 'DISMAN-SCHEDULE-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.64', 'ietf', 'v2', 'DISMAN-SCRIPT-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.64', 'ietf', 'v2', 'DISMAN-SCRIPT-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.81', 'ietf', 'v2', 'DISMAN-TRACEROUTE-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.81', 'ietf', 'v2', 'DISMAN-TRACEROUTE-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.46', 'ietf', 'v2', 'DLSW-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.32.2', 'ietf', 'v2', 'DNS-RESOLVER-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.32.1', 'ietf', 'v2', 'DNS-SERVER-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.10.127.5', 'ietf', 'v2', 'DOCS-BPI-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.69', 'ietf', 'v2', 'DOCS-CABLE-DEVICE-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.69', 'ietf', 'v2', 'DOCS-CABLE-DEVICE-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.126', 'ietf', 'v2', 'DOCS-IETF-BPI2-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.132', 'ietf', 'v2', 'DOCS-IETF-CABLE-DEVICE-NOTIFICATION-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.127', 'ietf', 'v2', 'DOCS-IETF-QOS-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.125', 'ietf', 'v2', 'DOCS-IETF-SUBMGT-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.10.127', 'ietf', 'v2', 'DOCS-IF-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.10.127', 'ietf', 'v2', 'DOCS-IF-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.10.45', 'ietf', 'v2', 'DOT12-IF-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.53', 'ietf', 'v2', 'DOT12-RPTR-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.155', 'ietf', 'v2', 'DOT3-EPON-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.158', 'ietf', 'v2', 'DOT3-OAM-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.4.1.2.2.1.1', 'ietf', 'v1', 'DPI20-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.10.82', 'ietf', 'v2', 'DS0BUNDLE-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.10.81', 'ietf', 'v2', 'DS0-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.10.18', 'ietf', 'v2', 'DS1-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.10.18', 'ietf', 'v2', 'DS1-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.10.18', 'ietf', 'v2', 'DS1-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.10.30', 'ietf', 'v2', 'DS3-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.10.30', 'ietf', 'v2', 'DS3-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.29', 'ietf', 'v2', 'DSA-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.16.26', 'ietf', 'v2', 'DSMON-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.34.7', 'ietf', 'v2', 'EBN-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.167', 'ietf', 'v2', 'EFM-CU-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.47', 'ietf', 'v2', 'ENTITY-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.47', 'ietf', 'v2', 'ENTITY-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.47', 'ietf', 'v2', 'ENTITY-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.99', 'ietf', 'v2', 'ENTITY-SENSOR-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.131', 'ietf', 'v2', 'ENTITY-STATE-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.130', 'ietf', 'v2', 'ENTITY-STATE-TC-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.70', 'ietf', 'v2', 'ETHER-CHIPSET-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.10.7', 'ietf', 'v1', 'EtherLike-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.10.7', 'ietf', 'v1', 'EtherLike-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.35', 'ietf', 'v2', 'EtherLike-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.35', 'ietf', 'v2', 'EtherLike-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.35', 'ietf', 'v2', 'EtherLike-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.35', 'ietf', 'v2', 'EtherLike-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.224', 'ietf', 'v2', 'FCIP-MGMT-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.10.56', 'ietf', 'v2', 'FC-MGMT-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.10.15.73.1', 'ietf', 'v1', 'FDDI-SMT73-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.75', 'ietf', 'v2', 'FIBRE-CHANNEL-FE-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.111', 'ietf', 'v2', 'Finisher-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.40', 'ietf', 'v2', 'FLOW-METER-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.40', 'ietf', 'v2', 'FLOW-METER-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.10.32', 'ietf', 'v2', 'FRAME-RELAY-DTE-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.86', 'ietf', 'v2', 'FR-ATM-PVC-SERVICE-IWF-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.10.47', 'ietf', 'v2', 'FR-MFR-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.10.44', 'ietf', 'v2', 'FRNETSERV-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.10.44', 'ietf', 'v2', 'FRNETSERV-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.10.44', 'ietf', 'v2', 'FRNETSERV-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.95', 'ietf', 'v2', 'FRSLD-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.10.166.16', 'ietf', 'v2', 'GMPLS-LABEL-STD-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.10.166.15', 'ietf', 'v2', 'GMPLS-LSR-STD-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.10.166.12', 'ietf', 'v2', 'GMPLS-TC-STD-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.10.166.13', 'ietf', 'v2', 'GMPLS-TE-STD-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.98', 'ietf', 'v2', 'GSMP-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.16.29', 'ietf', 'v2', 'HC-ALARM-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.107', 'ietf', 'v2', 'HC-PerfHist-TC-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.16.20.5', 'ietf', 'v2', 'HC-RMON-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.25.1', 'ietf', 'v1', 'HOST-RESOURCES-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.25.1', 'ietf', 'v2', 'HOST-RESOURCES-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.34.6.1.5', 'ietf', 'v2', 'HPR-IP-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.34.6', 'ietf', 'v2', 'HPR-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.106', 'ietf', 'v2', 'IANA-CHARSET-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.110', 'ietf', 'v2', 'IANA-FINISHER-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.152', 'ietf', 'v2', 'IANA-GMPLS-TC-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.30', 'ietf', 'v2', 'IANAifType-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.128', 'ietf', 'v2', 'IANA-IPPM-METRICS-REGISTRY-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.119', 'ietf', 'v2', 'IANA-ITU-ALARM-TC-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.154', 'ietf', 'v2', 'IANA-MAU-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.109', 'ietf', 'v2', 'IANA-PRINTER-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.4.1.2.6.2.13.1.1', 'ietf', 'v1', 'IBM-6611-APPN-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.166', 'ietf', 'v2', 'IF-CAP-STACK-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.10.230', 'ietf', 'v2', 'IFCP-MGMT-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.77', 'ietf', 'v2', 'IF-INVERTED-STACK-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.31', 'ietf', 'v2', 'IF-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.31', 'ietf', 'v2', 'IF-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.31', 'ietf', 'v2', 'IF-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.85', 'ietf', 'v2', 'IGMP-STD-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.76', 'ietf', 'v2', 'INET-ADDRESS-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.76', 'ietf', 'v2', 'INET-ADDRESS-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.76', 'ietf', 'v2', 'INET-ADDRESS-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.52.5', 'ietf', 'v2', 'INTEGRATED-SERVICES-GUARANTEED-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.52', 'ietf', 'v2', 'INTEGRATED-SERVICES-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.16.27', 'ietf', 'v2', 'INTERFACETOPN-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.6.3.17', 'ietf', 'v2', 'IPATM-IPMC-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.57', 'ietf', 'v2', 'IPATM-IPMC-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.4.24', 'ietf', 'v2', 'IP-FORWARD-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.4.24', 'ietf', 'v2', 'IP-FORWARD-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.168', 'ietf', 'v2', 'IPMCAST-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.48', 'ietf', 'v2', 'IP-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.48', 'ietf', 'v2', 'IP-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.83', 'ietf', 'v2', 'IPMROUTE-STD-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.10.46', 'ietf', 'v2', 'IPOA-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.141', 'ietf', 'v2', 'IPS-AUTH-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.153', 'ietf', 'v2', 'IPSEC-SPD-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.103', 'ietf', 'v2', 'IPV6-FLOW-LABEL-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.56', 'ietf', 'v2', 'IPV6-ICMP-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.55', 'ietf', 'v2', 'IPV6-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.91', 'ietf', 'v2', 'IPV6-MLD-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.3.86', 'ietf', 'v2', 'IPV6-TCP-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.3.87', 'ietf', 'v2', 'IPV6-UDP-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.142', 'ietf', 'v2', 'ISCSI-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.10.20', 'ietf', 'v2', 'ISDN-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.138', 'ietf', 'v2', 'ISIS-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.163', 'ietf', 'v2', 'ISNS-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.121', 'ietf', 'v2', 'ITU-ALARM-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.120', 'ietf', 'v2', 'ITU-ALARM-TC-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.4.1.2699.1.1', 'ietf', 'v2', 'Job-Monitoring-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.10.95', 'ietf', 'v2', 'L2TP-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.165', 'ietf', 'v2', 'LANGTAG-TC-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.10.227', 'ietf', 'v2', 'LMP-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.10.227', 'ietf', 'v2', 'LMP-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.101', 'ietf', 'v2', 'MALLOC-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.26.1', 'ietf', 'v1', 'MAU-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.26.6', 'ietf', 'v2', 'MAU-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.26.6', 'ietf', 'v2', 'MAU-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.26.6', 'ietf', 'v2', 'MAU-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.26.6', 'ietf', 'v2', 'MAU-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.171', 'ietf', 'v2', 'MIDCOM-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.10.38.1', 'ietf', 'v1', 'MIOX25-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.44', 'ietf', 'v2', 'MIP-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.133', 'ietf', 'v2', 'MOBILEIPV6-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.38', 'ietf', 'v2', 'Modem-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.10.166.8', 'ietf', 'v2', 'MPLS-FTN-STD-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.10.166.11', 'ietf', 'v2', 'MPLS-L3VPN-STD-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.10.166.9', 'ietf', 'v2', 'MPLS-LC-ATM-STD-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.10.166.10', 'ietf', 'v2', 'MPLS-LC-FR-STD-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.10.166.5', 'ietf', 'v2', 'MPLS-LDP-ATM-STD-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.10.166.6', 'ietf', 'v2', 'MPLS-LDP-FRAME-RELAY-STD-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.10.166.7', 'ietf', 'v2', 'MPLS-LDP-GENERIC-STD-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.4.1.9.10.65', 'ietf', 'v2', 'MPLS-LDP-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.10.166.4', 'ietf', 'v2', 'MPLS-LDP-STD-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.10.166.2', 'ietf', 'v2', 'MPLS-LSR-STD-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.10.166.1', 'ietf', 'v2', 'MPLS-TC-STD-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.10.166.3', 'ietf', 'v2', 'MPLS-TE-STD-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.3.92', 'ietf', 'v2', 'MSDP-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.28', 'ietf', 'v2', 'MTA-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.28', 'ietf', 'v2', 'MTA-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.28', 'ietf', 'v2', 'MTA-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.123', 'ietf', 'v2', 'NAT-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.27', 'ietf', 'v2', 'NETWORK-SERVICES-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.27', 'ietf', 'v2', 'NETWORK-SERVICES-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.71', 'ietf', 'v2', 'NHRP-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.92', 'ietf', 'v2', 'NOTIFICATION-LOG-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.10.133', 'ietf', 'v2', 'OPT-IF-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.14', 'ietf', 'v2', 'OSPF-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.14', 'ietf', 'v2', 'OSPF-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.14.16', 'ietf', 'v2', 'OSPF-TRAP-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.14.16', 'ietf', 'v2', 'OSPF-TRAP-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.10.34', 'ietf', 'v2', 'PARALLEL-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.17.6', 'ietf', 'v2', 'P-BRIDGE-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.58', 'ietf', 'v2', 'PerfHist-TC-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.58', 'ietf', 'v2', 'PerfHist-TC-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.172', 'ietf', 'v2', 'PIM-BSR-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.3.61', 'ietf', 'v2', 'PIM-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.157', 'ietf', 'v2', 'PIM-STD-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.93', 'ietf', 'v2', 'PINT-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.140', 'ietf', 'v2', 'PKTC-IETF-MTA-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.169', 'ietf', 'v2', 'PKTC-IETF-SIG-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.124', 'ietf', 'v2', 'POLICY-BASED-MANAGEMENT-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.105', 'ietf', 'v2', 'POWER-ETHERNET-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.10.23.4', 'ietf', 'v1', 'PPP-BRIDGE-NCP-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.10.23.3', 'ietf', 'v1', 'PPP-IP-NCP-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.10.23.1.1', 'ietf', 'v1', 'PPP-LCP-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.10.23.2', 'ietf', 'v1', 'PPP-SEC-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.43', 'ietf', 'v2', 'Printer-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.43', 'ietf', 'v2', 'Printer-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.79', 'ietf', 'v2', 'PTOPO-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.17.7', 'ietf', 'v2', 'Q-BRIDGE-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.67.2.2', 'ietf', 'v2', 'RADIUS-ACC-CLIENT-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.67.2.2', 'ietf', 'v2', 'RADIUS-ACC-CLIENT-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.67.2.1', 'ietf', 'v2', 'RADIUS-ACC-SERVER-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.67.2.1', 'ietf', 'v2', 'RADIUS-ACC-SERVER-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.67.1.2', 'ietf', 'v2', 'RADIUS-AUTH-CLIENT-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.67.1.2', 'ietf', 'v2', 'RADIUS-AUTH-CLIENT-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.67.1.1', 'ietf', 'v2', 'RADIUS-AUTH-SERVER-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.67.1.1', 'ietf', 'v2', 'RADIUS-AUTH-SERVER-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.145', 'ietf', 'v2', 'RADIUS-DYNAUTH-CLIENT-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.146', 'ietf', 'v2', 'RADIUS-DYNAUTH-SERVER-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.16.31', 'ietf', 'v2', 'RAQMON-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.16.32', 'ietf', 'v2', 'RAQMON-RDS-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.39', 'ietf', 'v2', 'RDBMS-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.1', 'ietf', 'v1', 'RFC1066-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.1', 'ietf', 'v1', 'RFC1156-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.1', 'ietf', 'v1', 'RFC1158-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.1', 'ietf', 'v1', 'RFC1213-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.12', 'ietf', 'v1', 'RFC1229-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.3.7', 'ietf', 'v1', 'RFC1230-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.10.9', 'ietf', 'v1', 'RFC1231-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.3.2', 'ietf', 'v1', 'RFC1232-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.3.15', 'ietf', 'v1', 'RFC1233-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.13.1', 'ietf', 'v1', 'RFC1243-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.13.1', 'ietf', 'v1', 'RFC1248-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.13.1', 'ietf', 'v1', 'RFC1252-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.14.1', 'ietf', 'v1', 'RFC1253-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.15', 'ietf', 'v1', 'RFC1269-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.16.1', 'ietf', 'v1', 'RFC1271-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.10.7', 'ietf', 'v1', 'RFC1284-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.10.15.1', 'ietf', 'v1', 'RFC1285-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.17.1', 'ietf', 'v1', 'RFC1286-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.18.1', 'ietf', 'v1', 'RFC1289-phivMIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.10.31', 'ietf', 'v1', 'RFC1304-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.10.32', 'ietf', 'v1', 'RFC1315-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.19', 'ietf', 'v1', 'RFC1316-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.10.33', 'ietf', 'v1', 'RFC1317-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.10.34', 'ietf', 'v1', 'RFC1318-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.20.2', 'ietf', 'v1', 'RFC1353-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.4.24', 'ietf', 'v1', 'RFC1354-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.10.16', 'ietf', 'v1', 'RFC1381-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.10.5', 'ietf', 'v1', 'RFC1382-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.23.1', 'ietf', 'v1', 'RFC1389-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.10.7', 'ietf', 'v1', 'RFC1398-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.10.18', 'ietf', 'v1', 'RFC1406-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.10.30', 'ietf', 'v1', 'RFC1407-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.24.1', 'ietf', 'v1', 'RFC1414-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.23', 'ietf', 'v2', 'RIPv2-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.16', 'ietf', 'v2', 'RMON2-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.16', 'ietf', 'v2', 'RMON2-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.16.1', 'ietf', 'v1', 'RMON-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.16.20.8', 'ietf', 'v2', 'RMON-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.112', 'ietf', 'v2', 'ROHC-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.114', 'ietf', 'v2', 'ROHC-RTP-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.113', 'ietf', 'v2', 'ROHC-UNCOMPRESSED-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.10.33', 'ietf', 'v2', 'RS-232-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.134', 'ietf', 'v2', 'RSTP-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.51', 'ietf', 'v2', 'RSVP-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.87', 'ietf', 'v2', 'RTP-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.139', 'ietf', 'v2', 'SCSI-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.104', 'ietf', 'v2', 'SCTP-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.4.1.4300.1', 'ietf', 'v2', 'SFLOW-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.149', 'ietf', 'v2', 'SIP-COMMON-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.36', 'ietf', 'v2', 'SIP-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.151', 'ietf', 'v2', 'SIP-SERVER-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.148', 'ietf', 'v2', 'SIP-TC-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.150', 'ietf', 'v2', 'SIP-UA-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.3.88', 'ietf', 'v2', 'SLAPM-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.16.22', 'ietf', 'v2', 'SMON-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.4.1.4.4', 'ietf', 'v1', 'SMUX-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.34', 'ietf', 'v2', 'SNA-NAU-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.34', 'ietf', 'v2', 'SNA-NAU-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.41', 'ietf', 'v2', 'SNA-SDLC-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.6.3.18', 'ietf', 'v2', 'SNMP-COMMUNITY-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.6.3.18', 'ietf', 'v2', 'SNMP-COMMUNITY-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.6.3.10', 'ietf', 'v2', 'SNMP-FRAMEWORK-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.6.3.10', 'ietf', 'v2', 'SNMP-FRAMEWORK-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.6.3.10', 'ietf', 'v2', 'SNMP-FRAMEWORK-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.6.3.21', 'ietf', 'v2', 'SNMP-IEEE802-TM-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.6.3.11', 'ietf', 'v2', 'SNMP-MPD-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.6.3.11', 'ietf', 'v2', 'SNMP-MPD-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.6.3.11', 'ietf', 'v2', 'SNMP-MPD-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.6.3.13', 'ietf', 'v2', 'SNMP-NOTIFICATION-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.6.3.13', 'ietf', 'v2', 'SNMP-NOTIFICATION-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.6.3.13', 'ietf', 'v2', 'SNMP-NOTIFICATION-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.6.3.14', 'ietf', 'v2', 'SNMP-PROXY-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.6.3.14', 'ietf', 'v2', 'SNMP-PROXY-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.6.3.14', 'ietf', 'v2', 'SNMP-PROXY-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.22.1.1', 'ietf', 'v1', 'SNMP-REPEATER-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.22.1.1', 'ietf', 'v1', 'SNMP-REPEATER-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.22.5', 'ietf', 'v2', 'SNMP-REPEATER-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.6.3.12', 'ietf', 'v2', 'SNMP-TARGET-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.6.3.12', 'ietf', 'v2', 'SNMP-TARGET-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.6.3.12', 'ietf', 'v2', 'SNMP-TARGET-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.6.3.15', 'ietf', 'v2', 'SNMP-USER-BASED-SM-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.6.3.15', 'ietf', 'v2', 'SNMP-USER-BASED-SM-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.6.3.15', 'ietf', 'v2', 'SNMP-USER-BASED-SM-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.6.3.20', 'ietf', 'v2', 'SNMP-USM-AES-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.3.101', 'ietf', 'v2', 'SNMP-USM-DH-OBJECTS-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.6.3.2', 'ietf', 'v2', 'SNMPv2-M2M-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.6.3.1', 'ietf', 'v2', 'SNMPv2-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.6.3.1', 'ietf', 'v2', 'SNMPv2-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.6.3.1', 'ietf', 'v2', 'SNMPv2-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.6.3.3', 'ietf', 'v2', 'SNMPv2-PARTY-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.6.3.6', 'ietf', 'v2', 'SNMPv2-USEC-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.6.3.16', 'ietf', 'v2', 'SNMP-VIEW-BASED-ACM-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.6.3.16', 'ietf', 'v2', 'SNMP-VIEW-BASED-ACM-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.6.3.16', 'ietf', 'v2', 'SNMP-VIEW-BASED-ACM-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.10.39', 'ietf', 'v2', 'SONET-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.10.39', 'ietf', 'v2', 'SONET-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.10.39', 'ietf', 'v2', 'SONET-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.17.3', 'ietf', 'v1', 'SOURCE-ROUTING-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.16.28', 'ietf', 'v2', 'SSPM-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.54', 'ietf', 'v2', 'SYSAPPL-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.137', 'ietf', 'v2', 'T11-FC-FABRIC-ADDR-MGR-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.162', 'ietf', 'v2', 'T11-FC-FABRIC-CONFIG-SERVER-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.159', 'ietf', 'v2', 'T11-FC-FABRIC-LOCK-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.143', 'ietf', 'v2', 'T11-FC-FSPF-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.135', 'ietf', 'v2', 'T11-FC-NAME-SERVER-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.144', 'ietf', 'v2', 'T11-FC-ROUTE-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.161', 'ietf', 'v2', 'T11-FC-RSCN-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.176', 'ietf', 'v2', 'T11-FC-SP-AUTHENTICATION-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.178', 'ietf', 'v2', 'T11-FC-SP-POLICY-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.179', 'ietf', 'v2', 'T11-FC-SP-SA-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.175', 'ietf', 'v2', 'T11-FC-SP-TC-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.177', 'ietf', 'v2', 'T11-FC-SP-ZONING-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.147', 'ietf', 'v2', 'T11-FC-VIRTUAL-FABRIC-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.160', 'ietf', 'v2', 'T11-FC-ZONE-SERVER-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.136', 'ietf', 'v2', 'T11-TC-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.156', 'ietf', 'v2', 'TCP-ESTATS-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.4.1.23.2.29.1', 'ietf', 'v1', 'TCPIPX-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.49', 'ietf', 'v2', 'TCP-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.49', 'ietf', 'v2', 'TCP-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.10.200', 'ietf', 'v2', 'TE-LINK-STD-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.122', 'ietf', 'v2', 'TE-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.3.124', 'ietf', 'v2', 'TIME-AGGREGATE-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.34.8', 'ietf', 'v2', 'TN3270E-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.34.9', 'ietf', 'v2', 'TN3270E-RT-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.10.9', 'ietf', 'v2', 'TOKENRING-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.10.9', 'ietf', 'v2', 'TOKENRING-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.16.1', 'ietf', 'v1', 'TOKEN-RING-RMON-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.42', 'ietf', 'v2', 'TOKENRING-STATION-SR-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.16.30', 'ietf', 'v2', 'TPM-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.100', 'ietf', 'v2', 'TRANSPORT-ADDRESS-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.116', 'ietf', 'v2', 'TRIP-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.115', 'ietf', 'v2', 'TRIP-TC-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.10.131', 'ietf', 'v2', 'TUNNEL-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.10.131', 'ietf', 'v2', 'TUNNEL-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.170', 'ietf', 'v2', 'UDPLITE-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.50', 'ietf', 'v2', 'UDP-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.50', 'ietf', 'v2', 'UDP-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.33', 'ietf', 'v2', 'UPS-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.164', 'ietf', 'v2', 'URI-TC-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.10.229', 'ietf', 'v2', 'VDSL-LINE-EXT-MCM-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.10.228', 'ietf', 'v2', 'VDSL-LINE-EXT-SCM-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.10.97', 'ietf', 'v2', 'VDSL-LINE-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.129', 'ietf', 'v2', 'VPN-TC-STD-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.68', 'ietf', 'v2', 'VRRP-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.2.1.65', 'ietf', 'v2', 'WWW-MIB']);
    push(@{$mibdepot}, ['1.3.6.1.4.1.8072', 'net-snmp', 'v2', 'NET-SNMP-MIB']);
    my $oids = $self->get_entries_by_walk(-varbindlist => [
        '1.3.6.1.2.1', '1.3.6.1.4.1', '1',
    ]);
    foreach my $mibinfo (@{$mibdepot}) {
      next if $self->opts->protocol eq "1" && $mibinfo->[2] ne "v1";
      next if $self->opts->protocol ne "1" && $mibinfo->[2] eq "v1";
      $Monitoring::GLPlugin::SNMP::MibsAndOids::mib_ids->{$mibinfo->[3]} = $mibinfo->[0];
    }
    $Monitoring::GLPlugin::SNMP::MibsAndOids::mib_ids->{'MIB-2-MIB'} = "1.3.6.1.2.1";
    foreach my $mib (keys %{$Monitoring::GLPlugin::SNMP::MibsAndOids::mib_ids}) {
      if ($self->implements_mib($mib)) {
        push(@outputlist, [$mib, $Monitoring::GLPlugin::SNMP::MibsAndOids::mib_ids->{$mib}]);
        $unknowns = {@{[map {
            $_, $self->rawdata->{$_}
        } grep {
            substr($_, 0, length($Monitoring::GLPlugin::SNMP::MibsAndOids::mib_ids->{$mib})) ne
                $Monitoring::GLPlugin::SNMP::MibsAndOids::mib_ids->{$mib} || (
            substr($_, 0, length($Monitoring::GLPlugin::SNMP::MibsAndOids::mib_ids->{$mib})) eq
                $Monitoring::GLPlugin::SNMP::MibsAndOids::mib_ids->{$mib} &&
            substr($_, length($Monitoring::GLPlugin::SNMP::MibsAndOids::mib_ids->{$mib}), 1) ne ".")
        } keys %{$unknowns}]}};
      }
    }
    my $toplevels = {};
    map {
        /^(1\.\d+\.\d+\.\d+\.\d+\.\d+\.\d+\.\d+)\./ and $toplevels->{$1} = 1; 
        /^(1\.\d+\.\d+\.\d+\.\d+\.\d+\.\d+\.\d+\.\d+\.\d+\.\d+\.\d+)\./ and $toplevels->{$1} = 1; 
    } keys %{$unknowns};
    foreach (sort {$a cmp $b} keys %{$toplevels}) {
      push(@outputlist, ["<unknown>", $_]);
    }
    foreach (sort {$a->[0] cmp $b->[0]} @outputlist) {
      printf "implements %s %s\n", $_->[0], $_->[1];
    }
    $self->add_ok("have fun");
    my ($code, $message) = $self->check_messages(join => ', ', join_all => ', ');
    $Monitoring::GLPlugin::plugin->nagios_exit($code, $message);
  } elsif ($self->mode =~ /device::supportedoids/) {
    my $unknowns = {};
    %{$unknowns} = %{$self->rawdata};
    my $confirmed = {};
    foreach my $mib (keys %{$Monitoring::GLPlugin::SNMP::MibsAndOids::mibs_and_oids}) {
      foreach my $sym (keys %{$Monitoring::GLPlugin::SNMP::MibsAndOids::mibs_and_oids->{$mib}}) {
        my $obj = $self->get_snmp_object($mib, $sym);
        if (defined $obj) {
          my $oid = $Monitoring::GLPlugin::SNMP::MibsAndOids::mibs_and_oids->{$mib}->{$sym};
          if (exists $unknowns->{$oid}) {
            $confirmed->{$oid} = sprintf '%s::%s = %s', $mib, $sym, $obj;
            delete $unknowns->{$oid};
          } elsif (exists $unknowns->{$oid.'.0'}) {
            $confirmed->{$oid.'.0'} = sprintf '%s::%s = %s', $mib, $sym, $obj;
            delete $unknowns->{$oid.'.0'};
          }
        }
        if ($sym =~ /Table$/) {
          if (my @table = $self->get_snmp_table_objects($mib, $sym)) {
            my $oid = $Monitoring::GLPlugin::SNMP::MibsAndOids::mibs_and_oids->{$mib}->{$sym};
            $confirmed->{$oid} = sprintf '%s::%s', $mib, $sym;
            $self->add_rawdata($oid, '--------------------');
            foreach my $line (@table) {
              if ($line->{flat_indices}) {
                foreach my $column (grep !/(flat_indices)|(indices)/, keys %{$line}) {
                  my $oid = $Monitoring::GLPlugin::SNMP::MibsAndOids::mibs_and_oids->{$mib}->{$column};
                  if (exists $unknowns->{$oid.'.'.$line->{flat_indices}}) {
                    $confirmed->{$oid.'.'.$line->{flat_indices}} = 
                        sprintf '%s::%s.%s = %s', $mib, $column, $line->{flat_indices}, $line->{$column};
                    delete $unknowns->{$oid.'.'.$line->{flat_indices}};
                  }
                }
              }
            }
          }
        } elsif ($sym =~ /Table/ and exists $Monitoring::GLPlugin::SNMP::MibsAndOids::mibs_and_oids->{$mib}->{$sym =~ s/Table/Entry/gr}) {
          # fuer die Pappenheimer von QNAP, die nennen ihren Krempel
          # kakaTable und kakaTableEntry
          # oder noch schlauer: systemIfTable und ifEntry
          if (my @table = $self->get_snmp_table_objects($mib, $sym)) {
            my $oid = $Monitoring::GLPlugin::SNMP::MibsAndOids::mibs_and_oids->{$mib}->{$sym};
            $confirmed->{$oid} = sprintf '%s::%s', $mib, $sym;
            $self->add_rawdata($oid, '--------------------');
            foreach my $line (@table) {
              if ($line->{flat_indices}) {
                foreach my $column (grep !/(flat_indices)|(indices)/, keys %{$line}) {
                  my $oid = $Monitoring::GLPlugin::SNMP::MibsAndOids::mibs_and_oids->{$mib}->{$column};
                  if (exists $unknowns->{$oid.'.'.$line->{flat_indices}}) {
                    $confirmed->{$oid.'.'.$line->{flat_indices}} =
                        sprintf '%s::%s.%s = %s', $mib, $column, $line->{flat_indices}, $line->{$column};
                    delete $unknowns->{$oid.'.'.$line->{flat_indices}};
                  }
                }
              }
            }
          }
        }
      }
    }
    my @sortedoids = $self->sort_oids([keys %{$self->rawdata}]);
    foreach (@sortedoids) {
      if (exists $confirmed->{$_}) {
        printf "%s\n", $confirmed->{$_};
      } else {
        printf "%s = %s\n", $_, $unknowns->{$_};
      }
    }
  }
}

sub check_snmp_and_model {
  my ($self) = @_;
  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 -ObentU -v%s -c%s %s 1.3.6.1.4.1 2>&1",
          $self->opts->snmpwalk,
          $self->opts->protocol,
          $self->opts->community,
          $self->opts->hostname;
      open(WALK, "$cmd |");
      while (<WALK>) {
        if (/^([\.\d]+) = .*?: (\-*\d+)/) {
          $response->{$1} = $2;
        } elsif (/^([\.\d]+) = .*?: "(.*?)"/) {
          $response->{$1} = $2;
          $response->{$1} =~ s/\s+$//;
        }
      }
      close WALK;
    } else {
      if (defined $self->opts->offline && $self->opts->mode ne 'walk') {
        if ((time - (stat($self->opts->snmpwalk))[9]) > $self->opts->offline) {
          $self->add_message(UNKNOWN,
              sprintf 'snmpwalk file %s is too old', $self->opts->snmpwalk);
        }
      }
      $self->opts->override_opt('hostname', 'walkhost') if $self->opts->mode ne 'walk';
      my $current_oid = undef;
      my @multiline_string = ();
      open(MESS, $self->opts->snmpwalk);
      while(<MESS>) {
        next if /No Such Object available on this agent at this OID/;
        # 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]+) = STRING: "([^"]*)$/) {
          $current_oid = $1;
          push(@multiline_string, $2);
        } elsif (/^([\d\.]+) = Network Address: (.*)/) {
          $response->{$1} = $2;
        } elsif (/^([\d\.]+) = Hex-STRING: (.*)/) {
          my $k = $1;
          my $h = $2;
          $h =~ s/\s+//g;
          $response->{$k} = unpack("B*", pack('H*', $h));
        } elsif (/^([\d\.]+) = \w+: (\-*\d+)\s*$/) {
          $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+$//;
        } elsif (/^([^"]*)"$/ && @multiline_string && $current_oid) {
          push(@multiline_string, $1);
          $response->{$current_oid} = join("\n", @multiline_string);
          $current_oid = undef;
          @multiline_string = ();
        } elsif (/^"$/ && @multiline_string && $current_oid) {
          $response->{$current_oid} = join("\n", @multiline_string);
          $current_oid = undef;
          @multiline_string = ();
        } elsif (/(.*)/ && @multiline_string && $current_oid) {
          push(@multiline_string, $1);
        }
      }
      close MESS;
    }
    foreach my $oid (keys %$response) {
      if ($oid =~ /^\./) {
        my $nodot = $oid;
        $nodot =~ s/^\.//g;
        $response->{$nodot} = $response->{$oid};
        delete $response->{$oid};
      }
    }
    # Achtung!! Der schnippelt von einem Hex-STRING, der mit 20 aufhoert,
    # das letzte Byte weg. Das muss beruecksichtigt werden, wenn man 
    # spaeter MAC-Adressen o.ae. zueueckrechnet.
    # } elsif ($self->{hwWlanRadioMac} && unpack("H12", $self->{hwWlanRadioMac}." ") =~ /(\w{2})(\w{2})(\w{2})(\w{2})(\w{2})(\w{2})/) {
    # siehe Classes::Huawei::Component::WlanSubsystem::Radio
    map { $response->{$_} =~ s/^\s+//; $response->{$_} =~ s/\s+$//; }
        keys %$response;
    $self->set_rawdata($response);
  } else {
    $self->establish_snmp_session();
  }
  if (! $self->check_messages()) {
    my $tic = time;
    # Datatype TimeTicks = 1/100s
    my $sysUptime = $self->get_snmp_object('MIB-2-MIB', 'sysUpTime', 0);
    # Datatype Integer32 = 1s
    my $snmpEngineTime = $self->get_snmp_object('SNMP-FRAMEWORK-MIB', 'snmpEngineTime');
    # Datatype TimeTicks = 1/100s
    my $hrSystemUptime = $self->get_snmp_object_maybe('HOST-RESOURCES-MIB', 'hrSystemUptime');
    my $sysDescr = $self->get_snmp_object('MIB-2-MIB', 'sysDescr', 0);
    my $tac = time;
    if (defined $hrSystemUptime && $hrSystemUptime =~ /^\d+$/ && $hrSystemUptime > 0) {
      $hrSystemUptime = $self->timeticks($hrSystemUptime);
      $self->debug(sprintf 'hrSystemUptime says: up since: %s / %s',
          scalar localtime (time -  $hrSystemUptime),
          $self->human_timeticks($hrSystemUptime));
    } else {
      $hrSystemUptime = undef;
    }
    if (defined $snmpEngineTime && $snmpEngineTime =~ /^\d+$/ && $snmpEngineTime > 0) {
      $snmpEngineTime = $snmpEngineTime;
      $self->debug(sprintf 'snmpEngineTime says: up since: %s / %s',
          scalar localtime (time - $snmpEngineTime),
          $self->human_timeticks($snmpEngineTime));
    } else {
      # drecksschrott asa liefert negative werte
      # und drecksschrott socomec liefert: wrong type (should be INTEGER): NULL
      $snmpEngineTime = undef;
    }
    if (defined $sysUptime) {
      $sysUptime = $self->timeticks($sysUptime);
      $self->debug(sprintf 'sysUptime says:      up since: %s / %s',
          scalar localtime (time - $sysUptime),
          $self->human_timeticks($sysUptime));
    }
    my $best_uptime = undef;
    if ($hrSystemUptime) {
      # Bei Linux-basierten Geraeten wird snmpEngineTime viel zu haeufig
      # durchgestartet, also lieber das hier.
      $best_uptime = $hrSystemUptime;
      # Es sei denn, snmpEngineTime ist tatsaechlich groesser, dann gilt
      # wiederum dieses. Mag sein, dass der zahlenwert hier manchmal huepft
      # und ein Performancegraph Zacken bekommt, aber das ist mir egal.
      # es geht nicht um Graphen in Form einer ansteigenden Geraden, sondern
      # um das Erkennen von spontanen Reboots und das Vermeiden von
      # falschen Alarmen.
      if ($snmpEngineTime && $snmpEngineTime > $hrSystemUptime) {
        $best_uptime = $snmpEngineTime;
      }
    } elsif ($snmpEngineTime) {
      $best_uptime = $snmpEngineTime;
    } else {
      $best_uptime = $sysUptime;
    }
    if (defined $best_uptime && defined $sysDescr) {
      $self->{uptime} = $best_uptime;
      $self->{productname} = $sysDescr;
      $self->{sysobjectid} = $self->get_snmp_object('MIB-2-MIB', 'sysObjectID', 0);
      $self->debug(sprintf 'uptime: %s', $self->{uptime});
      $self->debug(sprintf 'up since: %s',
          scalar localtime (time - $self->{uptime}));
      $Monitoring::GLPlugin::SNMP::uptime = $self->{uptime};
      $self->debug('whoami: '.$self->{productname});
    } else {
      if ($tac - $tic >= ($Monitoring::GLPlugin::SNMP::session ?
          $Monitoring::GLPlugin::SNMP::session->timeout : $self->opts->timeout())) {
        $self->add_message(UNKNOWN,
            'could not contact snmp agent, timeout during snmp-get sysUptime');
      } elsif ($self->{broken_snmp_agent}) {
        # plugins may add an array of subroutines to their Classes::Device.
        # For example, check_tl_health has to deal with IBM libraries, which
        # do not show sysUptime nor sysDescr nor any other uptime oids.
        # In order to let the plugin continue with a fake uptime, one of
        # the broken_snmp_agent subroutines must return a true value after it
        # has set the uptime to 1 hour and filled out $self->{productname}
        my $mein_lieber_freund_und_kupferstecher = 0;
        foreach my $kriegst_du_die_kurve (@{$self->{broken_snmp_agent}}) {
          if (&$kriegst_du_die_kurve()) {
            $mein_lieber_freund_und_kupferstecher = 1;
            $self->debug(sprintf 'uptime: %s', $self->{uptime});
            $self->debug(sprintf 'up since: %s',
                scalar localtime (time - $self->{uptime}));
            $Monitoring::GLPlugin::SNMP::uptime = $self->{uptime};
            $self->debug('whoami: '.$self->{productname});
            return;
          }
        }
        if (! $mein_lieber_freund_und_kupferstecher) {
          $self->add_message(UNKNOWN,
              'got neither sysUptime and sysDescr nor any other useful information, is this snmp agent working correctly?');
        }
      } else {
        $self->add_message(UNKNOWN,
            'Did not receive both sysUptime and sysDescr, is this snmp agent working correctly?');
      }
      $Monitoring::GLPlugin::SNMP::session->close if $Monitoring::GLPlugin::SNMP::session;
    }
  }
}

sub pretty_sysdesc {
  my ($self, $sysDesc) = @_;
  my $prettySysDescription = undef;
  if (exists $self->{classified_as}) {
    my $now_class = ref($self);
    my $now_pretty_sysdesc = $self->can("pretty_sysdesc");
    bless $self, $self->{classified_as};
    my $classified_pretty_sysdesc = $self->can("pretty_sysdesc");
    if ($now_pretty_sysdesc && $classified_pretty_sysdesc && $now_pretty_sysdesc ne $classified_pretty_sysdesc) {
      $prettySysDescription = $self->pretty_sysdesc($sysDesc);
    } elsif (! $now_pretty_sysdesc && $classified_pretty_sysdesc) {
      $prettySysDescription = $self->pretty_sysdesc($sysDesc);
    }
    bless $self, $now_class;
  }
  return $prettySysDescription ? $prettySysDescription : $sysDesc;
}

sub establish_snmp_session {
  my ($self) = @_;
  $self->set_timeout_alarm();
  if (eval "require Net::SNMP") {
    my %params = ();
    my $net_snmp_version = Net::SNMP->VERSION(); # 5.002000 or 6.000000
    $params{'-translate'} = [ # because we see "NULL" coming from socomec devices
      -all => 0x0,
      -nosuchobject => 1,
      -nosuchinstance => 1,
      -endofmibview => 1,
      -unsigned => 1,
    ];
    $params{'-hostname'} = $self->opts->hostname;
    $params{'-version'} = $self->opts->protocol;
    if ($self->opts->port) {
      $params{'-port'} = $self->opts->port;
    }
    if ($self->opts->domain) {
      $params{'-domain'} = $self->opts->domain;
    }
    $self->v2tov3;
    if ($self->opts->protocol eq '3') {
      $params{'-version'} = $self->opts->protocol;
      $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;
      }
      # context hat in der session nix verloren, sondern wird
      # als zusatzinfo bei den requests mitgeschickt
      #if ($self->opts->contextengineid) {
      #  $params{'-contextengineid'} = $self->opts->contextengineid;
      #}
      #if ($self->opts->contextname) {
      #  $params{'-contextname'} = $self->opts->contextname;
      #}
    } else {
      $params{'-community'} = $self->opts->community;
    }
    # breaks cisco wlc. at least with 15, wlc did not work.
    # removing this at all may cause strange epn errors. As if only
    # certain oids were returned as undef, others not.
    # next try: 50
    $params{'-timeout'} = $self->opts->timeout() >= 60 ?
        50 : $self->opts->timeout() - 2;
    my $stderrvar = "";
    *SAVEERR = *STDERR;
    open ERR ,'>',\$stderrvar;
    *STDERR = *ERR;
    my ($session, $error) = Net::SNMP->session(%params);
    *STDERR = *SAVEERR;
    if (($stderrvar && $error && $error =~ /Time synchronization failed/) ||
        ($error && $error =~ /Received usmStatsUnknownEngineIDs.0 Report-PDU with value \d+ during synchronization/)) {
      # This is what you get when you have
      # - an APC ups with a buggy firmware.
      # - no chance to update it.
      # - a support contract.
      no strict 'refs';
      no warnings 'redefine';
      *{'Net::SNMP::_discovery_synchronization_cb'} = sub {
        my ($this) = @_;
        if ($this->{_security}->discovered())
        {
          $this->_error_clear();
          return $this->_discovery_complete();
        }
        return $this->_discovery_failed();
      };
      ($session, $error) = Net::SNMP->session(%params);
    }
    if (! defined $session && $error && $error =~ /No response from remote host.*during synchronization/) {
      # Before the Oct 2024 Net::SNMP patch, this situation ended up in a
      # timeout and was caught by the alarm handler. With the patch, Net::SNMP
      # returns earlier so that we handle the return here.
      $self->add_message(UNKNOWN,
          sprintf 'cannot create session object: %s', $error);
    } elsif (! 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);
      if ($self->opts->protocol eq "1") {
        $Monitoring::GLPlugin::SNMP::maxrepetitions = 0;
      } else {
        $Monitoring::GLPlugin::SNMP::maxrepetitions = 20;
      }
      $Monitoring::GLPlugin::SNMP::max_msg_size = $max_msg_size;
      $Monitoring::GLPlugin::SNMP::session = $session;
      $Monitoring::GLPlugin::SNMP::session_params = \%params;
    }
  } else {
    $self->add_message(CRITICAL,
        'could not find Net::SNMP module');
  }
}

sub session_translate {
  my ($self, $translation) = @_;
  $Monitoring::GLPlugin::SNMP::session->translate($translation) if
      $Monitoring::GLPlugin::SNMP::session;
}

sub establish_snmp_secondary_session {
  my ($self) = @_;
  if ($self->opts->protocol eq '3' &&
      $self->opts->can('authprotocol2') && (
      defined $self->opts->authprotocol2 ||
      defined $self->opts->authpassword2 ||
      defined $self->opts->privprotocol2 ||
      defined $self->opts->privpassword2 ||
      defined $self->opts->username2 ||
      defined $self->opts->contextengineid2 ||
      defined $self->opts->contextname2
  )) {
    my $relogin = 0;
    # bei --community2="snmpv3,..." wurde alles in xyz2 per override gesteckt
    $relogin = 1 if ! $self->strequal($self->opts->authprotocol,
        $self->opts->authprotocol2);
    $relogin = 1 if ! $self->strequal($self->opts->authpassword,
        $self->opts->authpassword2);
    $relogin = 1 if ! $self->strequal($self->opts->privprotocol,
        $self->opts->privprotocol2);
    $relogin = 1 if ! $self->strequal($self->opts->privpassword,
        $self->opts->privpassword2);
    $relogin = 1 if ! $self->strequal($self->opts->username,
        $self->opts->username2);
    if ($relogin) {
      $Monitoring::GLPlugin::SNMP::session = undef;
      $self->opts->override_opt('authprotocol',
          $self->opts->authprotocol2);
      $self->opts->override_opt('authpassword',
          $self->opts->authpassword2);
      $self->opts->override_opt('privprotocol',
          $self->opts->privprotocol2);
      $self->opts->override_opt('privpassword',
          $self->opts->privpassword2);
      $self->opts->override_opt('username',
          $self->opts->username2);
      $self->establish_snmp_session;
    }
    $self->opts->override_opt('contextengineid',
        $self->opts->contextengineid2);
    $self->opts->override_opt('contextname',
        $self->opts->contextname2);
    return 1;
  } else {
    if (defined $self->opts->community2 &&
        $self->opts->community2 ne
        $self->opts->community) {
      $Monitoring::GLPlugin::SNMP::session = undef;
      $self->opts->override_opt('community',
          $self->opts->community2) ;
      $self->establish_snmp_session;
      return 1;
    }
  }
  return 0;
}

sub reset_snmp_max_msg_size {
  my ($self) = @_;
  $self->debug(sprintf "reset snmp_max_msg_size to %s",
      $Monitoring::GLPlugin::SNMP::max_msg_size) if $Monitoring::GLPlugin::SNMP::session;
  $Monitoring::GLPlugin::SNMP::session->max_msg_size($Monitoring::GLPlugin::SNMP::max_msg_size) if $Monitoring::GLPlugin::SNMP::session;
}

sub mult_snmp_max_msg_size {
  my ($self, $factor) = @_;
  $factor ||= 10;
  $self->debug(sprintf "raise snmp_max_msg_size %d * %d", 
      $factor, $Monitoring::GLPlugin::SNMP::session->max_msg_size()) if $Monitoring::GLPlugin::SNMP::session;
  $Monitoring::GLPlugin::SNMP::session->max_msg_size($factor * $Monitoring::GLPlugin::SNMP::session->max_msg_size()) if $Monitoring::GLPlugin::SNMP::session;
}

sub bulk_baeh_reset {
  my ($self, $maxrepetitions) = @_;
  $self->reset_snmp_max_msg_size();
  $Monitoring::GLPlugin::SNMP::maxrepetitions =
      int($Monitoring::GLPlugin::SNMP::session->max_msg_size() * 0.017) if $Monitoring::GLPlugin::SNMP::session;
}

sub bulk_is_baeh {
  my ($self, $maxrepetitions) = @_;
  $maxrepetitions ||= 1;
  $Monitoring::GLPlugin::SNMP::maxrepetitions = int($maxrepetitions);
}

sub get_max_repetitions {
  my ($self) = @_;
  return $Monitoring::GLPlugin::SNMP::maxrepetitions;
}

sub no_such_model {
  my ($self) = @_;
  printf "Model %s is not implemented\n", $self->{productname};
  exit 3;
}

sub no_such_mode {
  my ($self) = @_;
  if (ref($self) eq "Classes::Generic") {
    $self->init();
  } elsif (ref($self) eq "Classes::Device") {
    $self->add_message(UNKNOWN, 'the device did not implement the mibs this plugin is asking for');
    $self->add_message(UNKNOWN,
        sprintf('unknown device%s', $self->{productname} eq 'unknown' ?
            '' : '('.$self->{productname}.')'));
  } elsif (ref($self) eq "Monitoring::GLPlugin::SNMP") {
    # uptime, offline
    $self->init();
  } else {
    eval {
      if (exists $self->{generic_class}) {
        bless $self, $self->{generic_class};
      } else {
        bless $self, "Classes::Generic";
      }
      $self->init();
    };
    if ($@) {
      if ($@ =~ /Redefine exit to trap plugin exit/) {
        # this message comes from mod_gearman epn.
        # this happens mostly because mode is device::uptime and
        # inside init() in the eval nagios_exit was already called.
        # in the standalone plugin this exits immediately, however inside
        # of an embedded perl (like mod_gearman) there is another eval around
        # the whole plugin code, which catches the exit. whatever happens there
        # it leads to triple output and triple perfdata. (because init() would
        # be called again). No more init, we finish here.
        my ($code, $message) = $self->check_messages(join => ', ', join_all => ', ');
        exit $code;
      }
      bless $self, "Monitoring::GLPlugin::SNMP";
      $self->init();
    }
  }
  if (ref($self) eq "Monitoring::GLPlugin::SNMP") {
    $self->nagios_exit(3,
        sprintf "Mode %s is not implemented for this type of device",
        $self->opts->mode
    );
    exit 3;
  }
}

sub uptime {
  my ($self) = @_;
  return $Monitoring::GLPlugin::SNMP::uptime;
}

sub ago_sysuptime {
  my ($self, $eventtime) = @_;
  # if there is an oid containing the value of sysUptime at the time of
  # a certain event (e.g. cipSecFailTime), this method returns the
  # time in seconds that has passed since the event.
  # sysUptime overflows at 2**32, so it is possible that the eventtime is
  # bigger than sysUptime
  # the eventtime value must come as 1/100s, do not normalize it before.
  #
  # 0-----------------|---------------X
  #                   event=2Mio      sysUptime=5.5Mio
  # event happened (5.5Mio - 2Mio) seconds ago
  #
  # 0-----------------|---------------2**32/0-----------X
  #                   event=2Mio                        sysUptime=100k
  #
  # event happened (100k + (2**32 - 2Mio)) seconds ago
  #
  my $sysUptime = $self->get_snmp_object('MIB-2-MIB', 'sysUpTime', 0);
  if ($eventtime > $sysUptime) {
    return ($sysUptime + (2**32 - $eventtime)) / 100;
  } else {
    return ($sysUptime - $eventtime) / 100;
  }
}

sub map_oid_to_class {
  my ($self, $oid, $class) = @_;
  $Monitoring::GLPlugin::SNMP::MibsAndOids::discover_ids->{$oid} = $class;
}

sub discover_suitable_class {
  my ($self) = @_;
  my $sysobj = $self->get_snmp_object('MIB-2-MIB', 'sysObjectID', 0);
  $sysobj =~ s/^\.//g;
  foreach my $oid (keys %{$Monitoring::GLPlugin::SNMP::MibsAndOids::discover_ids}) {
    if ($sysobj && $oid eq $sysobj) {
      return $Monitoring::GLPlugin::SNMP::MibsAndOids::discover_ids->{$sysobj};
    }
  }
}

sub require_mib {
  my ($self, $mib) = @_;
  my $package = uc $mib;
  $package =~ s/-//g;
  if (exists $Monitoring::GLPlugin::SNMP::MibsAndOids::mibs_and_oids->{$mib} ||
      exists $Monitoring::GLPlugin::SNMP::MibsAndOids::mib_ids->{$mib}) {
    $self->debug("i know package "."Monitoring::GLPlugin::SNMP::MibsAndOids::".$package);
    return;
  } else {
    eval {
      my @oldkeys = ();
      $self->set_variable("verbosity", 2);
      if ($self->get_variable("verbose")) {
        my @oldkeys = exists $Monitoring::GLPlugin::SNMP::MibsAndOids::mibs_and_oids->{$mib} ?
            keys %{$Monitoring::GLPlugin::SNMP::MibsAndOids::mibs_and_oids} : 0;
      }
      $self->debug("load mib "."Monitoring::GLPlugin::SNMP::MibsAndOids::".$package);
      load "Monitoring::GLPlugin::SNMP::MibsAndOids::".$package;
      if ($self->get_variable("verbose")) {
        my @newkeys = exists $Monitoring::GLPlugin::SNMP::MibsAndOids::mibs_and_oids->{$mib} ?
            keys %{$Monitoring::GLPlugin::SNMP::MibsAndOids::mibs_and_oids} : 0;
        $self->debug(sprintf "now i know: %s", join(" ", sort @newkeys));
        $self->debug(sprintf "now i know %d keys.", scalar(@newkeys));
        if (scalar(@newkeys) <= scalar(@oldkeys)) {
          $self->debug(sprintf "from %d to %d keys. why did we load?",
              scalar(@oldkeys), scalar(@newkeys));
        }
      }
    };
    if ($@) {
      $self->debug("failed to load "."Monitoring::GLPlugin::SNMP::MibsAndOids::".$package);
    } else {
      if (exists $Monitoring::GLPlugin::SNMP::MibsAndOids::requirements->{$mib}) {
        foreach my $submib (@{$Monitoring::GLPlugin::SNMP::MibsAndOids::requirements->{$mib}}) {
          $self->require_mib($submib);
        }
      }
    }
  }
}

sub implements_mib {
  my ($self, $mib, $table) = @_;
  $self->require_mib($mib);
  if (! exists $Monitoring::GLPlugin::SNMP::MibsAndOids::mib_ids->{$mib}) {
    return 0;
  }
  if (! $table) {
    my $sysobj = $self->get_snmp_object('MIB-2-MIB', 'sysObjectID', 0);
    $sysobj =~ s/^\.// if $sysobj;
    if ($sysobj && $sysobj eq $Monitoring::GLPlugin::SNMP::MibsAndOids::mib_ids->{$mib}) {
      $self->debug(sprintf "implements %s (sysobj exact)", $mib);
      return 1;
    }
    if ($sysobj && $Monitoring::GLPlugin::SNMP::MibsAndOids::mib_ids->{$mib} eq
        substr $sysobj, 0, length $Monitoring::GLPlugin::SNMP::MibsAndOids::mib_ids->{$mib}) {
      $self->debug(sprintf "implements %s (sysobj)", $mib);
      return 1;
    }
  }

  my $check_oid = $table ?
      $Monitoring::GLPlugin::SNMP::MibsAndOids::mibs_and_oids->{$mib}->{$table} :
      $Monitoring::GLPlugin::SNMP::MibsAndOids::mib_ids->{$mib};

  # some mibs are only composed of tables
  my $traces;
  if ($self->opts->snmpwalk) {
    my @matches;
    # exact match  
    push(@matches, @{[map {
        $_, $self->rawdata->{$_}
    } grep {
        $_ eq $check_oid
    } keys %{$self->rawdata}]});

    # partial match (add trailing dot)  
    $check_oid =~ s/\.?$/./;
    push(@matches, @{[map {
        $_, $self->rawdata->{$_}
    } grep {
        substr($_, 0, length($check_oid)) eq $check_oid
    } keys %{$self->rawdata}]});
    $traces = {@matches};
  } else {
    my %params = (
        -varbindlist => [
            $check_oid
        ]
    );
    if ($Monitoring::GLPlugin::SNMP::session->version() == 3) {
      $params{-contextengineid} = $self->opts->contextengineid if $self->opts->contextengineid;
      $params{-contextname} = $self->opts->contextname if $self->opts->contextname;
    }
    my $timeout = $Monitoring::GLPlugin::SNMP::session->timeout();
    $Monitoring::GLPlugin::SNMP::session->timeout(10);
    $traces = $Monitoring::GLPlugin::SNMP::session->get_next_request(%params);
    $Monitoring::GLPlugin::SNMP::session->timeout($timeout);
  }
  if ($traces && # must find oids following to the ident-oid
      ! exists $traces->{$Monitoring::GLPlugin::SNMP::MibsAndOids::mib_ids->{$mib}} && # must not be the ident-oid
      grep { # following oid is inside this tree
          substr($_, 0, length($check_oid)) eq $check_oid
      } keys %{$traces}) {
    if (exists $traces->{$check_oid} &&
        $traces->{$check_oid} eq "endOfMibView") {
      return 0;
    }
    $self->debug(sprintf "implements %s (found traces)", $mib);
    return 1;
  }
}

sub timeticks {
  my ($self, $timestr) = @_;
  if ($timestr =~ /\((\d+)\)/) {
    # Timeticks: (20718727) 2 days, 9:33:07.27
    $timestr = $1 / 100;
  } elsif ($timestr =~ /(\d+)\s*day[s]*.*?(\d+):(\d+):(\d+)\.(\d+)/) {
    # Timeticks: 2 days, 9:33:07.27
    $timestr = $1 * 24 * 3600 + $2 * 3600 + $3 * 60 + $4;
  } elsif ($timestr =~ /(\d+):(\d+):(\d+):(\d+)\.(\d+)/) {
    # Timeticks: 0001:03:18:42.77
    $timestr = $1 * 3600 * 24 + $2 * 3600 + $3 * 60 + $4;
  } elsif ($timestr =~ /(\d+):(\d+):(\d+)\.(\d+)/) {
    # Timeticks: 9:33:07.27
    $timestr = $1 * 3600 + $2 * 60 + $3;
  } elsif ($timestr =~ /(\d+)\s*hour[s]*.*?(\d+):(\d+)\.(\d+)/) {
    # Timeticks: 3 hours, 42:17.98
    $timestr = $1 * 3600 + $2 * 60 + $3;
  } elsif ($timestr =~ /(\d+)\s*minute[s]*.*?(\d+)\.(\d+)/) {
    # Timeticks: 36 minutes, 01.96
    $timestr = $1 * 60 + $2;
  } elsif ($timestr =~ /(\d+)\.\d+\s*second[s]/) {
    # Timeticks: 01.02 seconds
    $timestr = $1;
  } elsif ($timestr =~ /^(\d+)$/) {
    $timestr = $1 / 100;
  }
  return $timestr;
}

sub human_timeticks {
  my ($self, $timeticks) = @_;
  my $days = int($timeticks / 86400);
  $timeticks -= ($days * 86400);
  my $hours = int($timeticks / 3600);
  $timeticks -= ($hours * 3600);
  my $minutes = int($timeticks / 60);
  my $seconds = $timeticks % 60;
  $days = $days < 1 ? '' : $days .'d ';
  return $days . sprintf "%dh %dm %ds", $hours, $minutes, $seconds;
}

sub internal_name {
  my ($self) = @_;
  my $class = ref($self);
  $class =~ s/^.*:://;
  if (exists $self->{flat_indices}) {
    return sprintf "%s_%s", uc $class, $self->{flat_indices};
  } else {
    return sprintf "%s", uc $class;
  }
}

################################################################
# file-related functions
#
sub create_interface_cache_file {
  my ($self) = @_;
  my $extension = "";
  if ($self->opts->snmpwalk && ! $self->opts->hostname) {
    $self->opts->override_opt('hostname',
        'snmpwalk.file'.md5_hex($self->opts->snmpwalk))
  }
  if ($self->opts->contextname) {
    $extension .= $self->opts->contextname . '_';
  }
  if ($self->opts->community) { 
    $extension .= md5_hex($self->opts->community);
  }
  $extension =~ s/\//_/g;
  $extension =~ s/\(/_/g;
  $extension =~ s/\)/_/g;
  $extension =~ s/\*/_/g;
  $extension =~ s/\s/_/g;
  return sprintf "%s/%s_interface_cache_%s", $self->statefilesdir(),
      $self->opts->hostname, lc $extension;
}

sub create_entry_cache_file {
  my ($self, $mib, $table, $key_attr_id) = @_;
  return lc sprintf "%s_%s_%s_%s_cache",
      $self->create_interface_cache_file(),
      $mib, $table, $key_attr_id;
}

sub update_entry_cache {
  my ($self, $force, $mib, $table, $key_attrs, $last_change) = @_;
  $self->debug(sprintf "last change of %s::%s was %s",
      $mib, $table, scalar localtime $last_change) if $last_change;
  my $update_deadline = time - 3600;
  if ($last_change) {
    # epoch timestamp, which is a hint when some table was last updated
    $update_deadline = ($last_change + 60)
        if $last_change > $update_deadline;
  }
  my $must_update = 0;
  if (ref($key_attrs) ne "ARRAY") {
    if ($key_attrs eq 'flat_indices') {
      # wird nur 1x verwendet bisher, bei OLD-CISCO-INTERFACES-MIB etherstats
      #my $entry = $table =~ s/Table/Entry/gr; # zu neu fuer centos6
      my $entry = $table;
      $entry =~ s/Table/Entry/g;
      my @sortednames = map {
          $_->[0]
      } sort {
          $a->[1] cmp $b->[1]
      } map {
          [$_, join '', map {
              sprintf("%30d", $_)
          } split( /\./, $Monitoring::GLPlugin::SNMP::MibsAndOids::mibs_and_oids->{$mib}->{$_})];
      } grep {
          $Monitoring::GLPlugin::SNMP::MibsAndOids::mibs_and_oids->{$mib}->{$_} =~ /^$Monitoring::GLPlugin::SNMP::MibsAndOids::mibs_and_oids->{$mib}->{$entry}\./;
      } keys %{$Monitoring::GLPlugin::SNMP::MibsAndOids::mibs_and_oids->{$mib}};
      $key_attrs = $sortednames[0];
    }
    $key_attrs = [$key_attrs];
  }
  my $key_attr_id = join('#', @{$key_attrs});
  my $cache = sprintf "%s_%s_%s_cache", $mib, $table, $key_attr_id;
  my $statefile = $self->create_entry_cache_file($mib, $table, $key_attr_id);
  if ($force == -1 && -f $statefile) {
    $must_update = 1;
    # brauchts unter keinen umstaenden.
    # z.b. wenn ein vorhergehender update_interfaces_cache keine aenderungen
    # angezeigt hat, dann spart man sich hier den etherlike-update o.ae.
    $self->debug(sprintf 'skip update of %s %s %s %s cache',
        $self->opts->hostname, $self->opts->mode, $mib, $table);
  } elsif ($force != 0 || ! -f $statefile || ((stat $statefile)[9]) < ($update_deadline)) {
    $must_update = 1;
    $self->debug(sprintf 'force update of %s %s %s %s cache',
        $self->opts->hostname, $self->opts->mode, $mib, $table);
    $self->{$cache} = {};
    foreach my $entry ($self->get_snmp_table_objects($mib, $table, undef, $key_attrs)) {
      # HUAWEI-L2MAM-MIB/hwMacVlanStatisticsTable hat hwMacVlanStatisticsVlanId, welches aber nicht
      # existiert. Id ist nichts anderes als der Index. sub finish() wuerde das Attribut
      # anlegen, aber das ist hier noch nicht gelaufen. $entry->{$_} ist daher undef
      # bei key_attrs==hwMacVlanStatisticsVlanId. Aber was noch geht:
      # Leerstring, dann heißt es ""-//-index => index statt blubb-//-index => index
      # Weil hwMacVlanStatisticsVlanId kein Text ist, sondern der Index, wird das Holen
      # aus dem Cache funktionieren, weil wenn --name numerisch ist, wird mit dem Index
      # aus ""-//-index verglichen und die leere Description ist Wurst.
      my $key = join('#', map { exists $entry->{$_} ? $entry->{$_} : "" } @{$key_attrs});
      my $hash = $key . '-//-' . join('.', @{$entry->{indices}});
      $self->{$cache}->{$hash} = $entry->{indices};
    }
    $self->save_cache($mib, $table, $key_attrs);
  }
  $self->load_cache($mib, $table, $key_attrs);
  return $must_update;
}

sub save_cache {
  my ($self, $mib, $table, $key_attrs) = @_;
  my $cache = sprintf "%s_%s_%s_cache", $mib, $table, join('#', @{$key_attrs});
  $self->create_statefilesdir();
  my $statefile = $self->create_entry_cache_file($mib, $table, join('#', @{$key_attrs}));
  my $tmpfile = $statefile.$$.rand();
  my $fh = IO::File->new();
  if ($fh->open($tmpfile, "w")) {
    my $coder = JSON::XS->new->ascii->pretty->allow_nonref;
    my $jsonscalar = $coder->encode($self->{$cache});
    $fh->print($jsonscalar);
    $fh->flush();
    $fh->close();
  }
  rename $tmpfile, $statefile;
  $self->debug(sprintf "saved %s to %s",
      Data::Dumper::Dumper($self->{$cache}), $statefile);
}

sub load_cache {
  my ($self, $mib, $table, $key_attrs) = @_;
  my $cache = sprintf "%s_%s_%s_cache", $mib, $table, join('#', @{$key_attrs});
  my $statefile = $self->create_entry_cache_file($mib, $table, join('#', @{$key_attrs}));
  $self->{$cache} = {};
  if ( -f $statefile) {
    my $jsonscalar = read_file($statefile);
    our $VAR1;
    eval {
      my $coder = JSON::XS->new->ascii->pretty->allow_nonref;
      $VAR1 = $coder->decode($jsonscalar);
    };
    if($@) {
      $self->debug(sprintf "json load from %s failed. fallback", $statefile);
      delete $INC{$statefile} if exists $INC{$statefile}; # else unit tests fail
      eval "$jsonscalar";
      if($@) {
        printf "FATAL: Could not load cache in perl format!\n";
        $self->debug(sprintf "fallback perl load from %s failed", $statefile);
      }
    }
    $self->debug(sprintf "load %s", Data::Dumper::Dumper($VAR1));
    $self->{$cache} = $VAR1;
  }
}

################################################################
# top-level convenience functions
#
sub get_snmp_objects {
  my ($self, $mib, @mos) = @_;
  foreach (@mos) {
    my $value = $self->get_snmp_object($mib, $_);
    if (defined $value) {
      $self->{$_} = $value;
    }
  }
}

sub get_snmp_tables {
  my ($self, $mib, $infos) = @_;
  foreach my $info (@{$infos}) {
    my $arrayname = $info->[0];
    my $table = $info->[1];
    my $class = $info->[2];
    my $filter = $info->[3];
    my $rows = $info->[4];
    my $key_attr = $info->[5];
    $self->{$arrayname} = [] if ! exists $self->{$arrayname};
    if (! exists $Monitoring::GLPlugin::SNMP::tablecache->{$mib} || ! exists $Monitoring::GLPlugin::SNMP::tablecache->{$mib}->{$table}) {
      $Monitoring::GLPlugin::SNMP::tablecache->{$mib}->{$table} = [];
      foreach ($key_attr ?
          $self->get_snmp_table_objects_with_cache($mib, $table, $key_attr, $rows) :
          $self->get_snmp_table_objects($mib, $table, undef, $rows)) {
        push(@{$Monitoring::GLPlugin::SNMP::tablecache->{$mib}->{$table}}, $_);
        my $new_object = $class->new(%{$_});
        next if (defined $filter && ! &$filter($new_object));
        push(@{$self->{$arrayname}}, $new_object);
      }
    } else {
      $self->debug(sprintf "get_snmp_tables %s %s cache hit", $mib, $table);
      foreach (@{$Monitoring::GLPlugin::SNMP::tablecache->{$mib}->{$table}}) {
        my $new_object = $class->new(%{$_});
        next if (defined $filter && ! &$filter($new_object));
        push(@{$self->{$arrayname}}, $new_object);
      }
    }
  }
}

sub get_snmp_tables_cached {
  my ($self, $mib, $infos, $retention) = @_;
  $retention ||= 3600;
  my $from_file = {};
  foreach my $info (@{$infos}) {
    my $table = $info->[1];
    my $statefile = $self->create_entry_cache_file($mib, $table, "tablecache");
    my @fileinfo = stat($statefile);
    if (@fileinfo and time - $fileinfo[9] < $retention) {
      # exist und recent
      my $cache = sprintf "%s_%s_%s_cache", $mib, $table, "tablecache";
      $self->load_cache($mib, $table, ["tablecache"]);
      if (exists $self->{$cache} and defined $self->{$cache} and
          ((ref($self->{$cache}) eq "HASH" and keys %{$self->{$cache}}) or
           (ref($self->{$cache}) eq "ARRAY" and @{$self->{$cache}}))) {
        $Monitoring::GLPlugin::SNMP::tablecache->{$mib}->{$table} = $self->{$cache};
        $from_file->{$cache} = 1 if exists $self->{$cache};
        $self->debug(sprintf "get_snmp_tables_cached loaded file for %s %s", $mib, $table);
      } else {
        $self->debug(sprintf "get_snmp_tables_cached loaded empty file for %s %s", $mib, $table);
      }
      delete $self->{$cache} if exists $self->{$cache};
    } else {
      $self->debug(sprintf "get_snmp_tables_cached has no (or outdated) file for %s %s", $mib, $table);
    }
  }
  $self->get_snmp_tables($mib, $infos);
  foreach my $info (@{$infos}) {
    my $table = $info->[1];
    if (exists $Monitoring::GLPlugin::SNMP::tablecache->{$mib} and
        exists $Monitoring::GLPlugin::SNMP::tablecache->{$mib}->{$table}) {
      my $cache = sprintf "%s_%s_%s_cache", $mib, $table, "tablecache";
      next if exists $from_file->{$cache};
      $self->{$cache} =
          $Monitoring::GLPlugin::SNMP::tablecache->{$mib}->{$table};
      $self->save_cache($mib, $table, ["tablecache"]);
      delete $self->{$cache};
    }
  }
}

sub merge_tables {
  my ($self, $into, @from) = @_;
  my $into_indices = {};
  map { $into_indices->{$_->{flat_indices}} = $_ } @{$self->{$into}};
  foreach (@from) {
    foreach my $element (@{$self->{$_}}) {
      if (exists $into_indices->{$element->{flat_indices}}) {
        foreach my $key (keys %{$element}) {
          $into_indices->{$element->{flat_indices}}->{$key} = $element->{$key};
        }
      }
    }
    delete $self->{$_};
  }
}

sub merge_tables_with_code {
  my ($self, $into, @from) = @_;
  my $into_indices = {};
  my @to_del = ();
  foreach my $into_elem (@{$self->{$into}}) {
    for (my $i = 0; $i < @from; $i += 2) {
      my ($from_elems, $func) = @from[$i, $i+1];
      foreach my $from_elem (@{$self->{$from_elems}}) {
        if (&$func($into_elem, $from_elem)) {
          foreach my $key (grep !/^(info|trace|warning|critical|blacklisted|extendedinfo|flat_indices|indices)/, sort keys %{$from_elem}) {
            $into_elem->{$key} = $from_elem->{$key};
          }
        }
      }
    }
  }
  for (my $i = 0; $i < @from; $i += 2) {
    my ($from_elems, $func) = @from[$i, $i+1];
    delete $self->{$from_elems};
  }
}

sub mibs_and_oids_definition {
  my ($self, $mib, $definition, @values) = @_;
  if (exists $Monitoring::GLPlugin::SNMP::MibsAndOids::definitions->{$mib} &&
      exists $Monitoring::GLPlugin::SNMP::MibsAndOids::definitions->{$mib}->{$definition}) {
    if (ref($Monitoring::GLPlugin::SNMP::MibsAndOids::definitions->{$mib}->{$definition}) eq "CODE") {
      return $Monitoring::GLPlugin::SNMP::MibsAndOids::definitions->{$mib}->{$definition}->(@values);
    } elsif (ref($Monitoring::GLPlugin::SNMP::MibsAndOids::definitions->{$mib}->{$definition}) eq "HASH") {
      return $Monitoring::GLPlugin::SNMP::MibsAndOids::definitions->{$mib}->{$definition}->{$values[0]};
    }
  } else {
    return "unknown_".$definition;
  }
}

sub clear_table_cache {
  my ($self, $mib, $table) = @_;
  if ($table && exists $Monitoring::GLPlugin::SNMP::tablecache->{$mib}) {
    delete $Monitoring::GLPlugin::SNMP::tablecache->{$mib}->{$table};
  } elsif ($mib) {
    delete $Monitoring::GLPlugin::SNMP::tablecache->{$mib};
  } else {
    $Monitoring::GLPlugin::SNMP::tablecache = {};
  }
}

################################################################
# 2nd level 
#
sub get_snmp_object {
  my ($self, $mib, $mo, $index) = @_;
  $self->require_mib($mib);
  my $oid = $Monitoring::GLPlugin::SNMP::MibsAndOids::mibs_and_oids->{$mib}->{$mo};
  if (defined $oid) {
    my $qoid = $oid.(defined $index ? '.'.$index : '');
    my $response = $self->get_request(-varbindlist => [$qoid]);
    if (defined $response->{$qoid}) {
      if ($response->{$qoid} eq 'noSuchInstance' || $response->{$qoid} eq 'noSuchObject') {
        $response->{$qoid} = undef;
      } elsif (my @symbols = $self->make_symbolic($mib, $response, [[$index]], { $oid => $mo })) {
        $response->{$oid} = $symbols[0]->{$mo};
      }
    }
    $self->debug(sprintf "GET: %s::%s (%s) : %s", $mib, $mo, $oid, defined $response->{$oid} ? $response->{$oid} : "<undef>");
    if (! defined $response->{$oid} && ! defined $index) {
      return $self->get_snmp_object($mib, $mo, 0);
    }
    return $response->{$oid};
  }
  return undef;
}

sub get_snmp_object_maybe {
    my ($self, @args) = @_;
    my $ret;

    # Just do a regular fetch when simulating
    return $self->get_snmp_object(@args) unless defined $Monitoring::GLPlugin::SNMP::session;

    # There may be no response at all. Turn the SNMP timeout down so we can
    # catch that without triggering SIGALRM
    my $orig_timeout = $Monitoring::GLPlugin::SNMP::session->timeout;
    my $new_timeout = $orig_timeout / 10;
    $new_timeout = 5 if $new_timeout > 5;
    $Monitoring::GLPlugin::SNMP::session->timeout($new_timeout);

    # Get
    $ret = $self->get_snmp_object(@args);

    # Restore timeout
    $Monitoring::GLPlugin::SNMP::session->timeout($orig_timeout);

    return $ret;
}

sub get_snmp_table_objects_with_cache {
  my ($self, $mib, $table, $key_attr, $rows, $force) = @_;
  $force ||= 0;
  $self->update_entry_cache($force, $mib, $table, $key_attr);
  my @indices = $self->get_cache_indices($mib, $table, $key_attr);
  my @entries = ();
  foreach ($self->get_snmp_table_objects($mib, $table, \@indices, $rows)) {
    push(@entries, $_);
  }
  return @entries;
}

sub get_table_row_oids {
  my ($self, $mib, $entry, $rows, $sym_lookup) = @_;
  $self->require_mib($mib);
  my $eoid = $Monitoring::GLPlugin::SNMP::MibsAndOids::mibs_and_oids->{$mib}->{$entry}.'.';
  my $eoidlen = length($eoid);
  my @columns = scalar(@{$rows}) ?
  map {
      $sym_lookup->{$_->[1]} = $_->[0];
      $_->[1];
  } grep {
      substr($_->[1], 0, $eoidlen) eq $eoid
  } map {
      [$_, $Monitoring::GLPlugin::SNMP::MibsAndOids::mibs_and_oids->{$mib}->{$_}]
  } @{$rows}
  :
  map {
      $sym_lookup->{$_->[1]} = $_->[0];
      $_->[1];
  } grep {
      substr($_->[1], 0, $eoidlen) eq $eoid
  } map {
      [$_, $Monitoring::GLPlugin::SNMP::MibsAndOids::mibs_and_oids->{$mib}->{$_}]
  } keys %{$Monitoring::GLPlugin::SNMP::MibsAndOids::mibs_and_oids->{$mib}};
  return $self->sort_oids(\@columns);
}

# get_snmp_table_objects('MIB-Name', 'Table-Name', 'Table-Entry', [indices])
# returns array of hashrefs
sub get_snmp_table_objects {
  my ($self, $mib, $table, $indices, $rows) = @_;
  $indices ||= [];
  $rows ||= [];
  $self->require_mib($mib);
  my @entries = ();
  my @augmenting = ();
  my @augmenting_tables = ();
  my $sym_lookup = {};
  $self->debug(sprintf "get_snmp_table_objects %s %s", $mib, $table);
  if ($table =~ /^(.*?)\+(.*)/) {
    $table = $1;
    @augmenting_tables = split(/\+/, $2);
  }
  my $entry = $table;
  $entry =~ s/Table/Entry/g;
  if (! exists $Monitoring::GLPlugin::SNMP::MibsAndOids::mibs_and_oids->{$mib} ||
      ! exists $Monitoring::GLPlugin::SNMP::MibsAndOids::mibs_and_oids->{$mib}->{$table}) {
    return ();
  }
  if (! exists $Monitoring::GLPlugin::SNMP::MibsAndOids::mibs_and_oids->{$mib}->{$entry}) {
    $self->debug(sprintf "table %s::%s has no entry oid", $mib, $table);
    $entry = $table;
    $entry =~ s/Table/TableEntry/g;
    if (! exists $Monitoring::GLPlugin::SNMP::MibsAndOids::mibs_and_oids->{$mib}->{$entry}) {
      return ();
    }
  }
  my $tableoid = $Monitoring::GLPlugin::SNMP::MibsAndOids::mibs_and_oids->{$mib}->{$table};
  my $entryoid = $Monitoring::GLPlugin::SNMP::MibsAndOids::mibs_and_oids->{$mib}->{$entry};
  my $augmenting_tableoid = undef;
  my @columns = $self->get_table_row_oids($mib, $entry, $rows, $sym_lookup);
  my @augmenting_columns = ();
  foreach my $augmenting_table (@augmenting_tables) {
    my $augmenting_entry = $augmenting_table;
    $augmenting_entry =~ s/Table/Entry/g;
    if (! exists $Monitoring::GLPlugin::SNMP::MibsAndOids::mibs_and_oids->{$mib}->{$augmenting_entry}) {
      $self->debug(sprintf "table %s::%s has no entry oid", $mib, $augmenting_table);
      $augmenting_entry = $augmenting_table;
      $augmenting_entry =~ s/Table/TableEntry/g;
      if (! exists $Monitoring::GLPlugin::SNMP::MibsAndOids::mibs_and_oids->{$mib}->{$augmenting_entry}) {
        $augmenting_entry = undef;
      }
    }
    if ($augmenting_entry) {
      $self->debug(sprintf "get_snmp_table_objects augment %s %s with %s", $mib, $table, $augmenting_table);
      @augmenting_columns = $self->get_table_row_oids($mib, $augmenting_entry, $rows, $sym_lookup);
      $augmenting_tableoid = $Monitoring::GLPlugin::SNMP::MibsAndOids::mibs_and_oids->{$mib}->{$augmenting_table};
      push(@augmenting, [$augmenting_tableoid, \@augmenting_columns]);
    }
  }
  if (scalar(@{$indices}) == 1 && $indices->[0] == -1) {
    # get mini-version of a table
    my $result = $self->get_entries(
        -columns => \@columns,
    );
    foreach (@augmenting) {
      my($augmenting_tableoid, @augmenting_columns) = ($_->[0], @{$_->[1]});
      my $augmenting_result = $self->get_entries(
          -columns => \@augmenting_columns,
      );
      while (my ($key, $value) = each %{$augmenting_result}) {
        $result->{$key} = $value;
      }
    }
    my @indices = 
        $self->get_indices(
            -baseoid => $entryoid,
            -oids => [keys %{$result}]);
    @entries = map {
        $_->{indices} = shift @indices; $_
    } @entries = $self->make_symbolic($mib, $result, \@indices, $sym_lookup);
    $self->debug(sprintf "get_snmp_table_objects mini returns %d entries",
        scalar(@entries));
  } elsif (scalar(@{$indices}) == 1) {
    my $index = join('.', @{$indices->[0]});
    my $result = $self->get_entries(
        -startindex => $index,
        -endindex => $index,
        -columns => \@columns,
    );
    foreach (@augmenting) {
      my($augmenting_tableoid, @augmenting_columns) = ($_->[0], @{$_->[1]});
      my $augmenting_result = $self->get_entries(
          -startindex => $index,
          -endindex => $index,
          -columns => \@augmenting_columns,
      );
      while (my ($key, $value) = each %{$augmenting_result}) {
        $result->{$key} = $value;
      }
    }
    @entries = map {
        $_->{indices} = shift @{$indices}; $_
    } $self->make_symbolic($mib, $result, $indices, $sym_lookup);
    $self->debug(sprintf "get_snmp_table_objects single returns %d entries",
        scalar(@entries));
  } elsif (scalar(@{$indices}) > 1) {
    my $result = {};
    my @sortedindices = $self->sort_indices($indices);
    my $startindex = $sortedindices[0];
    my $endindex = $sortedindices[$#sortedindices];
    if (0) {
      # holzweg. dicke ciscos liefern unvollstaendiges resultat, d.h.
      # bei 138,19,157 kommt nur 138..144, dann ist schluss.
      # maxrepetitions bringt nichts.
      #$result = $self->get_entries(
      #    -startindex => $startindex,
      #    -endindex => $endindex,
      #    -columns => \@columns,
      #);
      # anderer ansatz. zusammenhaengende sequenzen suchen und dann
      # mehrere bulkwalks absetzen. bringt aber nichts, wenn die indices
      # wild verstreut sind
      my @sequences = ();
      my $sequence;
      foreach my $idx (0..scalar(@sortedindices)-1) {
        if ($idx != 0 && $sortedindices[$idx] == $sortedindices[$idx-1]+1) {
          push(@{$sequence}, $sortedindices[$idx]);
        } else {
          $sequence = [$sortedindices[$idx]];
          push(@sequences, $sequence);
        }
      }
      foreach my $sequence (@sequences) {
        my $startindex = $sequence->[0];
        my $endindex = $sequence->[-1];
        my $tmp_result = $self->get_entries(
            -startindex => $startindex,
            -endindex => $endindex,
            -columns => \@columns,
        );
        while (my ($key, $value) = each %{$tmp_result}) {
          $result->{$key} = $value;
        }
      }
    } else {
      foreach my $idx (@sortedindices) {
        my $tmp_result = $self->get_entries(
            -startindex => $idx,
            -endindex => $idx,
            -columns => \@columns,
        );
        while (my ($key, $value) = each %{$tmp_result}) {
          $result->{$key} = $value;
        }
      }
    }
    foreach (@augmenting) {
      my($augmenting_tableoid, @augmenting_columns) = ($_->[0], @{$_->[1]});
      foreach my $idx (@sortedindices) {
        my $tmp_result = $self->get_entries(
            -startindex => $idx,
            -endindex => $idx,
            -columns => \@augmenting_columns,
        );
        while (my ($key, $value) = each %{$tmp_result}) {
          $result->{$key} = $value;
        }
      }
    }
    # now we have numerical_oid+index => value
    # needs to become symboic_oid => value
    @entries = map {
        $_->{indices} = shift @{$indices}; $_
    } $self->make_symbolic($mib, $result, $indices, $sym_lookup);
    $self->debug(sprintf "get_snmp_table_objects single returns %d entries",
        scalar(@entries));
  } elsif (scalar(@{$rows})) {
    my $result = $self->get_entries(
        -columns => \@columns,
    );
    foreach (@augmenting) {
      my($augmenting_tableoid, @augmenting_columns) = ($_->[0], @{$_->[1]});
      my $augmenting_result = $self->get_entries(
          -columns => \@augmenting_columns,
      );
      while (my ($key, $value) = each %{$augmenting_result}) {
        $result->{$key} = $value;
      }
    }
    my @indices =
        $self->get_indices(
            -baseoid => $entryoid,
            -oids => [keys %{$result}]);
    @entries = map {
        $_->{indices} = shift @indices; $_
    } $self->make_symbolic($mib, $result, \@indices, $sym_lookup);
    $self->debug(sprintf "get_snmp_table_objects rows returns %d entries",
        scalar(@entries));
  } else {
    my $result = $self->get_table(
        -baseoid => $tableoid,
    );
    foreach (@augmenting) {
      my($augmenting_tableoid, @augmenting_columns) = ($_->[0], @{$_->[1]});
      my $augmenting_result = $self->get_table(
          -baseoid => $augmenting_tableoid,
      );
      while (my ($key, $value) = each %{$augmenting_result}) {
        $result->{$key} = $value;
      }
    }
    # now we have numerical_oid+index => value
    # needs to become symboic_oid => value
    my @indices = 
        $self->get_indices(
            -baseoid => $entryoid,
            -oids => [keys %{$result}]);
    @entries = map {
        $_->{indices} = shift @indices; $_
    } $self->make_symbolic($mib, $result, \@indices, $sym_lookup);
    $self->debug(sprintf "get_snmp_table_objects default returns %d entries",
        scalar(@entries));
  }
  @entries = map { $_->{flat_indices} = join(".", @{$_->{indices}}); $_ } @entries;
  return @entries;
}

################################################################
# 3rd level functions. calling net::snmp-functions
# 
sub get_request {
  my ($self, %params) = @_;
  my @notcached = ();
  foreach my $oid (@{$params{'-varbindlist'}}) {
    $self->add_oidtrace($oid);
    if (! exists $Monitoring::GLPlugin::SNMP::rawdata->{$oid}) {
      push(@notcached, $oid);
    }
  }
  if (! $self->opts->snmpwalk && (scalar(@notcached) > 0)) {
    my %params = ();
    if ($Monitoring::GLPlugin::SNMP::session->version() == 0) {
      $params{-varbindlist} = \@notcached;
    } elsif ($Monitoring::GLPlugin::SNMP::session->version() == 1) {
      $params{-varbindlist} = \@notcached;
      #$params{-nonrepeaters} = scalar(@notcached);
    } elsif ($Monitoring::GLPlugin::SNMP::session->version() == 3) {
      $params{-varbindlist} = \@notcached;
      $params{-contextengineid} = $self->opts->contextengineid if $self->opts->contextengineid;
      $params{-contextname} = $self->opts->contextname if $self->opts->contextname;
    }
    my $result = $Monitoring::GLPlugin::SNMP::session->get_request(%params);
    # so, und jetzt gibts stinkstiefel, die kriegen
    # params{-varbindlist => [1.3.6.1.4.1.318.1.1.1.1.1.1]
    # und result ist
    # { 1.3.6.1.4.1.318.1.1.1.1.1.1.0 => "Smart-UPS RT 10000 XL" }
    # letzteres kommt in raw_data
    # und beim abschliessenden map wirds natuerlich nicht mehr gefunden 
    # also leeres return. <<kraftausdruck>>
    while (my ($key, $value) = each %{$result}) {
      # so, und zwei jahre spaeter kommt man drauf, dass es viele sorten 
      # von stinkstiefeln gibt. die fragt man nach 1.3.6.1.4.1.13885.120.1.3.1
      # und kriegt als antwort 1.3.6.1.4.1.13885.120.1.3.1.0=[noSuchInstance]
      # bis zum 11.10.16 wurde das in den cache geschrieben. eine etage hoeher
      # wird aber dann nach 1.3.6.1.4.1.13885.120.1.3.1.0 gefallbacked, was
      # dann prompt aus dem cache gefischt wird, anstatt den agenten zu fragen,
      # der in diesem fall eine saubere antwort liefern wuerde.
      # ergo: keine fehlermeldungen in den chache
      $self->add_rawdata($key, $value) if defined $value && $value ne 'noSuchInstance';
    }
  }
  my $result = {};
  map {
      $result->{$_} = exists $Monitoring::GLPlugin::SNMP::rawdata->{$_} ?
          $Monitoring::GLPlugin::SNMP::rawdata->{$_} :
      exists $Monitoring::GLPlugin::SNMP::rawdata->{$_.'.0'} ?
          $Monitoring::GLPlugin::SNMP::rawdata->{$_.'.0'} : undef;
  } @{$params{'-varbindlist'}};
  return $result;
}

sub get_entries_get_bulk {
  my ($self, %params) = @_;
  my $result = {};
  my %newparams = ();
  $newparams{'-startindex'} = $params{'-startindex'}
      if defined $params{'-startindex'};
  $newparams{'-endindex'} = $params{'-endindex'}
      if defined $params{'-endindex'};
  $newparams{'-columns'} = $params{'-columns'};
  if ($Monitoring::GLPlugin::SNMP::maxrepetitions) {
    $newparams{'-maxrepetitions'} = $Monitoring::GLPlugin::SNMP::maxrepetitions;
  } else {
    $newparams{'-maxrepetitions'} = 3;
  }
  $self->debug(sprintf "get_entries_get_bulk %s", Data::Dumper::Dumper(\%newparams));
  if ($Monitoring::GLPlugin::SNMP::session->version() == 3) {
    $newparams{-contextengineid} = $self->opts->contextengineid if $self->opts->contextengineid;
    $newparams{-contextname} = $self->opts->contextname if $self->opts->contextname;
  }
  delete $newparams{'-maxrepetitions'}; # bulk howe gsagt!!
  $result = $Monitoring::GLPlugin::SNMP::session->get_entries(%newparams);
  return $result;
}

sub get_entries_get_next {
  my ($self, %params) = @_;
  my $result = {};
  my %newparams = ();
  $newparams{'-maxrepetitions'} = 0;
  $newparams{'-startindex'} = $params{'-startindex'}
      if defined $params{'-startindex'};
  $newparams{'-endindex'} = $params{'-endindex'}
      if defined $params{'-endindex'};
  $newparams{'-columns'} = $params{'-columns'};
  $self->debug(sprintf "get_entries_get_next %s", Data::Dumper::Dumper(\%newparams));
  if ($Monitoring::GLPlugin::SNMP::session->version() == 3) {
    $newparams{-contextengineid} = $self->opts->contextengineid if $self->opts->contextengineid;
    $newparams{-contextname} = $self->opts->contextname if $self->opts->contextname;
  }
  $result = $Monitoring::GLPlugin::SNMP::session->get_entries(%newparams);
  return $result;
}

sub get_entries_get_next_1index {
  my ($self, %params) = @_;
  my $result = {};
  my %newparams = ();
  $newparams{'-startindex'} = $params{'-startindex'}
      if defined $params{'-startindex'};
  $newparams{'-endindex'} = $params{'-endindex'}
      if defined $params{'-endindex'};
  $newparams{'-columns'} = $params{'-columns'};
  $self->debug(sprintf "get_entries_get_next_1index %s", Data::Dumper::Dumper(\%newparams));
  my %singleparams = ();
  $singleparams{'-maxrepetitions'} = 0;
  if ($Monitoring::GLPlugin::SNMP::session->version() == 3) {
    $singleparams{-contextengineid} = $self->opts->contextengineid if $self->opts->contextengineid;
    $singleparams{-contextname} = $self->opts->contextname if $self->opts->contextname;
  }
  foreach my $index ($newparams{'-startindex'}..$newparams{'-endindex'}) {
    foreach my $oid (@{$newparams{'-columns'}}) {
      $singleparams{'-columns'} = [$oid];
      $singleparams{'-startindex'} = $index;
      $singleparams{'-endindex'} =$index;
      my $singleresult = $Monitoring::GLPlugin::SNMP::session->get_entries(%singleparams);
      while (my ($key, $value) = each %{$singleresult}) {
        $result->{$key} = $value;
      }
    }
  }
  return $result;
}

sub get_entries_get_simple {
  my ($self, %params) = @_;
  my $result = {};
  my %newparams = ();
  $newparams{'-startindex'} = $params{'-startindex'}
      if defined $params{'-startindex'};
  $newparams{'-endindex'} = $params{'-endindex'}
      if defined $params{'-endindex'};
  $newparams{'-columns'} = $params{'-columns'};
  $self->debug(sprintf "get_entries_get_simple %s", Data::Dumper::Dumper(\%newparams));
  my %singleparams = ();
  if ($Monitoring::GLPlugin::SNMP::session->version() == 3) {
    $singleparams{-contextengineid} = $self->opts->contextengineid if $self->opts->contextengineid;
    $singleparams{-contextname} = $self->opts->contextname if $self->opts->contextname;
  }
  foreach my $index ($newparams{'-startindex'}..$newparams{'-endindex'}) {
    foreach my $oid (@{$newparams{'-columns'}}) {
      $singleparams{'-varbindlist'} = [$oid.".".$index];
      my $singleresult = $Monitoring::GLPlugin::SNMP::session->get_request(%singleparams);
      while (my ($key, $value) = each %{$singleresult}) {
        if ($value eq "noSuchObject" ||
            $value eq "noSuchInstance" ||
            $value eq "endOfMibView") {
          $result->{$key} = undef;
        } else {
          $result->{$key} = $value;
        }
      }
    }
  }
  return $result;
}

sub get_entries {
  my ($self, %params) = @_;
  # [-startindex]
  # [-endindex]
  # -columns
  $params{'-columns'} = [$self->sort_oids($params{'-columns'})];
  my $result = {};
  $self->debug(sprintf "get_entries %s", Data::Dumper::Dumper(\%params));
  if (! $self->opts->snmpwalk) {
    if (scalar (@{$params{'-columns'}}) < 5 && $params{'-endindex'} && $params{'-startindex'} eq $params{'-endindex'}) {
      $result = $self->get_entries_get_simple(%params);
    } else {
      $result = $self->get_entries_get_bulk(%params);
      if (! $result) {
        $self->debug("bulk failed, retry simple");
        # The message size exceeded the buffer maxMsgSize of (\d+)
        # Message size exceeded buffer maxMsgSize
        if ($Monitoring::GLPlugin::SNMP::session->error() =~ /message size exceeded.*buffer maxMsgSize/i) {
          # old methos. might be no good idea for wan
          #$self->debug(sprintf "buffer exceeded. raise *5 for next try");
          #$self->mult_snmp_max_msg_size(5);
          $self->debug(sprintf "buffer exceeded. lower repetitions for next try");
          $self->bulk_is_baeh($self->get_max_repetitions() * 0.75);
        } elsif ($Monitoring::GLPlugin::SNMP::session->error() =~ /Authentication failure/ && $Monitoring::GLPlugin::SNMP::session->max_msg_size() > 10) {
          $self->debug("A so a Rimbfiech, so a saudumms. Aaf oamol kennda me nimmer");
          # australische Nexus. Bulkwalk mit hochgedrehten max_repetitions (*128)
          # fuehrt so so einem fake Authentication failure
          $self->mult_snmp_max_msg_size(5);
          $result = $self->get_entries(%params);
        } else {
          $self->debug($Monitoring::GLPlugin::SNMP::session->error());
        }
        if ($result) {
          # Rimbfiech fallback was successful
        } elsif (defined $params{'-endindex'} && defined $params{'-startindex'}) {
          $result = $self->get_entries_get_simple(%params);
        } else {
          $result = $self->get_entries_get_next(%params);
        }
      }
    }
    if (! $result) {
      $result = $self->get_entries_get_next(%params);
      if (! $result && defined $params{'-startindex'} && $params{'-startindex'} !~ /\./) {
        # compound indexes cannot continue, as these two methods iterate numerically
        if ($Monitoring::GLPlugin::SNMP::session->error() =~ /tooBig/i) {
          $self->debug(sprintf "answer too big");
          $result = $self->get_entries_get_next_1index(%params);
        }
        if (! $result) {
          $result = $self->get_entries_get_simple(%params);
        }
        if (! $result) {
          $self->debug(sprintf "nutzt nix\n");
        }
      }
    }
    foreach my $key (keys %{$result}) {
      if (substr($key, -1) eq " ") {
        my $value = $result->{$key};
        delete $result->{$key};
        $key =~ s/\s+$//g;
        $result->{$key} = $value;
        #
        # warum?
        #
        # %newparams ist:
        #  '-columns' => [
        #                  '1.3.6.1.2.1.2.2.1.8',
        #                  '1.3.6.1.2.1.2.2.1.13',
        #                  ...
        #                  '1.3.6.1.2.1.2.2.1.16'
        #                ],
        #  '-startindex' => '2',
        #  '-endindex' => '2'
        #
        # und $result ist:
        #  ...
        #  '1.3.6.1.2.1.2.2.1.2.2' => 'Adaptive Security Appliance \'outside\' interface',
        #  '1.3.6.1.2.1.2.2.1.16.2 ' => 4281465004,
        #  '1.3.6.1.2.1.2.2.1.13.2' => 0,
        #  ...
        #
        # stinkstiefel!
        #
      }
      $self->add_rawdata($key, $result->{$key});
    }
  } else {
    my $preresult = $self->get_matching_oids(
        -columns => $params{'-columns'});
    while (my ($key, $value) = each %{$preresult}) {
      $result->{$key} = $value;
    }
    my @sortedkeys = $self->sort_oids([keys %{$result}]);
    my @to_del = ();
    if ($params{'-startindex'}) {
      foreach my $resoid (@sortedkeys) {
        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 lt $params{'-startindex'}) {
                push(@to_del, $oid.'.'.$1);
              }
            }
          }
        }
      }
    }
    if ($params{'-endindex'}) {
      foreach my $resoid (@sortedkeys) {
        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 gt $params{'-endindex'}) {
                push(@to_del, $oid.'.'.$1);
              }
            }
          }
        }
      }
    }
    foreach (@to_del) {
      delete $result->{$_};
    }
  }
  return $result;
}

sub get_entries_by_walk {
  my ($self, %params) = @_;
  if (! $self->opts->snmpwalk) {
    $self->add_ok("if you get this crap working correctly, let me know");
    if ($Monitoring::GLPlugin::SNMP::session->version() == 3) {
      $params{-contextengineid} = $self->opts->contextengineid if $self->opts->contextengineid;
      $params{-contextname} = $self->opts->contextname if $self->opts->contextname;
    }
    $self->debug(sprintf "get_tree %s", Data::Dumper::Dumper(\%params));
    my @baseoids = @{$params{-varbindlist}};
    delete $params{-varbindlist};
    if ($Monitoring::GLPlugin::SNMP::session->version() == 0) {
      foreach my $baseoid (@baseoids) {
        $params{-varbindlist} = [$baseoid];
        while (my $result = $Monitoring::GLPlugin::SNMP::session->get_next_request(%params)) {
          $params{-varbindlist} = [($Monitoring::GLPlugin::SNMP::session->var_bind_names)[0]];
        }
      }
    } else {
      $params{-maxrepetitions} = 200;
      foreach my $baseoid (@baseoids) {
        $params{-varbindlist} = [$baseoid];
        while (my $result = $Monitoring::GLPlugin::SNMP::session->get_bulk_request(%params)) {
          my @names = $Monitoring::GLPlugin::SNMP::session->var_bind_names();
          my @oids = $self->sort_oids(\@names);
          foreach (keys %{$result}) {
            $self->add_rawdata($_, $result->{$_});
          }
          $params{-varbindlist} = [pop @oids];
        }
      }
    }
  } else {
    return $self->get_matching_oids(
        -columns => $params{-varbindlist});
  }
}

sub get_table {
  my ($self, %params) = @_;
  my $tic;
  my $tac;
  $self->add_oidtrace($params{'-baseoid'});
  if (! $self->opts->snmpwalk) {
    my @notcached = ();
    if ($Monitoring::GLPlugin::SNMP::session->version() == 3) {
      $params{-contextengineid} = $self->opts->contextengineid if $self->opts->contextengineid;
      $params{-contextname} = $self->opts->contextname if $self->opts->contextname;
    }
    if ($Monitoring::GLPlugin::SNMP::maxrepetitions) {
      # some devices (Bintec) don't like bulk-requests. They call bulk_is_baeh(), so
      # we immediately send get-next
      $params{'-maxrepetitions'} = $Monitoring::GLPlugin::SNMP::maxrepetitions;
    }
    $self->debug(sprintf "get_table %s", Data::Dumper::Dumper(\%params));
    $tic = time;
    my $result = $Monitoring::GLPlugin::SNMP::session->get_table(%params);
    $tac = time;
    if (! defined $result || (defined $result && ! %{$result})) {
      $self->debug(sprintf "get_table error: %s", 
          $Monitoring::GLPlugin::SNMP::session->error());
      my $fallback = 0;
      if ($Monitoring::GLPlugin::SNMP::session->error() =~ /message size exceeded.*buffer maxMsgSize/i) {
        # bei irrsinnigen maxrepetitions
        $self->debug(sprintf "buffer exceeded");
        #$self->reset_snmp_max_msg_size();
        if ($params{'-maxrepetitions'}) {
          $params{'-maxrepetitions'} = int($params{'-maxrepetitions'} / 2);
          $self->debug(sprintf "reduce maxrepetitions to %d",
              $params{'-maxrepetitions'});
        } else {
          $self->mult_snmp_max_msg_size(2);
        }
        $fallback = 1;
      } elsif ($Monitoring::GLPlugin::SNMP::session->error() =~ /No response from remote host/i) {
        # some agents can not handle big loads
        if ($params{'-maxrepetitions'}) {
          $params{'-maxrepetitions'} = int($params{'-maxrepetitions'} / 2);
          $self->debug(sprintf "reduce maxrepetitions to %d",
              $params{'-maxrepetitions'});
          $fallback = 1;
        }
      } elsif ($Monitoring::GLPlugin::SNMP::session->error() =~ /Received tooBig/i) {
        # some agents can not handle big loads
        if ($params{'-maxrepetitions'}) {
          $params{'-maxrepetitions'} = int($params{'-maxrepetitions'} / 4) + 1;
          $self->debug(sprintf "toobig reduce maxrepetitions to %d",
              $params{'-maxrepetitions'});
          $fallback = 1;
        }
      }
      if ($fallback) {
        $self->debug("get_table error: try fallback");
        $self->debug(sprintf "get_table %s", Data::Dumper::Dumper(\%params));
        $tic = time;
        $result = $Monitoring::GLPlugin::SNMP::session->get_table(%params);
        $tac = time;
        $self->debug(sprintf "get_table returned %d oids in %ds", scalar(keys %{$result}), $tac - $tic);
      }
      if (! defined $result || ! %{$result}) {
        $self->debug(sprintf "get_table error: %s", 
            $Monitoring::GLPlugin::SNMP::session->error());
        if (exists $params{'-maxrepetitions'} && $params{'-maxrepetitions'} > 1) {
          $params{'-maxrepetitions'} = 1;
          $self->debug("get_table error: try getnext fallback");
          $self->debug(sprintf "get_table %s", Data::Dumper::Dumper(\%params));
          $tic = time;
          $result = $Monitoring::GLPlugin::SNMP::session->get_table(%params);
          $tac = time;
          $self->debug(sprintf "get_table returned %d oids in %ds", scalar(keys %{$result}), $tac - $tic);
        }
        if (! defined $result || ! %{$result}) {
          $self->debug("get_table error: no more fallbacks. Try --protocol 1");
        }
      }
    }
    $result = {} if ! defined $result;
    # Drecksstinkstiefel Net::SNMP
    # '1.3.6.1.2.1.2.2.1.22.4 ' => 'endOfMibView',
    # '1.3.6.1.2.1.2.2.1.22.4' => '0.0',
    my $num_rows = 0;
    my @blank_keys = ();
    while (my ($key, $value) = each %{$result}) {
      $num_rows++;
      if (substr($key, -1) eq " ") {
        push(@blank_keys, $key);
      } else {
        $self->add_rawdata($key, $value);
      }
    }
    $self->debug(sprintf "get_table returned %d oids in %ds", $num_rows, $tac - $tic);
    foreach my $key (@blank_keys) {
      my $value = $result->{$key};
      delete $result->{$key};
      (my $shortkey = $key) =~ s/\s+$//g;
      if (! exists $result->{shortkey}) {
        $self->add_rawdata($shortkey, $value);
      }
    }
  }
  return $self->get_matching_oids(
      -columns => [$params{'-baseoid'}]);
}

################################################################
# helper functions
# 
sub valid_response {
  my ($self, $mib, $mo, $index) = @_;
  $self->require_mib($mib);
  if (exists $Monitoring::GLPlugin::SNMP::MibsAndOids::mibs_and_oids->{$mib} &&
      exists $Monitoring::GLPlugin::SNMP::MibsAndOids::mibs_and_oids->{$mib}->{$mo}) {
    # make it numerical
    my $oid = $Monitoring::GLPlugin::SNMP::MibsAndOids::mibs_and_oids->{$mib}->{$mo};
    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') {
      $self->debug(sprintf "GET: %s::%s (%s) : %s", $mib, $mo, $oid, defined $result->{$oid} ? $result->{$oid} : "<undef>");
      return undef;
    } else {
      $self->debug(sprintf "GET: %s::%s (%s) : %s", $mib, $mo, $oid, defined $result->{$oid} ? $result->{$oid} : "<undef>");
      $self->add_rawdata($oid, $result->{$oid});
      return $result->{$oid};
    }
  } else {
    return undef;
  }
}

sub get_symbol {
  my ($self, $mib, $oid) = @_;
  # "LIEBERT-GP-ENVIRONMENTAL-MIB", "1.3.6.1.4.1.476.1.42.3.4.1.1.4"
  # dahinter steckt
  # lgpEnvSupplyAirTemperature => '1.3.6.1.4.1.476.1.42.3.4.1.1.3'
  # lgpAmbientTemperature => '1.3.6.1.4.1.476.1.42.3.4.1.1.4'
  # und als info vom geraet
  # LgpEnvTemperatureMeasurementDegC = '1.3.6.1.4.1.476.1.42.3.4.1.1.4'
  # der name des temp. sensor wird ueber die oid mitgeteilt
  $self->require_mib($mib);
  $oid =~ s/^\.//g;
  foreach my $symoid
      (keys %{$Monitoring::GLPlugin::SNMP::MibsAndOids::mibs_and_oids->{$mib}}) {
    if ($oid eq $Monitoring::GLPlugin::SNMP::MibsAndOids::mibs_and_oids->{$mib}->{$symoid}) {
      return $symoid;
    }
  }
  return undef;
}

# 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, $mib, $result, $indices, $sym_lookup) = @_;
  $self->require_mib($mib);
  my @entries = ();
  if (! wantarray && ref(\$result) eq "SCALAR" && ref(\$indices) eq "SCALAR") {
    # $self->make_symbolic('CISCO-IETF-NAT-MIB', 'cnatProtocolStatsName', $self->{cnatProtocolStatsName});
    my $oid = $Monitoring::GLPlugin::SNMP::MibsAndOids::mibs_and_oids->{$mib}->{$result};
    $result = { $oid => $self->{$result} };
    $indices = [[]];
  }
  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
    if (! $sym_lookup) {
      foreach my $symoid
          (keys %{$Monitoring::GLPlugin::SNMP::MibsAndOids::mibs_and_oids->{$mib}}) {
        my $oid = $Monitoring::GLPlugin::SNMP::MibsAndOids::mibs_and_oids->{$mib}->{$symoid};
        if (ref($oid) ne 'HASH') {
          my $fulloid = $oid . '.'.$idx;
          if (exists $result->{$fulloid}) {
            if (exists $Monitoring::GLPlugin::SNMP::MibsAndOids::mibs_and_oids->{$mib}->{$symoid.'Definition'}) {
              if (ref($Monitoring::GLPlugin::SNMP::MibsAndOids::mibs_and_oids->{$mib}->{$symoid.'Definition'}) eq 'HASH') {
                if (exists $Monitoring::GLPlugin::SNMP::MibsAndOids::mibs_and_oids->{$mib}->{$symoid.'Definition'}->{$result->{$fulloid}}) {
                  $mo->{$symoid} = $Monitoring::GLPlugin::SNMP::MibsAndOids::mibs_and_oids->{$mib}->{$symoid.'Definition'}->{$result->{$fulloid}};
                } else {
                  $mo->{$symoid} = 'unknown_'.$result->{$fulloid};
                }
              } elsif ($Monitoring::GLPlugin::SNMP::MibsAndOids::mibs_and_oids->{$mib}->{$symoid.'Definition'} =~ /^OID::(.*)/) {
                my $othermib = $1;
                if (! exists $Monitoring::GLPlugin::SNMP::MibsAndOids::definitions->{$othermib}) {
                  # may point to another mib's definitions, which hasn't
                  # been used yet.
                  $self->require_mib($othermib);
                }
                my $value_which_is_a_oid = $result->{$fulloid};
                $value_which_is_a_oid =~ s/^\.//g;
                my @result = grep { $Monitoring::GLPlugin::SNMP::MibsAndOids::mibs_and_oids->{$othermib}->{$_} eq $value_which_is_a_oid } keys %{$Monitoring::GLPlugin::SNMP::MibsAndOids::mibs_and_oids->{$othermib}};
                if (scalar(@result)) {
                  $mo->{$symoid} = $result[0];
                } else {
                  $mo->{$symoid} = 'unknown_'.$result->{$fulloid};
                }
              } elsif ($Monitoring::GLPlugin::SNMP::MibsAndOids::mibs_and_oids->{$mib}->{$symoid.'Definition'} =~ /^(.*?)::(.*)/) {
                my $mib = $1;
                my $definition = $2;
                my $parameters = undef;
                if ($definition =~ /(.*)\((.*)\)/) {
                  $definition = $1;
                  $parameters = $2;
                }
                if (! exists $Monitoring::GLPlugin::SNMP::MibsAndOids::definitions->{$mib}) {
                  # may point to another mib's definitions, which hasn't
                  # been used yet.
                  $self->require_mib($mib);
                }
                if  (exists $Monitoring::GLPlugin::SNMP::MibsAndOids::definitions->{$mib} &&
                    exists $Monitoring::GLPlugin::SNMP::MibsAndOids::definitions->{$mib}->{$definition} &&
                    ref($Monitoring::GLPlugin::SNMP::MibsAndOids::definitions->{$mib}->{$definition}) eq 'CODE') {
                  if ($parameters) {
                    my @args = ($result->{$fulloid});
                    foreach my $parameter (split(",", $parameters)) {
                      if (! exists $mo->{$parameter}) {
                        # this happens if there are two isolated get_snmp_object calls, one for
                        # cLHaPeerIpAddressType and one for cLHaPeerIpAddress where the latter needs
                        # the symbolized value of the first. we are inside this index-loop because
                        # both have this usual extra .0 although this is not a table row.
                        # if this were a table row, $mo would know cLHaPeerIpAddressType.
                        # there's a chance that $self got cLHaPeerIpAddressType in a previous call
                        # to make_symbolic
                        if (@{$indices} and scalar(@{$indices}) == 1 and ! $indices->[0]->[0]) {
                          $mo->{$parameter} = $self->{$parameter};
                        }
                      }
                      push(@args, $mo->{$parameter});
                    }
                    $mo->{$symoid} = $Monitoring::GLPlugin::SNMP::MibsAndOids::definitions->{$mib}->{$definition}->(@args);
                  } else {
                    $mo->{$symoid} = $Monitoring::GLPlugin::SNMP::MibsAndOids::definitions->{$mib}->{$definition}->($result->{$fulloid});
                  }
                } elsif  (exists $Monitoring::GLPlugin::SNMP::MibsAndOids::definitions->{$mib} &&
                    exists $Monitoring::GLPlugin::SNMP::MibsAndOids::definitions->{$mib}->{$definition} &&
                    ref($Monitoring::GLPlugin::SNMP::MibsAndOids::definitions->{$mib}->{$definition}) eq 'HASH' &&
                    # weil am 27.3.24 so ein Drecksticket reinkam und bei
                    # einem Cisco link-aggregations-Gedoens eins der Interfaces
                    # ifLinkUpDownTrapEnable=<undefined> lieferte.
                    # Drum muss man extra nochmal kontrollieren, ob das ein
                    # gueltiger Wert ist, den man im Def-Hash suchen kann.
                    # Wieder ein halber Vormittag im Arsch wegen so einem Dreck.
                    defined $result->{$fulloid} && \
                    exists $Monitoring::GLPlugin::SNMP::MibsAndOids::definitions->{$mib}->{$definition}->{$result->{$fulloid}}) {
                  $mo->{$symoid} = $Monitoring::GLPlugin::SNMP::MibsAndOids::definitions->{$mib}->{$definition}->{$result->{$fulloid}};
                } else {
                  $mo->{$symoid} = 'unknown_'.(defined $result->{$fulloid} ?
                      $result->{$fulloid} : '<undef>');
                }
              } else {
                $mo->{$symoid} = 'unknown_'.$result->{$fulloid};
                # oder $Monitoring::GLPlugin::SNMP::MibsAndOids::mibs_and_oids->{$mib}->{$symoid.'Definition'}?
              }
            } else {
              $mo->{$symoid} = $result->{$fulloid};
            }
          }
        }
      }
    } else {
      my @sym_lookup_keys = $self->sort_oids([keys %{$sym_lookup}]);
      foreach my $oid (@sym_lookup_keys) {
        if (ref($oid) ne 'HASH') {
          my $fulloid = $oid . '.'.$idx;
          my $symoid = $sym_lookup->{$oid};
          if (exists $result->{$fulloid}) {
            if (exists $Monitoring::GLPlugin::SNMP::MibsAndOids::mibs_and_oids->{$mib}->{$symoid.'Definition'}) {
              if (ref($Monitoring::GLPlugin::SNMP::MibsAndOids::mibs_and_oids->{$mib}->{$symoid.'Definition'}) eq 'HASH') {
                if (exists $Monitoring::GLPlugin::SNMP::MibsAndOids::mibs_and_oids->{$mib}->{$symoid.'Definition'}->{$result->{$fulloid}}) {
                  $mo->{$symoid} = $Monitoring::GLPlugin::SNMP::MibsAndOids::mibs_and_oids->{$mib}->{$symoid.'Definition'}->{$result->{$fulloid}};
                } else {
                  $mo->{$symoid} = 'unknown_'.$result->{$fulloid};
                }
              } elsif ($Monitoring::GLPlugin::SNMP::MibsAndOids::mibs_and_oids->{$mib}->{$symoid.'Definition'} =~ /^OID::(.*)/) {
                my $othermib = $1;
                if (! exists $Monitoring::GLPlugin::SNMP::MibsAndOids::definitions->{$othermib}) {
                  # may point to another mib's definitions, which hasn't
                  # been used yet.
                  $self->require_mib($othermib);
                }
                my $value_which_is_a_oid = $result->{$fulloid};
                $value_which_is_a_oid =~ s/^\.//g;
                my @result = grep { $Monitoring::GLPlugin::SNMP::MibsAndOids::mibs_and_oids->{$othermib}->{$_} eq $value_which_is_a_oid } keys %{$Monitoring::GLPlugin::SNMP::MibsAndOids::mibs_and_oids->{$othermib}};
                if (scalar(@result)) {
                  $mo->{$symoid} = $result[0];
                } else {
                  $mo->{$symoid} = 'unknown_'.$result->{$fulloid};
                }
              } elsif ($Monitoring::GLPlugin::SNMP::MibsAndOids::mibs_and_oids->{$mib}->{$symoid.'Definition'} =~ /^(.*?)::(.*)/) {
                my $mib = $1;
                my $definition = $2;
                my $parameters = undef;
                if ($definition =~ /(.*)\((.*)\)/) {
                  $definition = $1;
                  $parameters = $2;
                }
                if (! exists $Monitoring::GLPlugin::SNMP::MibsAndOids::definitions->{$mib}) {
                  # may point to another mib's definitions, which hasn't
                  # been used yet.
                  $self->require_mib($mib);
                }
                if  (exists $Monitoring::GLPlugin::SNMP::MibsAndOids::definitions->{$mib} &&
                    exists $Monitoring::GLPlugin::SNMP::MibsAndOids::definitions->{$mib}->{$definition} &&
                    ref($Monitoring::GLPlugin::SNMP::MibsAndOids::definitions->{$mib}->{$definition}) eq 'CODE') {
                  if ($parameters) {
                    my @args = ($result->{$fulloid});
                    foreach my $parameter (split(",", $parameters)) {
                      if (! exists $mo->{$parameter}) {
                        if (@{$indices} and scalar(@{$indices}) == 1 and ! $indices->[0]->[0]) {
                          $mo->{$parameter} = $self->{$parameter};
                        }
                      }
                      push(@args, $mo->{$parameter});
                    }
                    $mo->{$symoid} = $Monitoring::GLPlugin::SNMP::MibsAndOids::definitions->{$mib}->{$definition}->(@args);
                  } else {
                    $mo->{$symoid} = $Monitoring::GLPlugin::SNMP::MibsAndOids::definitions->{$mib}->{$definition}->($result->{$fulloid});
                  }
                } elsif  (exists $Monitoring::GLPlugin::SNMP::MibsAndOids::definitions->{$mib} &&
                    exists $Monitoring::GLPlugin::SNMP::MibsAndOids::definitions->{$mib}->{$definition} &&
                    ref($Monitoring::GLPlugin::SNMP::MibsAndOids::definitions->{$mib}->{$definition}) eq 'HASH' &&
                    exists $Monitoring::GLPlugin::SNMP::MibsAndOids::definitions->{$mib}->{$definition}->{$result->{$fulloid}}) {
                  $mo->{$symoid} = $Monitoring::GLPlugin::SNMP::MibsAndOids::definitions->{$mib}->{$definition}->{$result->{$fulloid}};
                } else {
                  $mo->{$symoid} = 'unknown_'.$result->{$fulloid};
                }
              } else {
                $mo->{$symoid} = 'unknown_'.$result->{$fulloid};
                # oder $Monitoring::GLPlugin::SNMP::MibsAndOids::mibs_and_oids->{$mib}->{$symoid.'Definition'}?
              }
            } else {
              $mo->{$symoid} = $result->{$fulloid};
            }
          }
        }
      }
    }
    push(@entries, $mo);
  }
  if (@{$indices} and scalar(@{$indices}) == 1 and !defined $indices->[0]->[0]) {
    my $mo = {};
    my @lookup_keys = ();
    if (! $sym_lookup) {
      @lookup_keys = keys %{$Monitoring::GLPlugin::SNMP::MibsAndOids::mibs_and_oids->{$mib}};
    } else {
      @lookup_keys = values %{$sym_lookup};
    }
    foreach my $symoid (@lookup_keys) {
      my $oid = $Monitoring::GLPlugin::SNMP::MibsAndOids::mibs_and_oids->{$mib}->{$symoid};
      if (ref($oid) ne 'HASH') {
        if (exists $result->{$oid}) {
          if (exists $Monitoring::GLPlugin::SNMP::MibsAndOids::mibs_and_oids->{$mib}->{$symoid.'Definition'}) {
            if (ref($Monitoring::GLPlugin::SNMP::MibsAndOids::mibs_and_oids->{$mib}->{$symoid.'Definition'}) eq 'HASH') {
              if (exists $Monitoring::GLPlugin::SNMP::MibsAndOids::mibs_and_oids->{$mib}->{$symoid.'Definition'}->{$result->{$oid}}) {
                $mo->{$symoid} = $Monitoring::GLPlugin::SNMP::MibsAndOids::mibs_and_oids->{$mib}->{$symoid.'Definition'}->{$result->{$oid}};
                push(@entries, $mo);
              }
            } elsif ($Monitoring::GLPlugin::SNMP::MibsAndOids::mibs_and_oids->{$mib}->{$symoid.'Definition'} =~ /^(.*?)::(.*)/) {
              my $mib = $1;
              my $definition = $2;
              my $parameters = undef;
              if ($definition =~ /(.*)\((.*)\)/) {
                $definition = $1;
                $parameters = $2;
              }
              if  (exists $Monitoring::GLPlugin::SNMP::MibsAndOids::definitions->{$mib} &&
                  exists $Monitoring::GLPlugin::SNMP::MibsAndOids::definitions->{$mib}->{$definition} &&
                  ref($Monitoring::GLPlugin::SNMP::MibsAndOids::definitions->{$mib}->{$definition}) eq 'CODE') {
                if ($parameters) {
                  # we come here fo resolve single oids, so $mo is always initialized new here.
                  # there's a chance that $self->{$parameters} was queried in a previous call
                  if (! exists $mo->{$parameters}) {
                    $mo->{$parameters} = $self->{$parameters};
                  }
                  $mo->{$symoid} = $Monitoring::GLPlugin::SNMP::MibsAndOids::definitions->{$mib}->{$definition}->($result->{$oid}, $mo->{$parameters});
                } else {
                  $mo->{$symoid} = $Monitoring::GLPlugin::SNMP::MibsAndOids::definitions->{$mib}->{$definition}->($result->{$oid});
                }
              } elsif  (exists $Monitoring::GLPlugin::SNMP::MibsAndOids::definitions->{$mib} &&
                  exists $Monitoring::GLPlugin::SNMP::MibsAndOids::definitions->{$mib}->{$definition} &&
                  ref($Monitoring::GLPlugin::SNMP::MibsAndOids::definitions->{$mib}->{$definition}) eq 'HASH' &&
                  exists $Monitoring::GLPlugin::SNMP::MibsAndOids::definitions->{$mib}->{$definition}->{$result->{$oid}}) {
                $mo->{$symoid} = $Monitoring::GLPlugin::SNMP::MibsAndOids::definitions->{$mib}->{$definition}->{$result->{$oid}};
              } else {
                $mo->{$symoid} = 'unknown_'.$result->{$oid};
              }
            } else {
              $mo->{$symoid} = 'unknown_'.$result->{$oid};
              # oder $Monitoring::GLPlugin::SNMP::MibsAndOids::mibs_and_oids->{$mib}->{$symoid.'Definition'}?
            }
          } else {
            $mo->{$symoid} = $result->{$oid};
          }
        }
      }
    }
    push(@entries, $mo) if keys %{$mo};
  }
  if (wantarray) {
    return @entries;
  } else {
    foreach my $entry (@entries) {
      foreach my $key (keys %{$entry}) {
        $self->{$key} = $entry->{$key};
      }
    }
  }
}

sub sort_oids {
  my ($self, $oids) = @_;
  $oids ||= [];
  my @sortedkeys = map { $_->[0] }
      sort { $a->[1] cmp $b->[1] }
          map { [$_,
                  join '', map { sprintf("%30d",$_) } split( /\./, $_)
                ] } @{$oids};
  return @sortedkeys;
}

sub sort_indices {
  my ($self, $indices) = @_;
  my @sortedindices = map { $_->[0] } 
      sort { $a->[1] cmp $b->[1] } 
          map { [$_, 
              join '', map { sprintf("%30d",$_) } split( /\./, $_) 
          ] } map { join('.', @{$_})} @{$indices}; 
  return @sortedindices;
}


sub get_matching_oids {
  my ($self, %params) = @_;
  my $result = {};
  $self->debug(sprintf "get_matching_oids %s", Data::Dumper::Dumper(\%params));
  foreach my $oid (@{$params{'-columns'}}) {
    while (my ($key, $value) = each %{$Monitoring::GLPlugin::SNMP::rawdata}) {
      # oid 1.3.6.1.4.1.9999.12.3.4
      # raw 1.3.6.1.4.1.9999.12.3.4.2.3
      # raw 1.3.6.1.4.1.9999.12.3.41.10.2
      # raw 1.3.6.1.4.1.9999.12.3.4
      if (rindex($key, $oid, 0) == 0) {
        my $len = length($oid);
        if ($len == length($key)) {
          $result->{$key} = $value;
        } elsif (substr($key, $len, 1) eq ".") {
          $result->{$key} = $value;
        }
      }
    }
    #my $oidpattern = $oid;
    #$oidpattern =~ s/\./\\./g;
    #map { $result->{$_} = $Monitoring::GLPlugin::SNMP::rawdata->{$_} }
    #    grep /^$oidpattern(?=\.|$)/, keys %{$Monitoring::GLPlugin::SNMP::rawdata};
  }
  $self->debug(sprintf "get_matching_oids returns %d from %d oids", 
      scalar(keys %{$result}), scalar(keys %{$Monitoring::GLPlugin::SNMP::rawdata}));
  return $result;
}

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

# 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, $indexlists, @element) = @_;
  # $indexlists = zeiger auf array aus [1, 2]
  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;
}

################################################################
# caching functions
# 
sub set_rawdata {
  my ($self, $rawdata) = @_;
  $Monitoring::GLPlugin::SNMP::rawdata = $rawdata;
}

sub add_rawdata {
  my ($self, $oid, $value) = @_;
  $Monitoring::GLPlugin::SNMP::rawdata->{$oid} = $value;
}

sub rawdata {
  my ($self) = @_;
  return $Monitoring::GLPlugin::SNMP::rawdata;
}

sub add_oidtrace {
  my ($self, $oid) = @_;
  $self->debug("cache: ".$oid);
  push(@{$Monitoring::GLPlugin::SNMP::oidtrace}, $oid);
}

#  $self->update_entry_cache(0, $mib, $table, $key_attr);
#  my @indices = $self->get_cache_indices();
sub get_cache_indices {
  my ($self, $mib, $table, $key_attr) = @_;
  # get_cache_indices is only used by get_snmp_table_objects_with_cache
  # so if we dont use --name returning all the indices would result
  # in a step-by-step get_table_objecs(index 1...n) which could take long
  # time when used with nexus or f5 pools
  # returning () forces get_snmp_table_objects to use get_tables
  return () if ! $self->opts->name;
  if (ref($key_attr) ne "ARRAY") {
    $key_attr = [$key_attr];
  }
  my $cache = sprintf "%s_%s_%s_cache", 
      $mib, $table, join('#', @{$key_attr});
  my @indices = ();
  foreach my $key (keys %{$self->{$cache}}) {
    my ($descr, $index) = split('-//-', $key, 2);
    if ($self->opts->name) {
      if ($self->opts->regexp) {
        my $pattern = $self->opts->name;
        if ($descr =~ /$pattern/i) {
          push(@indices, $self->{$cache}->{$key});
        }
      } else {
        if ($self->opts->name =~ /^\d+$/) {
          if ($index == 1 * $self->opts->name) {
            push(@indices, [1 * $self->opts->name]);
          }
        } else {
          if (lc $descr eq lc $self->opts->name) {
            push(@indices, $self->{$cache}->{$key});
          }
        }
      }
    } else {
      push(@indices, $self->{$cache}->{$key});
    }
  }
  return @indices;
  return map { join('.', ref($_) eq "ARRAY" ? @{$_} : $_) } @indices;
}

sub get_cache_indices_by_value {
  # we have a table like
  #  [TABLEITEM_40 in dot1dBasePortTable]
  #  dot1dBasePort: 40 (-> index in dot1qPortVlanTable)
  #  dot1dBasePortCircuit: .0.0
  #  dot1dBasePortIfIndex: 46  -> ifIndex in ifTable
  # $self->update_entry_cache(0, 'BRIDGE-MIB', 'dot1dBasePortTable', ['dot1dBasePort', "dot1dBasePortIfIndex"]);
  # creates entries like
  # '40#46-//-40' => [
  #   '40'
  # ],
  # get_cache_indices only works with --name
  #  '40#46-//-40' will be split into descr=40#46 and index=40
  #  descr would then be compared to --name and the value (which is [indices])
  #  be added to the return list. (the index 40 can also be a flat_indices)
  # we can't use dot1dBasePortIfIndex as the key_attr, as it is not unique
  # i ho an dot1dBasePortIfIndex und mou aussafina, wos fir index das aaf
  # dem hizoing.
  # zammbaut ho i dem zaech mied ['dot1dBasePort', "dot1dBasePortIfIndex"]
  # also woas i das descr vo dene zammgsetzt is und da wecha wos is.
  # na moue sched get_cache_indices_by_value('BRIDGE-MIB', 'dot1dBasePortTable', ['dot1dBasePort', "dot1dBasePortIfIndex"], "dot1dBasePortIfIndex", $pifidx)
  # aafruafa.
  # Liefert flache zruck.
  my ($self, $mib, $table, $key_attr, $cmp_attr, $cmp_value) = @_;
  if (ref($key_attr) ne "ARRAY") {
    $key_attr = [$key_attr];
  }
  if (ref($cmp_value) ne "ARRAY") {
    $cmp_value = [$cmp_value];
  }
  my $cache = sprintf "%s_%s_%s_cache",
      $mib, $table, join('#', @{$key_attr});
  my @indices = ();
  foreach my $key (keys %{$self->{$cache}}) {
    my ($descr, $index) = split('-//-', $key, 2);
    # descr was join('#', map { exists $entry->{$_} ? $entry->{$_} : "" } @{$key_attrs});
    my @values = split(/#/, $descr);
    my %cmp = map { $key_attr->[$_] => $values[$_] } 0 .. $#values;
    for my $cmp_val (@{$cmp_value}) {
      if ($cmp{$cmp_attr} && $cmp{$cmp_attr} eq $cmp_val) {
        push(@indices, $index);
      }
    }
  }
  return @indices;
}

sub get_cache_values_by_indices {
  my ($self, $mib, $table, $key_attr, $indices) = @_;
  # -> indices is an array of flat_indices
# records are
# val1#val#2#flat_index-//-flat_index => [indices]
# val1#val2 represent join(#, @key_attr)
  if (ref($key_attr) ne "ARRAY") {
    $key_attr = [$key_attr];
  }
  if (ref($indices) ne "ARRAY") {
    $indices = [$indices];
  }
  my $cache = sprintf "%s_%s_%s_cache",
      $mib, $table, join('#', @{$key_attr});
  my @results = ();
  foreach my $key (keys %{$self->{$cache}}) {
    my ($descr, $index) = split('-//-', $key, 2);
    my @values = split('#', $descr);
    foreach my $flat_indices (@{$indices}) {
      if ($flat_indices eq $index) {
        my $element = {
          flat_indices => $flat_indices,
        };
        foreach my $descr_idx (0 .. $#values) {
          $element->{$key_attr->[$descr_idx]} = $values[$descr_idx];
        }
        push(@results, $element);
      }
    }
  }
  return @results;
}

sub get_entities {
  my ($self, $class, $filter) = @_;
  foreach ($self->get_sub_table('ENTITY-MIB', [
    'entPhysicalDescr',
    'entPhysicalName',
    'entPhysicalClass',
  ])) {
    my $new_object = $class->new(%{$_});
    next if (defined $filter && ! &$filter($new_object));
    push @{$self->{entities}}, $new_object;
  }
}

sub get_sub_table {
  my ($self, $mib, $names) = @_;
  $self->require_mib($mib);
  my @oids = map {
    $Monitoring::GLPlugin::SNMP::MibsAndOids::mibs_and_oids->{$mib}->{$_}
  } @$names;
  my $result = $self->get_entries(
    -columns => \@oids
  );
  my $indices = ();
  map { if ($_ =~ /\.(\d+)$/) { $indices->{$1} = [ $1 ]; } } keys %$result;
  my @indices = values %$indices;
  my @entries = $self->make_symbolic($mib, $result, \@indices);
  @entries = map { $_->{indices} = shift @indices; $_ } @entries;
  @entries = map { $_->{flat_indices} = join(".", @{$_->{indices}}); $_ } @entries;
  return @entries;
}

sub join_table {
  my ($self, $to, $from) = @_;
  my $to_i = {};
  foreach (@$to) {
    my $i = $_->{flat_indices};
    $to_i->{$i} = $_;
  }
  foreach my $f (@$from) {
    my $i = $f->{flat_indices};
    if (exists $to_i->{$i}) {
      foreach (keys %$f) {
        next if $_ =~ /indices/;
        $to_i->{$i}->{$_} = $f->{$_};
      }
    }
  }
}




package Monitoring::GLPlugin::SNMP::CSF;
#our @ISA = qw(Monitoring::GLPlugin::SNMP);
use Digest::MD5 qw(md5_hex);
use strict;

sub create_statefile {
  my ($self, %params) = @_;
  my $extension = "";
  $extension .= $params{name} ? '_'.$params{name} : '';
  if ($self->opts->community) {
    $extension .= md5_hex($self->opts->community);
  }
  if ($self->opts->contextname) {
    $extension .= $self->opts->contextname;
  }
  $extension =~ s/\//_/g;
  $extension =~ s/\(/_/g;
  $extension =~ s/\)/_/g;
  $extension =~ s/\*/_/g;
  $extension =~ s/\s/_/g;
  if ($self->opts->snmpwalk && ! $self->opts->hostname) {
    return sprintf "%s/%s_%s%s", $self->statefilesdir(),
        'snmpwalk.file'.md5_hex($self->opts->snmpwalk),
        $self->clean_path($self->mode), $self->clean_path(lc $extension);
  } elsif ($self->opts->snmpwalk && $self->opts->hostname eq "walkhost") {
    return sprintf "%s/%s_%s%s", $self->statefilesdir(),
        'snmpwalk.file'.md5_hex($self->opts->snmpwalk),
        $self->clean_path($self->mode), $self->clean_path(lc $extension);
  } else {
    return sprintf "%s/%s_%s%s", $self->statefilesdir(),
        $self->opts->hostname,
        $self->clean_path($self->mode), $self->clean_path(lc $extension);
  }
}



package Monitoring::GLPlugin::SNMP::Item;
our @ISA = qw(Monitoring::GLPlugin::SNMP::CSF Monitoring::GLPlugin::Item Monitoring::GLPlugin::SNMP);
use strict;



package Monitoring::GLPlugin::SNMP::TableItem;
our @ISA = qw(Monitoring::GLPlugin::SNMP::CSF Monitoring::GLPlugin::TableItem Monitoring::GLPlugin::SNMP);
use strict;

sub ensure_index {
  my ($self, $key) = @_;
  $self->{$key} ||= $self->{flat_indices};
}

sub unhex_ip {
  my ($self, $value) = @_;
  if ($value && $value =~ /^0x(\w{8})/) {
    $value = join(".", unpack "C*", pack "H*", $1);
  } elsif ($value && $value =~ /^0x(\w{2} \w{2} \w{2} \w{2})/) {
    $value = $1;
    $value =~ s/ //g;
    $value = join(".", unpack "C*", pack "H*", $value);
  } elsif ($value && $value =~ /^([A-Z0-9]{2} [A-Z0-9]{2} [A-Z0-9]{2} [A-Z0-9]{2})/i) {
    $value = $1;
    $value =~ s/ //g;
    $value = join(".", unpack "C*", pack "H*", $value);
  } elsif ($value && unpack("H8", $value) =~ /(\w{2})(\w{2})(\w{2})(\w{2})/) {
    $value = join(".", map { hex($_) } ($1, $2, $3, $4));
  }
  return $value;
}

sub compact_v6 {
  my ($self, $addr) = @_;

  my @o = split /:/, $addr;
  return $addr unless @o and grep { $_ =~ m/^0+$/ } @o;

  my @candidates	= ();
  my $start		= undef;

  for my $i (0 .. $#o) {
    if (defined $start) {
      if ($o[$i] !~ m/^0+$/) {
        push @candidates, [ $start, $i - $start ];
        $start = undef;
      }
    } else {
      $start = $i if $o[$i] =~ m/^0+$/;
    }
  }

  push @candidates, [$start, 8 - $start] if defined $start;

  my $l = (sort { $b->[1] <=> $a->[1] } @candidates)[0];

  return $addr unless defined $l;

  $addr = $l->[0] == 0 ? '' : join ':', @o[0 .. $l->[0] - 1];
  $addr .= '::';
  $addr .= join ':', @o[$l->[0] + $l->[1] .. $#o];
  $addr =~ s/(^|:)0{1,3}/$1/g;

  return $addr;
}

sub old_unhex_ipv6 {
  my ($self, $value) = @_;
  if (! defined $value) {
    return $value; # tut mir leid
  } elsif ($value =~ /^0x(\w{32})/) {
    $value = join(":", unpack "C*", pack "H*", $1);
  } elsif ($value && $value =~ /^0x(\w{2} \w{2} \w{2} \w{2} \w{2} \w{2} \w{2} \w{2} \w{2} \w{2} \w{2} \w{2} \w{2} \w{2} \w{2} \w{2})/) {
    $value = $1;
    $value =~ s/ //g;
    $value = join(":", unpack "C*", pack "H*", $value);
  } elsif ($value =~ /^([A-Z0-9]{2} [A-Z0-9]{2} [A-Z0-9]{2} [A-Z0-9]{2} [A-Z0-9]{2} [A-Z0-9]{2} [A-Z0-9]{2} [A-Z0-9]{2} [A-Z0-9]{2} [A-Z0-9]{2} [A-Z0-9]{2} [A-Z0-9]{2} [A-Z0-9]{2} [A-Z0-9]{2} [A-Z0-9]{2} [A-Z0-9]{2})/i) {
    $value = $1;
    $value =~ s/ //g;
    $value = join(":", unpack "C*", pack "H*", $value);
  } elsif (unpack("H*", $value) =~ /^([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})/i) {
	  #$value = join(":", unpack "C*", pack "H*", $value);
    $value = join(":", ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16));
  }
  return $self->compact_v6($value);
}

sub unhex_ipv6 {
  my ($self, $value) = @_;
  my @octets = ();
  if (! defined $value) {
    return $value; # tut mir leid
  } elsif ($value =~ /^0x(\w{32})/) {
    @octets = unpack "H2" x 16, pack "H*", $1;
  } elsif ($value && $value =~ /^0x(\w{2} \w{2} \w{2} \w{2} \w{2} \w{2} \w{2} \w{2} \w{2} \w{2} \w{2} \w{2} \w{2} \w{2} \w{2} \w{2})/) {
    $value = $1;
    @octets = split(" ", $value);
  } elsif ($value =~ /^([A-Z0-9]{2} [A-Z0-9]{2} [A-Z0-9]{2} [A-Z0-9]{2} [A-Z0-9]{2} [A-Z0-9]{2} [A-Z0-9]{2} [A-Z0-9]{2} [A-Z0-9]{2} [A-Z0-9]{2} [A-Z0-9]{2} [A-Z0-9]{2} [A-Z0-9]{2} [A-Z0-9]{2} [A-Z0-9]{2} [A-Z0-9]{2})/i) {
    $value = $1;
    $value =~ s/ //g;
    @octets = unpack "H2" x 16, pack "H*", $value;
  } elsif (unpack("H*", $value) =~ /^([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})/i) {
    @octets = ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16);
  }
  $value = join(":",
      map { my $idx = 2*$_; $octets[$idx].$octets[$idx+1] } (0..7)
  );
  return $value;
  return $self->compact_v6($value);
}

sub unhex_mac {
  my ($self, $value) = @_;
  if ($value && $value =~ /^0x(\w{12})/) {
    $value = join(".", unpack "C*", pack "H*", $1);
  } elsif ($value && $value =~ /^0x(\w{2}\s*\w{2}\s*\w{2}\s*\w{2}\s*\w{2}\s*\w{2})/) {
    $value = $1;
    $value =~ s/ //g;
    $value = join(":", unpack "C*", pack "H*", $value);
  } elsif ($value && unpack("H12", $value) =~ /(\w{2})(\w{2})(\w{2})(\w{2})(\w{2})(\w{2})/) {
    $value = join(":", ($1, $2, $3, $4, $5, $6));
  }
  return $value;
}

sub unhex_octet_string {
  my ($self, $value) = @_;
  my $original = $value;
  $value =~ s/ //g;
  if ($value && $value =~ /^0x([0-9a-zA-Z]+)$/) {
    $value = join("", unpack "A*", pack "H*", $1);
  } elsif ($value && $value =~ /^([0-9a-zA-Z]+)$/) {
    $value = join("", unpack "A*", pack "H*", $1);
  } else {
    $value = $original;
  }
  return $value;
}



package Monitoring::GLPlugin::SNMP::MibsAndOids;
our @ISA = qw(Monitoring::GLPlugin::SNMP);

{
  no warnings qw(once);
  $Monitoring::GLPlugin::SNMP::MibsAndOids::discover_ids = {};
  $Monitoring::GLPlugin::SNMP::MibsAndOids::mib_ids = {};
  $Monitoring::GLPlugin::SNMP::MibsAndOids::mibs_and_oids = {};
  $Monitoring::GLPlugin::SNMP::MibsAndOids::definitions = {};
  $Monitoring::GLPlugin::SNMP::MibsAndOids::origin = {};
}



package Monitoring::GLPlugin::SNMP::MibsAndOids::MGSNMPUPSMIB;

$Monitoring::GLPlugin::SNMP::MibsAndOids::origin->{'MG-SNMP-UPS-MIB'} = {
  url => "",
  name => "MG-SNMP-UPS-MIB",
};

$Monitoring::GLPlugin::SNMP::MibsAndOids::mib_ids->{'MG-SNMP-UPS-MIB'} = 
  '1.3.6.1.4.1.705.1';

$Monitoring::GLPlugin::SNMP::MibsAndOids::mibs_and_oids->{'MG-SNMP-UPS-MIB'} = {
  merlinGerin => '1.3.6.1.4.1.705',
  upsmg => '1.3.6.1.4.1.705.1',
  upsmgIdent => '1.3.6.1.4.1.705.1.1',
  upsmgIdentFamilyName => '1.3.6.1.4.1.705.1.1.1',
  upsmgIdentModelName => '1.3.6.1.4.1.705.1.1.2',
  upsmgIdentRevisionLevel => '1.3.6.1.4.1.705.1.1.3',
  upsmgIdentFirmwareVersion => '1.3.6.1.4.1.705.1.1.4',
  upsmgIdentUserID => '1.3.6.1.4.1.705.1.1.5',
  upsmgIdentInstallationDate => '1.3.6.1.4.1.705.1.1.6',
  upsmgIdentSerialNumber => '1.3.6.1.4.1.705.1.1.7',
  upsmgManagement => '1.3.6.1.4.1.705.1.2',
  upsmgManagersNum => '1.3.6.1.4.1.705.1.2.1',
  upsmgManagersTable => '1.3.6.1.4.1.705.1.2.2',
  upsmgManagersEntry => '1.3.6.1.4.1.705.1.2.2.1',
  mgmanagerIndex => '1.3.6.1.4.1.705.1.2.2.1.1',
  mgmanagerDeviceNumber => '1.3.6.1.4.1.705.1.2.2.1.2',
  mgmanagerNMSType => '1.3.6.1.4.1.705.1.2.2.1.3',
  mgmanagerNMSTypeDefinition => {
    8 => 'ibmnetview',
    2 => 'decnetview',
    6 => 'sunnetmanager',
    4 => 'dview',
    7 => 'novellnms',
    9 => 'other',
    1 => 'umclient',
    5 => 'hpopenview',
    10 => 'autolearning',
    3 => 'umview',
  },
  mgmanagerCommType => '1.3.6.1.4.1.705.1.2.2.1.4',
  mgmanagerCommTypeDefinition => {
    5 => 'snmpv2',
    1 => 'other',
    4 => 'snmpv1',
    3 => 'cmip',
    2 => 'invalid',
  },
  mgmanagerDescr => '1.3.6.1.4.1.705.1.2.2.1.5',
  mgmanagerAddress => '1.3.6.1.4.1.705.1.2.2.1.6',
  mgmanagerCommunity => '1.3.6.1.4.1.705.1.2.2.1.7',
  mgmanagerSeverityLevel => '1.3.6.1.4.1.705.1.2.2.1.8',
  mgmanagerTrapAck => '1.3.6.1.4.1.705.1.2.2.1.9',
  mgmanagerTrapAckDefinition => {
    4 => 'mgacks',
    2 => 'mgnoack',
    3 => 'stdnomg',
    5 => 'cpqnoack',
    1 => 'mgack',
  },
  upsmgReceptacle => '1.3.6.1.4.1.705.1.3',
  upsmgReceptaclesNum => '1.3.6.1.4.1.705.1.3.1',
  upsmgReceptaclesTable => '1.3.6.1.4.1.705.1.3.2',
  upsmgReceptaclesEntry => '1.3.6.1.4.1.705.1.3.2.1',
  mgreceptacleIndex => '1.3.6.1.4.1.705.1.3.2.1.1',
  mgreceptacleLevel => '1.3.6.1.4.1.705.1.3.2.1.2',
  mgreceptacleType => '1.3.6.1.4.1.705.1.3.2.1.3',
  mgreceptacleIdent => '1.3.6.1.4.1.705.1.3.2.1.4',
  mgreceptacleState => '1.3.6.1.4.1.705.1.3.2.1.5',
  mgreceptacleStateDefinition => {
    3 => 'normalON',
    8 => 'scheduleOFF',
    1 => 'manualON',
    2 => 'manualOFF',
    6 => 'controlOFF',
    4 => 'normalOFF',
    5 => 'controlON',
    7 => 'scheduleON',
  },
  mgreceptacleReceptacle => '1.3.6.1.4.1.705.1.3.2.1.6',
  mgreceptaclePowerCons => '1.3.6.1.4.1.705.1.3.2.1.7',
  mgreceptacleOverload => '1.3.6.1.4.1.705.1.3.2.1.8',
  mgreceptacleAutonomy => '1.3.6.1.4.1.705.1.3.2.1.9',
  upsmgConfig => '1.3.6.1.4.1.705.1.4',
  upsmgConfigBatteryInstalled => '1.3.6.1.4.1.705.1.4.1',
  upsmgConfigBatteryInstalledDefinition => {
    1 => 'yes',
    2 => 'no',
  },
  upsmgConfigNominalBatteryVoltage => '1.3.6.1.4.1.705.1.4.2',
  upsmgConfigNominalBatteryTime => '1.3.6.1.4.1.705.1.4.3',
  upsmgConfigNominalRechargeTime => '1.3.6.1.4.1.705.1.4.4',
  upsmgConfigMinRechargeLevel => '1.3.6.1.4.1.705.1.4.5',
  upsmgConfigMaxRechargeTime => '1.3.6.1.4.1.705.1.4.6',
  upsmgConfigLowBatteryTime => '1.3.6.1.4.1.705.1.4.7',
  upsmgConfigLowBatteryLevel => '1.3.6.1.4.1.705.1.4.8',
  upsmgConfigAutoRestart => '1.3.6.1.4.1.705.1.4.9',
  upsmgConfigAutoRestartDefinition => {
    3 => 'onmain',
    2 => 'never',
    1 => 'always',
  },
  upsmgConfigShutdownTimer => '1.3.6.1.4.1.705.1.4.10',
  upsmgConfigSysShutDuration => '1.3.6.1.4.1.705.1.4.11',
  upsmgConfigVARating => '1.3.6.1.4.1.705.1.4.12',
  upsmgConfigLowTransfer => '1.3.6.1.4.1.705.1.4.13',
  upsmgConfigHighTransfer => '1.3.6.1.4.1.705.1.4.14',
  upsmgConfigOutputNominalVoltage => '1.3.6.1.4.1.705.1.4.15',
  upsmgConfigOutputNominalCurrent => '1.3.6.1.4.1.705.1.4.16',
  upsmgConfigOutputNomFrequency => '1.3.6.1.4.1.705.1.4.17',
  upsmgConfigByPassType => '1.3.6.1.4.1.705.1.4.18',
  upsmgConfigByPassTypeDefinition => {
    2 => 'relay',
    1 => 'none',
    3 => 'static',
  },
  upsmgConfigAlarmAudible => '1.3.6.1.4.1.705.1.4.19',
  upsmgConfigAlarmAudibleDefinition => {
    1 => 'yes',
    2 => 'no',
  },
  upsmgConfigAlarmTimeDelay => '1.3.6.1.4.1.705.1.4.20',
  upsmgConfigDevicesNum => '1.3.6.1.4.1.705.1.4.21',
  upsmgConfigDevicesTable => '1.3.6.1.4.1.705.1.4.22',
  upsmgConfigDevicesEntry => '1.3.6.1.4.1.705.1.4.22.1',
  mgdeviceIndex => '1.3.6.1.4.1.705.1.4.22.1.1',
  mgdeviceReceptacleNum => '1.3.6.1.4.1.705.1.4.22.1.2',
  mgdeviceIdent => '1.3.6.1.4.1.705.1.4.22.1.3',
  mgdeviceVaRating => '1.3.6.1.4.1.705.1.4.22.1.4',
  mgdeviceSequenceOff => '1.3.6.1.4.1.705.1.4.22.1.5',
  mgdeviceSequenceOn => '1.3.6.1.4.1.705.1.4.22.1.6',
  mgdeviceShutdownDuration => '1.3.6.1.4.1.705.1.4.22.1.7',
  mgdeviceBootUpDuration => '1.3.6.1.4.1.705.1.4.22.1.8',
  upsmgConfigReceptaclesTable => '1.3.6.1.4.1.705.1.4.23',
  upsmgConfigReceptaclesEntry => '1.3.6.1.4.1.705.1.4.23.1',
  mgreceptacleIndexb => '1.3.6.1.4.1.705.1.4.23.1.1',
  mgreceptacleStateTurnOn => '1.3.6.1.4.1.705.1.4.23.1.2',
  mgreceptacleStateTurnOnDefinition => {
    1 => 'on',
    3 => 'last',
    2 => 'off',
    4 => 'schedule',
  },
  mgreceptacleStateMainReturn => '1.3.6.1.4.1.705.1.4.23.1.3',
  mgreceptacleStateMainReturnDefinition => {
    1 => 'on',
    3 => 'last',
    2 => 'off',
    4 => 'schedule',
  },
  mgreceptacleStateDischarge => '1.3.6.1.4.1.705.1.4.23.1.4',
  mgreceptacleStateDischargeDefinition => {
    1 => 'on',
    3 => 'last',
    2 => 'off',
    4 => 'schedule',
  },
  mgreceptacleShutoffLevel => '1.3.6.1.4.1.705.1.4.23.1.5',
  mgreceptacleShutoffTimer => '1.3.6.1.4.1.705.1.4.23.1.6',
  mgreceptacleRestartLevel => '1.3.6.1.4.1.705.1.4.23.1.7',
  mgreceptacleRestartDelay => '1.3.6.1.4.1.705.1.4.23.1.8',
  mgreceptacleShutdownDuration => '1.3.6.1.4.1.705.1.4.23.1.9',
  mgreceptacleBootUpDuration => '1.3.6.1.4.1.705.1.4.23.1.10',
  upsmgConfigExtAlarmNum => '1.3.6.1.4.1.705.1.4.24',
  upsmgConfigExtAlarmTable => '1.3.6.1.4.1.705.1.4.25',
  upsmgConfigExtAlarmEntry => '1.3.6.1.4.1.705.1.4.25.1',
  mgextAlarmIndex => '1.3.6.1.4.1.705.1.4.25.1.1',
  mgextAlarmUID => '1.3.6.1.4.1.705.1.4.25.1.2',
  upsmgConfigEmergencyTestFail => '1.3.6.1.4.1.705.1.4.26',
  upsmgConfigEmergencyTestFailDefinition => {
    1 => 'yes',
    2 => 'no',
  },
  upsmgConfigEmergencyOnByPass => '1.3.6.1.4.1.705.1.4.27',
  upsmgConfigEmergencyOnByPassDefinition => {
    1 => 'yes',
    2 => 'no',
  },
  upsmgConfigEmergencyOverload => '1.3.6.1.4.1.705.1.4.28',
  upsmgConfigEmergencyOverloadDefinition => {
    1 => 'yes',
    2 => 'no',
  },
  upsmgConfigControlDayTable => '1.3.6.1.4.1.705.1.4.29',
  upsmgConfigControlDayEntry => '1.3.6.1.4.1.705.1.4.29.1',
  mgcontrolDayIndex => '1.3.6.1.4.1.705.1.4.29.1.1',
  mgcontrolDayOn => '1.3.6.1.4.1.705.1.4.29.1.2',
  mgcontrolDayOff => '1.3.6.1.4.1.705.1.4.29.1.3',
  upsmgConfigLowBooster => '1.3.6.1.4.1.705.1.4.30',
  upsmgConfigHighBooster => '1.3.6.1.4.1.705.1.4.31',
  upsmgConfigLowFader => '1.3.6.1.4.1.705.1.4.32',
  upsmgConfigHighFader => '1.3.6.1.4.1.705.1.4.33',
  upsmgConfigEnvironmentTable => '1.3.6.1.4.1.705.1.4.34',
  upsmgConfigEnvironmentEntry => '1.3.6.1.4.1.705.1.4.34.1',
  upsmgConfigSensorIndex => '1.3.6.1.4.1.705.1.4.34.1.1',
  upsmgConfigSensorName => '1.3.6.1.4.1.705.1.4.34.1.2',
  upsmgConfigTemperatureLow => '1.3.6.1.4.1.705.1.4.34.1.3',
  upsmgConfigTemperatureHigh => '1.3.6.1.4.1.705.1.4.34.1.4',
  upsmgConfigTemperatureHysteresis => '1.3.6.1.4.1.705.1.4.34.1.5',
  upsmgConfigHumidityLow => '1.3.6.1.4.1.705.1.4.34.1.6',
  upsmgConfigHumidityHigh => '1.3.6.1.4.1.705.1.4.34.1.7',
  upsmgConfigHumidityHysteresis => '1.3.6.1.4.1.705.1.4.34.1.8',
  upsmgConfigInput1Name => '1.3.6.1.4.1.705.1.4.34.1.9',
  upsmgConfigInput1ClosedLabel => '1.3.6.1.4.1.705.1.4.34.1.10',
  upsmgConfigInput1OpenLabel => '1.3.6.1.4.1.705.1.4.34.1.11',
  upsmgConfigInput2Name => '1.3.6.1.4.1.705.1.4.34.1.12',
  upsmgConfigInput2ClosedLabel => '1.3.6.1.4.1.705.1.4.34.1.13',
  upsmgConfigInput2OpenLabel => '1.3.6.1.4.1.705.1.4.34.1.14',
  upsmgBattery => '1.3.6.1.4.1.705.1.5',
  upsmgBatteryRemainingTime => '1.3.6.1.4.1.705.1.5.1',
  upsmgBatteryLevel => '1.3.6.1.4.1.705.1.5.2',
  upsmgBatteryRechargeTime => '1.3.6.1.4.1.705.1.5.3',
  upsmgBatteryRechargeLevel => '1.3.6.1.4.1.705.1.5.4',
  upsmgBatteryVoltage => '1.3.6.1.4.1.705.1.5.5',
  upsmgBatteryCurrent => '1.3.6.1.4.1.705.1.5.6',
  upsmgBatteryTemperature => '1.3.6.1.4.1.705.1.5.7',
  upsmgBatteryFullRechargeTime => '1.3.6.1.4.1.705.1.5.8',
  upsmgBatteryFaultBattery => '1.3.6.1.4.1.705.1.5.9',
  upsmgBatteryFaultBatteryDefinition => {
    1 => 'yes',
    2 => 'no',
  },
  upsmgBatteryNoBattery => '1.3.6.1.4.1.705.1.5.10',
  upsmgBatteryNoBatteryDefinition => {
    1 => 'yes',
    2 => 'no',
  },
  upsmgBatteryReplacement => '1.3.6.1.4.1.705.1.5.11',
  upsmgBatteryReplacementDefinition => {
    1 => 'yes',
    2 => 'no',
  },
  upsmgBatteryUnavailableBattery => '1.3.6.1.4.1.705.1.5.12',
  upsmgBatteryUnavailableBatteryDefinition => {
    1 => 'yes',
    2 => 'no',
  },
  upsmgBatteryNotHighCharge => '1.3.6.1.4.1.705.1.5.13',
  upsmgBatteryNotHighChargeDefinition => {
    1 => 'yes',
    2 => 'no',
  },
  upsmgBatteryLowBattery => '1.3.6.1.4.1.705.1.5.14',
  upsmgBatteryLowBatteryDefinition => {
    1 => 'yes',
    2 => 'no',
  },
  upsmgBatteryChargerFault => '1.3.6.1.4.1.705.1.5.15',
  upsmgBatteryChargerFaultDefinition => {
    1 => 'yes',
    2 => 'no',
  },
  upsmgBatteryLowCondition => '1.3.6.1.4.1.705.1.5.16',
  upsmgBatteryLowConditionDefinition => {
    1 => 'yes',
    2 => 'no',
  },
  upsmgBatteryLowRecharge => '1.3.6.1.4.1.705.1.5.17',
  upsmgBatteryLowRechargeDefinition => {
    1 => 'yes',
    2 => 'no',
  },
  upsmgInput => '1.3.6.1.4.1.705.1.6',
  upsmgInputPhaseNum => '1.3.6.1.4.1.705.1.6.1',
  upsmgInputPhaseTable => '1.3.6.1.4.1.705.1.6.2',
  upsmgInputPhaseEntry => '1.3.6.1.4.1.705.1.6.2.1',
  mginputIndex => '1.3.6.1.4.1.705.1.6.2.1.1',
  mginputVoltage => '1.3.6.1.4.1.705.1.6.2.1.2',
  mginputFrequency => '1.3.6.1.4.1.705.1.6.2.1.3',
  mginputMinimumVoltage => '1.3.6.1.4.1.705.1.6.2.1.4',
  mginputMaximumVoltage => '1.3.6.1.4.1.705.1.6.2.1.5',
  mginputCurrent => '1.3.6.1.4.1.705.1.6.2.1.6',
  upsmgInputBadStatus => '1.3.6.1.4.1.705.1.6.3',
  upsmgInputBadStatusDefinition => {
    1 => 'yes',
    2 => 'no',
  },
  upsmgInputLineFailCause => '1.3.6.1.4.1.705.1.6.4',
  upsmgInputLineFailCauseDefinition => {
    2 => 'outoftolvolt',
    4 => 'utilityoff',
    1 => 'no',
    3 => 'outoftolfreq',
  },
  upsmgOutput => '1.3.6.1.4.1.705.1.7',
  upsmgOutputPhaseNum => '1.3.6.1.4.1.705.1.7.1',
  upsmgOutputPhaseTable => '1.3.6.1.4.1.705.1.7.2',
  upsmgOutputPhaseEntry => '1.3.6.1.4.1.705.1.7.2.1',
  mgoutputPhaseIndex => '1.3.6.1.4.1.705.1.7.2.1.1',
  mgoutputVoltage => '1.3.6.1.4.1.705.1.7.2.1.2',
  mgoutputFrequency => '1.3.6.1.4.1.705.1.7.2.1.3',
  mgoutputLoadPerPhase => '1.3.6.1.4.1.705.1.7.2.1.4',
  mgoutputCurrent => '1.3.6.1.4.1.705.1.7.2.1.5',
  upsmgOutputOnBattery => '1.3.6.1.4.1.705.1.7.3',
  upsmgOutputOnBatteryDefinition => {
    1 => 'yes',
    2 => 'no',
  },
  upsmgOutputOnByPass => '1.3.6.1.4.1.705.1.7.4',
  upsmgOutputOnByPassDefinition => {
    1 => 'yes',
    2 => 'no',
  },
  upsmgOutputUnavailableByPass => '1.3.6.1.4.1.705.1.7.5',
  upsmgOutputUnavailableByPassDefinition => {
    1 => 'yes',
    2 => 'no',
  },
  upsmgOutputNoByPass => '1.3.6.1.4.1.705.1.7.6',
  upsmgOutputNoByPassDefinition => {
    1 => 'yes',
    2 => 'no',
  },
  upsmgOutputUtilityOff => '1.3.6.1.4.1.705.1.7.7',
  upsmgOutputUtilityOffDefinition => {
    1 => 'yes',
    2 => 'no',
  },
  upsmgOutputOnBoost => '1.3.6.1.4.1.705.1.7.8',
  upsmgOutputOnBoostDefinition => {
    1 => 'yes',
    2 => 'no',
  },
  upsmgOutputInverterOff => '1.3.6.1.4.1.705.1.7.9',
  upsmgOutputInverterOffDefinition => {
    1 => 'yes',
    2 => 'no',
  },
  upsmgOutputOverLoad => '1.3.6.1.4.1.705.1.7.10',
  upsmgOutputOverLoadDefinition => {
    1 => 'yes',
    2 => 'no',
  },
  upsmgOutputOverTemp => '1.3.6.1.4.1.705.1.7.11',
  upsmgOutputOverTempDefinition => {
    1 => 'yes',
    2 => 'no',
  },
  upsmgOutputOnBuck => '1.3.6.1.4.1.705.1.7.12',
  upsmgEnviron => '1.3.6.1.4.1.705.1.8',
  upsmgEnvironAmbientTemp => '1.3.6.1.4.1.705.1.8.1',
  upsmgEnvironAmbientHumidity => '1.3.6.1.4.1.705.1.8.2',
  upsmgEnvironExtAlarmTable => '1.3.6.1.4.1.705.1.8.3',
  upsmgEnvironExtAlarmEntry => '1.3.6.1.4.1.705.1.8.3.1',
  mgalarmNum => '1.3.6.1.4.1.705.1.8.3.1.1',
  mgalarmState => '1.3.6.1.4.1.705.1.8.3.1.2',
  mgalarmStateDefinition => {
    2 => 'inactive',
    1 => 'active',
  },
  upsmgEnvironSensorNum => '1.3.6.1.4.1.705.1.8.4',
  upsmgEnvironSensorTable => '1.3.6.1.4.1.705.1.8.5',
  upsmgEnvironSensorEntry => '1.3.6.1.4.1.705.1.8.5.1',
  mgsensorNum => '1.3.6.1.4.1.705.1.8.5.1.1',
  mgsensorTemp => '1.3.6.1.4.1.705.1.8.5.1.2',
  mgsensorHumidity => '1.3.6.1.4.1.705.1.8.5.1.3',
  upsmgEnvironmentNum => '1.3.6.1.4.1.705.1.8.6',
  upsmgEnvironmentSensorTable => '1.3.6.1.4.1.705.1.8.7',
  upsmgEnvironmentSensorEntry => '1.3.6.1.4.1.705.1.8.7.1',
  upsmgEnvironmentIndex => '1.3.6.1.4.1.705.1.8.7.1.1',
  upsmgEnvironmentComFailure => '1.3.6.1.4.1.705.1.8.7.1.2',
  upsmgEnvironmentComFailureDefinition => {
    1 => 'yes',
    2 => 'no',
  },
  upsmgEnvironmentTemperature => '1.3.6.1.4.1.705.1.8.7.1.3',
  upsmgEnvironmentTemperatureLow => '1.3.6.1.4.1.705.1.8.7.1.4',
  upsmgEnvironmentTemperatureLowDefinition => {
    1 => 'yes',
    2 => 'no',
  },
  upsmgEnvironmentTemperatureHigh => '1.3.6.1.4.1.705.1.8.7.1.5',
  upsmgEnvironmentTemperatureHighDefinition => {
    1 => 'yes',
    2 => 'no',
  },
  upsmgEnvironmentHumidity => '1.3.6.1.4.1.705.1.8.7.1.6',
  upsmgEnvironmentHumidityLow => '1.3.6.1.4.1.705.1.8.7.1.7',
  upsmgEnvironmentHumidityLowDefinition => {
    1 => 'yes',
    2 => 'no',
  },
  upsmgEnvironmentHumidityHigh => '1.3.6.1.4.1.705.1.8.7.1.8',
  upsmgEnvironmentHumidityHighDefinition => {
    1 => 'yes',
    2 => 'no',
  },
  upsmgEnvironmentInput1State => '1.3.6.1.4.1.705.1.8.7.1.9',
  upsmgEnvironmentInput1StateDefinition => {
    2 => 'open',
    1 => 'closed',
  },
  upsmgEnvironmentInput2State => '1.3.6.1.4.1.705.1.8.7.1.10',
  upsmgEnvironmentInput2StateDefinition => {
    2 => 'open',
    1 => 'closed',
  },
  upsmgControl => '1.3.6.1.4.1.705.1.9',
  upsmgControlReceptaclesTable => '1.3.6.1.4.1.705.1.9.1',
  upsmgControlReceptaclesEntry => '1.3.6.1.4.1.705.1.9.1.1',
  mgreceptacleIndexc => '1.3.6.1.4.1.705.1.9.1.1.1',
  mgreceptacleOnDelay => '1.3.6.1.4.1.705.1.9.1.1.2',
  mgreceptacleOnCtrl => '1.3.6.1.4.1.705.1.9.1.1.3',
  mgreceptacleOnCtrlDefinition => {
    3 => 'stop',
    1 => 'nothing',
    2 => 'start',
  },
  mgreceptacleOnStatus => '1.3.6.1.4.1.705.1.9.1.1.4',
  mgreceptacleOnStatusDefinition => {
    3 => 'inprogressinups',
    1 => 'none',
    2 => 'started',
    4 => 'completed',
  },
  mgreceptacleOffDelay => '1.3.6.1.4.1.705.1.9.1.1.5',
  mgreceptacleOffCtrl => '1.3.6.1.4.1.705.1.9.1.1.6',
  mgreceptacleOffCtrlDefinition => {
    3 => 'stop',
    1 => 'nothing',
    2 => 'start',
  },
  mgreceptacleOffStatus => '1.3.6.1.4.1.705.1.9.1.1.7',
  mgreceptacleOffStatusDefinition => {
    3 => 'inprogressinups',
    1 => 'none',
    2 => 'started',
    4 => 'completed',
  },
  mgreceptacleToggleDelay => '1.3.6.1.4.1.705.1.9.1.1.8',
  mgreceptacleToggleCtrl => '1.3.6.1.4.1.705.1.9.1.1.9',
  mgreceptacleToggleCtrlDefinition => {
    3 => 'stop',
    1 => 'nothing',
    2 => 'start',
  },
  mgreceptacleToggleStatus => '1.3.6.1.4.1.705.1.9.1.1.10',
  mgreceptacleToggleStatusDefinition => {
    3 => 'inprogressinups',
    1 => 'none',
    2 => 'started',
    4 => 'completed',
  },
  mgreceptacleToggleDuration => '1.3.6.1.4.1.705.1.9.1.1.11',
  upsmgControlDayOff => '1.3.6.1.4.1.705.1.9.2',
  upsmgControlDayOffDefinition => {
    1 => 'sunday',
    8 => 'none',
    6 => 'friday',
    3 => 'tuesday',
    2 => 'monday',
    4 => 'wednesday',
    5 => 'thursday',
    7 => 'saterday',
  },
  upsmgControlDayOn => '1.3.6.1.4.1.705.1.9.3',
  upsmgControlDayOnDefinition => {
    1 => 'sunday',
    8 => 'none',
    6 => 'friday',
    3 => 'tuesday',
    2 => 'monday',
    4 => 'wednesday',
    5 => 'thursday',
    7 => 'saterday',
  },
  upsmgTest => '1.3.6.1.4.1.705.1.10',
  upsmgTestBatterySchedule => '1.3.6.1.4.1.705.1.10.1',
  upsmgTestBatteryScheduleDefinition => {
    4 => 'atturnon',
    2 => 'weekly',
    5 => 'none',
    1 => 'unknown',
    3 => 'monthly',
    6 => 'dayly',
  },
  upsmgTestDiagnostics => '1.3.6.1.4.1.705.1.10.2',
  upsmgTestDiagnosticsDefinition => {
    1 => 'default',
    2 => 'start',
  },
  upsmgTestDiagResult => '1.3.6.1.4.1.705.1.10.3',
  upsmgTestDiagResultDefinition => {
    1 => 'success',
    3 => 'none',
    2 => 'failed',
  },
  upsmgTestBatteryCalibration => '1.3.6.1.4.1.705.1.10.4',
  upsmgTestBatteryCalibrationDefinition => {
    1 => 'default',
    2 => 'start',
  },
  upsmgTestLastCalibration => '1.3.6.1.4.1.705.1.10.5',
  upsmgTestIndicators => '1.3.6.1.4.1.705.1.10.6',
  upsmgTestCommandLine => '1.3.6.1.4.1.705.1.10.7',
  upsmgTestCommandReady => '1.3.6.1.4.1.705.1.10.8',
  upsmgTestCommandReadyDefinition => {
    1 => 'yes',
    2 => 'no',
  },
  upsmgTestResponseLine => '1.3.6.1.4.1.705.1.10.9',
  upsmgTestResponseReady => '1.3.6.1.4.1.705.1.10.10',
  upsmgTestResponseReadyDefinition => {
    1 => 'yes',
    2 => 'no',
  },
  upsmgTestBatteryResult => '1.3.6.1.4.1.705.1.10.11',
  upsmgTestBatteryResultDefinition => {
    5 => 'none',
    4 => 'sfailed',
    2 => 'mfailed',
    1 => 'msuccess',
    3 => 'ssuccess',
  },
  upsmgTraps => '1.3.6.1.4.1.705.1.11',
  upsmgAgent => '1.3.6.1.4.1.705.1.12',
  upsmgAgentIpaddress => '1.3.6.1.4.1.705.1.12.1',
  upsmgAgentSubnetMask => '1.3.6.1.4.1.705.1.12.2',
  upsmgAgentDefGateway => '1.3.6.1.4.1.705.1.12.3',
  upsmgAgentBaudRate => '1.3.6.1.4.1.705.1.12.4',
  upsmgAgentPollRate => '1.3.6.1.4.1.705.1.12.5',
  upsmgAgentType => '1.3.6.1.4.1.705.1.12.6',
  upsmgAgentTypeDefinition => {
    3 => 'proxyEth',
    2 => 'deviceTR',
    5 => 'other',
    4 => 'proxyTR',
    1 => 'deviceEth',
  },
  upsmgAgentTrapAlarmDelay => '1.3.6.1.4.1.705.1.12.7',
  upsmgAgentTrapAlarmRetry => '1.3.6.1.4.1.705.1.12.8',
  upsmgAgentReset => '1.3.6.1.4.1.705.1.12.9',
  upsmgAgentResetDefinition => {
    1 => 'reset',
    2 => 'nothing',
  },
  upsmgAgentFactReset => '1.3.6.1.4.1.705.1.12.10',
  upsmgAgentFactResetDefinition => {
    1 => 'reset',
    2 => 'nothing',
  },
  upsmgAgentMibVersion => '1.3.6.1.4.1.705.1.12.11',
  upsmgAgentFirmwareVersion => '1.3.6.1.4.1.705.1.12.12',
  upsmgAgentCommUPS => '1.3.6.1.4.1.705.1.12.13',
  upsmgAgentTrapAck => '1.3.6.1.4.1.705.1.12.14',
  upsmgAgentAutoLearning => '1.3.6.1.4.1.705.1.12.15',
  upsmgAgentAutoLearningDefinition => {
    1 => 'yes',
    2 => 'no',
  },
  upsmgAgentBootP => '1.3.6.1.4.1.705.1.12.16',
  upsmgAgentBootPDefinition => {
    1 => 'yes',
    2 => 'no',
  },
  upsmgAgentTFTP => '1.3.6.1.4.1.705.1.12.17',
  upsmgAgentTFTPDefinition => {
    1 => 'yes',
    2 => 'no',
  },
  upsmgAgentTrapSignature => '1.3.6.1.4.1.705.1.12.18',
  upsmgRemote => '1.3.6.1.4.1.705.1.13',
  upsmgRemoteOnBattery => '1.3.6.1.4.1.705.1.13.1',
  upsmgRemoteIpAddress => '1.3.6.1.4.1.705.1.13.2',
};



package Monitoring::GLPlugin::SNMP::MibsAndOids::MIB2MIB;

$Monitoring::GLPlugin::SNMP::MibsAndOids::origin->{'MIB-2-MIB'} = {
  url => "",
  name => "MIB-2-MIB",
};

$Monitoring::GLPlugin::SNMP::MibsAndOids::mibs_and_oids->{'MIB-2-MIB'} = {
  sysDescr => '1.3.6.1.2.1.1.1',
  sysObjectID => '1.3.6.1.2.1.1.2',
  sysUpTime => '1.3.6.1.2.1.1.3',
  sysName => '1.3.6.1.2.1.1.5',
  sysORTable => '1.3.6.1.2.1.1.9',
  sysOREntry => '1.3.6.1.2.1.1.9.1',
  sysORIndex => '1.3.6.1.2.1.1.9.1.1',
  sysORID => '1.3.6.1.2.1.1.9.1.2',
  sysORDescr => '1.3.6.1.2.1.1.9.1.3',
  sysORUpTime => '1.3.6.1.2.1.1.9.1.4',
};

$Monitoring::GLPlugin::SNMP::MibsAndOids::definitions->{'MIB-2-MIB'} = {
  'DateAndTime' => sub {
    my $value = shift;
    use Time::Local;
    my ($month, $day, $hour, $minute, $second, $dseconds, $dirutc, $hoursutc, $minutesutc,
        $wday, $yday, $isdst, $year) =
        (0, 0, 0, 0, 0, 0, "+", 0, 0, 0, 0, 0, 0);
#      DISPLAY-HINT "2d-1d-1d,1d:1d:1d.1d,1a1d:1d"
#      STATUS       current
#      DESCRIPTION
#              "A date-time specification.
#  
#              field  octets  contents                  range
#              -----  ------  --------                  -----
#                1      1-2   year*                     0..65536
#                2       3    month                     1..12
#                3       4    day                       1..31
#                4       5    hour                      0..23
#                5       6    minutes                   0..59
#                6       7    seconds                   0..60
#                             (use 60 for leap-second)
#                7       8    deci-seconds              0..9
#                8       9    direction from UTC        '+' / '-'
#                9      10    hours from UTC*           0..13
#               10      11    minutes from UTC          0..59
#  
#              * Notes:
#              - the value of year is in network-byte order
#              - daylight saving time in New Zealand is +13
#  
#              For example, Tuesday May 26, 1992 at 1:30:15 PM EDT would be
#              displayed as:
#  
#                               1992-5-26,13:30:15.0,-4:0
#  
#              Note that if only local time is known, then timezone
#              information (fields 8-10) is not present."
#      SYNTAX       OCTET STRING (SIZE (8 | 11))

    if ($value && $value !~ /^[ \w,\:\-\+\.]+$/) {
      $value = unpack("H*", $value);
    }
    if ($value && (
        $value =~ /^0x((\w{2} ){8,})/ ||
        $value =~ /^0x((\w{2} ){7,}(\w{2}){1,})/ ||
        $value =~ /^((\w{2}){8,})/ ||
        $value =~ /^((\w{2} ){8,})/ ||
        $value =~ /^((\w{2} ){7,}(\w{2}){1,})/ ||
        $value =~ /^(([0-9a-fA-F][0-9a-fA-F]){6,})$/
    )) {
      $value = $1;
      $value =~ s/ //g;
      $year = hex substr($value, 0, 4);
      $value = substr($value, 4);
      if (length($value) > 12) {
        ($month, $day, $hour, $minute, $second, $dseconds,
            $dirutc, $hoursutc, $minutesutc) = unpack "C*", pack "H*", $value;
        $minutesutc ||= 0;
        $dirutc = ($dirutc == 43) ? "+" : ($dirutc == 45) ? "-" : "+";
        if ($value eq "000000000000000000") {
          $day = 1;
          $month = 1;
          $year = 1970;
        }
      } else {
        ($month, $day, $hour, $minute, $second, $dseconds) = unpack "C*", pack "H*", $value;
        $second ||= 0;
        $dseconds ||= 0;
        my @t = localtime(time);
        my $gmt_offset_in_hours = (timegm(@t) - timelocal(@t)) / 3600;
        ($dirutc, $hoursutc, $minutesutc) = ("+", $gmt_offset_in_hours, 0);
      }
    } elsif ($value && $value =~ /(\d+)-(\d+)-(\d+),(\d+):(\d+):(\d+)\.(\d+),([\+\-]*)(\d+):(\d+)/) {
      ($year, $month, $day, $hour, $minute, $second, $dseconds,
          $dirutc, $hoursutc, $minutesutc) = ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10);
    } elsif ($value && $value =~ /(\d+)-(\d+)-(\d+),(\d+):(\d+):(\d+)/) {
      ($year, $month, $day, $hour, $minute, $second, $dseconds) =
          ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10);
    } else {
      ($second, $minute, $hour, $day, $month, $year, $wday, $yday, $isdst) =
          gmtime(time);
      $year -= 1900;
      $month += 1;
    }
    my $epoch = timegm($second, $minute, $hour, $day, $month-1, $year-1900);
    # 1992-5-26,13:30:15.0,-4:0 = Tuesday May 26, 1992 at 1:30:15 PM EDT
    # Eastern Daylight Time (EDT) is 4 hours behind Coordinated Universal Time (UTC)
    # 13:30:15 EDT = 17:30:15 UTC
    # wir haben gesetzt timegm(13:30:15), d.h. man muss von epoch noch 4 stunden abziehen
    #
    if ($hoursutc || $minutesutc) {
      if ($dirutc && $dirutc eq "+") {
        $epoch -= ($hoursutc * 3600 + $minutesutc);
      } elsif ($dirutc && $dirutc eq "-") {
        $epoch += ($hoursutc * 3600 + $minutesutc);
      }
    }
    return $epoch;
  },
};



package Monitoring::GLPlugin::SNMP::MibsAndOids::RITTALCMCIIIMIB;

$Monitoring::GLPlugin::SNMP::MibsAndOids::origin->{'RITTAL-CMC-III-MIB'} = {
  url => '',
  name => 'RITTAL-CMC-III-MIB',
};

$Monitoring::GLPlugin::SNMP::MibsAndOids::mib_ids->{'RITTAL-CMC-III-MIB'} = 
  '1.3.6.1.4.1.2606.7';

$Monitoring::GLPlugin::SNMP::MibsAndOids::mibs_and_oids->{'RITTAL-CMC-III-MIB'} = {
  'cmcIII' => '1.3.6.1.4.1.2606.7',
  'cmcIIINotifications' => '1.3.6.1.4.1.2606.7.0',
  'cmcIIIMibRev' => '1.3.6.1.4.1.2606.7.1',
  'cmcIIIMibMajRev' => '1.3.6.1.4.1.2606.7.1.1',
  'cmcIIIMibMinRev' => '1.3.6.1.4.1.2606.7.1.2',
  'cmcIIIAgentRev' => '1.3.6.1.4.1.2606.7.1.3',
  'cmcIIIUnit' => '1.3.6.1.4.1.2606.7.2',
  'cmcIIIUnitStatus' => '1.3.6.1.4.1.2606.7.2.1',
  'cmcIIIUnitStatusDefinition' => 'RITTAL-CMC-III-MIB::cmcIIIUnitStatus',
  'cmcIIIUnitURL' => '1.3.6.1.4.1.2606.7.2.2',
  'cmcIIIUnitHWRev' => '1.3.6.1.4.1.2606.7.2.3',
  'cmcIIIUnitFWRev' => '1.3.6.1.4.1.2606.7.2.4',
  'cmcIIIUnitOSRev' => '1.3.6.1.4.1.2606.7.2.5',
  'cmcIIIUnitSerial' => '1.3.6.1.4.1.2606.7.2.6',
  'cmcIIIUnitProd' => '1.3.6.1.4.1.2606.7.2.7',
  'cmcIIIUnitType' => '1.3.6.1.4.1.2606.7.2.8',
  'cmcIIIUnitTypeDefinition' => 'RITTAL-CMC-III-MIB::cmcIIIUnitType',
  'cmcIIIUnitCurrentSource' => '1.3.6.1.4.1.2606.7.2.9',
  'cmcIIIUnitCurrentSourceDefinition' => 'RITTAL-CMC-III-MIB::cmcIIIUnitCurrentSource',
  'cmcIIICan0CurrentConsumption' => '1.3.6.1.4.1.2606.7.2.10',
  'cmcIIICan1CurrentConsumption' => '1.3.6.1.4.1.2606.7.2.11',
  'cmcIIIUnitUpTime' => '1.3.6.1.4.1.2606.7.2.12',
  'cmcIIISetup' => '1.3.6.1.4.1.2606.7.3',
  'cmcIIILastChangeSetup' => '1.3.6.1.4.1.2606.7.3.1',
  'cmcIIISetupGeneral' => '1.3.6.1.4.1.2606.7.3.2',
  'cmcIIISetTempUnit' => '1.3.6.1.4.1.2606.7.3.2.1',
  'cmcIIISetTempUnitDefinition' => 'RITTAL-CMC-III-MIB::cmcIIISetTempUnit',
  'cmcIIISetBeeper' => '1.3.6.1.4.1.2606.7.3.2.2',
  'cmcIIISetBeeperDefinition' => 'RITTAL-CMC-III-MIB::cmcIIISetBeeper',
  'cmcIIIQuitRelay' => '1.3.6.1.4.1.2606.7.3.2.3',
  'cmcIIIQuitRelayDefinition' => 'RITTAL-CMC-III-MIB::cmcIIIQuitRelay',
  'cmcIIILogicRelay' => '1.3.6.1.4.1.2606.7.3.2.4',
  'cmcIIILogicRelayDefinition' => 'RITTAL-CMC-III-MIB::cmcIIILogicRelay',
  'cmcIIIUnitMsgRelay' => '1.3.6.1.4.1.2606.7.3.2.5',
  'cmcIIIUnitMsgRelayDefinition' => 'RITTAL-CMC-III-MIB::cmcIIIUnitMsgRelay',
  'cmcIIIFunctionRelay' => '1.3.6.1.4.1.2606.7.3.2.6',
  'cmcIIIFunctionRelayDefinition' => 'RITTAL-CMC-III-MIB::cmcIIIFunctionRelay',
  'cmcIIITimeZone' => '1.3.6.1.4.1.2606.7.3.2.7',
  'cmcIIISetupDate' => '1.3.6.1.4.1.2606.7.3.2.8',
  'cmcIIISetupTime' => '1.3.6.1.4.1.2606.7.3.2.9',
  'cmcIIIWebAccess' => '1.3.6.1.4.1.2606.7.3.2.10',
  'cmcIIIHttpPort' => '1.3.6.1.4.1.2606.7.3.2.11',
  'cmcIIIHttpsPort' => '1.3.6.1.4.1.2606.7.3.2.12',
  'cmcIIIFtpAccess' => '1.3.6.1.4.1.2606.7.3.2.13',
  'cmcIIIFtpAccessDefinition' => 'RITTAL-CMC-III-MIB::cmcIIIFtpAccess',
  'cmcIIIFtpPort' => '1.3.6.1.4.1.2606.7.3.2.14',
  'cmcIIISshAccess' => '1.3.6.1.4.1.2606.7.3.2.15',
  'cmcIIISshAccessDefinition' => 'RITTAL-CMC-III-MIB::cmcIIISshAccess',
  'cmcIIISshPort' => '1.3.6.1.4.1.2606.7.3.2.16',
  'cmcIIITelnetAccess' => '1.3.6.1.4.1.2606.7.3.2.17',
  'cmcIIITelnetAccessDefinition' => 'RITTAL-CMC-III-MIB::cmcIIITelnetAccess',
  'cmcIIITelnetPort' => '1.3.6.1.4.1.2606.7.3.2.18',
  'cmcIIILanguage' => '1.3.6.1.4.1.2606.7.3.2.19',
  'cmcIIILanguageDefinition' => 'RITTAL-CMC-III-MIB::cmcIIILanguage',
  'cmcIIIOpcUaAccess' => '1.3.6.1.4.1.2606.7.3.2.20',
  'cmcIIIOpcUaAccessDefinition' => 'RITTAL-CMC-III-MIB::cmcIIIOpcUaAccess',
  'cmcIIIOpcUaPort' => '1.3.6.1.4.1.2606.7.3.2.21',
  'cmcIIISetupTimer' => '1.3.6.1.4.1.2606.7.3.3',
  'cmcIIINumberOfTimers' => '1.3.6.1.4.1.2606.7.3.3.1',
  'cmcIIITimerTable' => '1.3.6.1.4.1.2606.7.3.3.2',
  'cmcIIITimerEntry' => '1.3.6.1.4.1.2606.7.3.3.2.1',
  'cmcIIITimerIndex' => '1.3.6.1.4.1.2606.7.3.3.2.1.1',
  'cmcIIITimerStatus' => '1.3.6.1.4.1.2606.7.3.3.2.1.2',
  'cmcIIITimerStatusDefinition' => 'RITTAL-CMC-III-MIB::cmcIIITimerStatus',
  'cmcIIITimerDayOfWeek' => '1.3.6.1.4.1.2606.7.3.3.2.1.3',
  'cmcIIITimerDayOfWeekDefinition' => 'RITTAL-CMC-III-MIB::cmcIIITimerDayOfWeek',
  'cmcIIITimeOn' => '1.3.6.1.4.1.2606.7.3.3.2.1.4',
  'cmcIIITimeOff' => '1.3.6.1.4.1.2606.7.3.3.2.1.5',
  'cmcIIITimeControl' => '1.3.6.1.4.1.2606.7.3.3.2.1.6',
  'cmcIIITimeControlDefinition' => 'RITTAL-CMC-III-MIB::cmcIIITimeControl',
  'cmcIIITimerFunction' => '1.3.6.1.4.1.2606.7.3.3.2.1.7',
  'cmcIIITimerFunctionDefinition' => 'RITTAL-CMC-III-MIB::cmcIIITimerFunction',
  'cmcIIISetupTrap' => '1.3.6.1.4.1.2606.7.3.4',
  'cmcIIINumberOfTrapReceivers' => '1.3.6.1.4.1.2606.7.3.4.1',
  'cmcIIITrapReceiverTable' => '1.3.6.1.4.1.2606.7.3.4.2',
  'cmcIIITrapReceiverEntry' => '1.3.6.1.4.1.2606.7.3.4.2.1',
  'cmcIIITrapReceiverIndex' => '1.3.6.1.4.1.2606.7.3.4.2.1.1',
  'cmcIIITrapReceiverStatus' => '1.3.6.1.4.1.2606.7.3.4.2.1.2',
  'cmcIIITrapReceiverStatusDefinition' => 'RITTAL-CMC-III-MIB::cmcIIITrapReceiverStatus',
  'cmcIIITrapReceiverIpAddress' => '1.3.6.1.4.1.2606.7.3.4.2.1.3',
  'cmcIIISetupSMTP' => '1.3.6.1.4.1.2606.7.3.5',
  'cmcIIISmtpStatus' => '1.3.6.1.4.1.2606.7.3.5.1',
  'cmcIIISmtpStatusDefinition' => 'RITTAL-CMC-III-MIB::cmcIIISmtpStatus',
  'cmcIIISmtpServer' => '1.3.6.1.4.1.2606.7.3.5.2',
  'cmcIIISmtpPort' => '1.3.6.1.4.1.2606.7.3.5.3',
  'cmcIIISmtpAuth' => '1.3.6.1.4.1.2606.7.3.5.4',
  'cmcIIISmtpAuthDefinition' => 'RITTAL-CMC-III-MIB::cmcIIISmtpAuth',
  'cmcIIISmtpUsername' => '1.3.6.1.4.1.2606.7.3.5.5',
  'cmcIIISmtpPassword' => '1.3.6.1.4.1.2606.7.3.5.6',
  'cmcIIISmtpSender' => '1.3.6.1.4.1.2606.7.3.5.7',
  'cmcIIISmtpReply' => '1.3.6.1.4.1.2606.7.3.5.8',
  'cmcIIINumberOfSmtpReceivers' => '1.3.6.1.4.1.2606.7.3.5.9',
  'cmcIIISmtpReceiverTable' => '1.3.6.1.4.1.2606.7.3.5.10',
  'cmcIIISmtpReceiverEntry' => '1.3.6.1.4.1.2606.7.3.5.10.1',
  'cmcIIISmtpReceiverIndex' => '1.3.6.1.4.1.2606.7.3.5.10.1.1',
  'cmcIIISmtpReceiverStatus' => '1.3.6.1.4.1.2606.7.3.5.10.1.2',
  'cmcIIISmtpReceiverStatusDefinition' => 'RITTAL-CMC-III-MIB::cmcIIISmtpReceiverStatus',
  'cmcIIISmtpReceiverAddress' => '1.3.6.1.4.1.2606.7.3.5.10.1.3',
  'cmcIIISetupSMS' => '1.3.6.1.4.1.2606.7.3.6',
  'cmcIIISmsStatus' => '1.3.6.1.4.1.2606.7.3.6.1',
  'cmcIIISmsStatusDefinition' => 'RITTAL-CMC-III-MIB::cmcIIISmsStatus',
  'cmcIIISmsPIN' => '1.3.6.1.4.1.2606.7.3.6.2',
  'cmcIIISmsService' => '1.3.6.1.4.1.2606.7.3.6.3',
  'cmcIIISmsMSN' => '1.3.6.1.4.1.2606.7.3.6.4',
  'cmcIIISmsPreDial' => '1.3.6.1.4.1.2606.7.3.6.5',
  'cmcIIINumberOfSmsReceivers' => '1.3.6.1.4.1.2606.7.3.6.6',
  'cmcIIISmsReceiverTable' => '1.3.6.1.4.1.2606.7.3.6.7',
  'cmcIIISmsReceiverEntry' => '1.3.6.1.4.1.2606.7.3.6.7.1',
  'cmcIIISmsReceiverIndex' => '1.3.6.1.4.1.2606.7.3.6.7.1.1',
  'cmcIIISmsReceiverStatus' => '1.3.6.1.4.1.2606.7.3.6.7.1.2',
  'cmcIIISmsReceiverStatusDefinition' => 'RITTAL-CMC-III-MIB::cmcIIISmsReceiverStatus',
  'cmcIIISmsReceiverNumber' => '1.3.6.1.4.1.2606.7.3.6.7.1.3',
  'cmcIIISetupSysLog' => '1.3.6.1.4.1.2606.7.3.7',
  'cmcIIISysLogStatus' => '1.3.6.1.4.1.2606.7.3.7.1',
  'cmcIIISysLogStatusDefinition' => 'RITTAL-CMC-III-MIB::cmcIIISysLogStatus',
  'cmcIIISysLogFacility' => '1.3.6.1.4.1.2606.7.3.7.2',
  'cmcIIISysLogFacilityDefinition' => 'RITTAL-CMC-III-MIB::cmcIIISysLogFacility',
  'cmcIIISysLogServer1' => '1.3.6.1.4.1.2606.7.3.7.3',
  'cmcIIISysLogServer2' => '1.3.6.1.4.1.2606.7.3.7.4',
  'cmcIIISetupNTP' => '1.3.6.1.4.1.2606.7.3.8',
  'cmcIIINtpStatus' => '1.3.6.1.4.1.2606.7.3.8.1',
  'cmcIIINtpStatusDefinition' => 'RITTAL-CMC-III-MIB::cmcIIINtpStatus',
  'cmcIIINtpTimeZone' => '1.3.6.1.4.1.2606.7.3.8.2',
  'cmcIIINtpServer1' => '1.3.6.1.4.1.2606.7.3.8.3',
  'cmcIIINtpServer2' => '1.3.6.1.4.1.2606.7.3.8.4',
  'cmcIIISetupLDAP' => '1.3.6.1.4.1.2606.7.3.9',
  'cmcIIILdapStatus' => '1.3.6.1.4.1.2606.7.3.9.1',
  'cmcIIILdapStatusDefinition' => 'RITTAL-CMC-III-MIB::cmcIIILdapStatus',
  'cmcIIILdapServer' => '1.3.6.1.4.1.2606.7.3.9.2',
  'cmcIIILdapBindDN' => '1.3.6.1.4.1.2606.7.3.9.3',
  'cmcIIILdapBindPW' => '1.3.6.1.4.1.2606.7.3.9.4',
  'cmcIIILdapUserBase' => '1.3.6.1.4.1.2606.7.3.9.5',
  'cmcIIILdapUserSearch' => '1.3.6.1.4.1.2606.7.3.9.6',
  'cmcIIILdapUserAttrib' => '1.3.6.1.4.1.2606.7.3.9.7',
  'cmcIIILdapGroupBase' => '1.3.6.1.4.1.2606.7.3.9.8',
  'cmcIIILdapGroupSearch' => '1.3.6.1.4.1.2606.7.3.9.9',
  'cmcIIILdapGroupAttrib' => '1.3.6.1.4.1.2606.7.3.9.10',
  'cmcIIISetupShutdown' => '1.3.6.1.4.1.2606.7.3.10',
  'cmcIIINumberOfShutdowns' => '1.3.6.1.4.1.2606.7.3.10.1',
  'cmcIIIShutdownTable' => '1.3.6.1.4.1.2606.7.3.10.2',
  'cmcIIIShutdownEntry' => '1.3.6.1.4.1.2606.7.3.10.2.1',
  'cmcIIIShutdownIndex' => '1.3.6.1.4.1.2606.7.3.10.2.1.1',
  'cmcIIIShutdownServer' => '1.3.6.1.4.1.2606.7.3.10.2.1.2',
  'cmcIIIShutdownName' => '1.3.6.1.4.1.2606.7.3.10.2.1.3',
  'cmcIIIShutdownPort' => '1.3.6.1.4.1.2606.7.3.10.2.1.4',
  'cmcIIIShutdownDelay' => '1.3.6.1.4.1.2606.7.3.10.2.1.5',
  'cmcIIIShutdownEnabled' => '1.3.6.1.4.1.2606.7.3.10.2.1.6',
  'cmcIIIShutdownEnabledDefinition' => 'RITTAL-CMC-III-MIB::cmcIIIShutdownEnabled',
  'cmcIIISetupModbus' => '1.3.6.1.4.1.2606.7.3.11',
  'cmcIIIModbusStatus' => '1.3.6.1.4.1.2606.7.3.11.1',
  'cmcIIIModbusStatusDefinition' => 'RITTAL-CMC-III-MIB::cmcIIIModbusStatus',
  'cmcIIIModbusPort' => '1.3.6.1.4.1.2606.7.3.11.2',
  'cmcIIINumberOfModbusSources' => '1.3.6.1.4.1.2606.7.3.11.3',
  'cmcIIIModbusTable' => '1.3.6.1.4.1.2606.7.3.11.4',
  'cmcIIIModbusEntry' => '1.3.6.1.4.1.2606.7.3.11.4.1',
  'cmcIIIModbusIndex' => '1.3.6.1.4.1.2606.7.3.11.4.1.1',
  'cmcIIIModbusAccess' => '1.3.6.1.4.1.2606.7.3.11.4.1.2',
  'cmcIIIModbusAccessDefinition' => 'RITTAL-CMC-III-MIB::cmcIIIModbusAccess',
  'cmcIIIModbusSource' => '1.3.6.1.4.1.2606.7.3.11.4.1.3',
  'cmcIIIDevice' => '1.3.6.1.4.1.2606.7.4',
  'cmcIIIDevs' => '1.3.6.1.4.1.2606.7.4.1',
  'cmcIIIDevInfo' => '1.3.6.1.4.1.2606.7.4.1.1',
  'cmcIIIOverallDevStatus' => '1.3.6.1.4.1.2606.7.4.1.1.1',
  'cmcIIIOverallDevStatusDefinition' => 'RITTAL-CMC-III-MIB::cmcIIIOverallDevStatus',
  'cmcIIINumberOfDevs' => '1.3.6.1.4.1.2606.7.4.1.1.2',
  'cmcIIILastChangeOverallDevStatus' => '1.3.6.1.4.1.2606.7.4.1.1.3',
  'cmcIIILastChangeNumberOfDevs' => '1.3.6.1.4.1.2606.7.4.1.1.4',
  'cmcIIILastChangeDevSettings' => '1.3.6.1.4.1.2606.7.4.1.1.5',
  'cmcIIILastChangeDevs' => '1.3.6.1.4.1.2606.7.4.1.1.6',
  'cmcIIIDevTable' => '1.3.6.1.4.1.2606.7.4.1.2',
  'cmcIIIDevEntry' => '1.3.6.1.4.1.2606.7.4.1.2.1',
  'cmcIIIDevIndex' => '1.3.6.1.4.1.2606.7.4.1.2.1.1',
  'cmcIIIDevName' => '1.3.6.1.4.1.2606.7.4.1.2.1.2',
  'cmcIIIDevAlias' => '1.3.6.1.4.1.2606.7.4.1.2.1.3',
  'cmcIIIDevType' => '1.3.6.1.4.1.2606.7.4.1.2.1.4',
  'cmcIIIDevNodeID' => '1.3.6.1.4.1.2606.7.4.1.2.1.5',
  'cmcIIIDevStatus' => '1.3.6.1.4.1.2606.7.4.1.2.1.6',
  'cmcIIIDevStatusDefinition' => 'RITTAL-CMC-III-MIB::cmcIIIDevStatus',
  'cmcIIIDevArtNr' => '1.3.6.1.4.1.2606.7.4.1.2.1.7',
  'cmcIIIDevLocation' => '1.3.6.1.4.1.2606.7.4.1.2.1.8',
  'cmcIIIDevBus' => '1.3.6.1.4.1.2606.7.4.1.2.1.9',
  'cmcIIIDevBusDefinition' => 'RITTAL-CMC-III-MIB::cmcIIIDevBus',
  'cmcIIIDevPos' => '1.3.6.1.4.1.2606.7.4.1.2.1.10',
  'cmcIIIDevFW' => '1.3.6.1.4.1.2606.7.4.1.2.1.11',
  'cmcIIIDevHW' => '1.3.6.1.4.1.2606.7.4.1.2.1.12',
  'cmcIIIDevSerial' => '1.3.6.1.4.1.2606.7.4.1.2.1.13',
  'cmcIIIDevProductWeek' => '1.3.6.1.4.1.2606.7.4.1.2.1.14',
  'cmcIIIDevLastChange' => '1.3.6.1.4.1.2606.7.4.1.2.1.15',
  'cmcIIIDevURL' => '1.3.6.1.4.1.2606.7.4.1.2.1.16',
  'cmcIIIDevNumberOfVars' => '1.3.6.1.4.1.2606.7.4.1.2.1.17',
  'cmcIIIDevNumberOfMsgs' => '1.3.6.1.4.1.2606.7.4.1.2.1.18',
  'cmcIIIDevStatusText' => '1.3.6.1.4.1.2606.7.4.1.2.1.19',
  'cmcIIIDevCurrentMinConsumption' => '1.3.6.1.4.1.2606.7.4.1.2.1.20',
  'cmcIIIDevCurrentMaxConsumption' => '1.3.6.1.4.1.2606.7.4.1.2.1.21',
  'cmcIIIDevEntPhysicalIndex' => '1.3.6.1.4.1.2606.7.4.1.2.1.22',
  'cmcIIIDevAssembly' => '1.3.6.1.4.1.2606.7.4.1.2.1.23',
  'cmcIIIVars' => '1.3.6.1.4.1.2606.7.4.2',
  'cmcIIIVarInfo' => '1.3.6.1.4.1.2606.7.4.2.1',
  'cmcIIINumberOfVars' => '1.3.6.1.4.1.2606.7.4.2.1.1',
  'cmcIIILastChangeNumberOfVars' => '1.3.6.1.4.1.2606.7.4.2.1.2',
  'cmcIIILastChangeVarSettings' => '1.3.6.1.4.1.2606.7.4.2.1.3',
  'cmcIIILastChangeVars' => '1.3.6.1.4.1.2606.7.4.2.1.4',
  'cmcIIIVarTable' => '1.3.6.1.4.1.2606.7.4.2.2',
  'cmcIIIVarEntry' => '1.3.6.1.4.1.2606.7.4.2.2.1',
  'cmcIIIVarDeviceIndex' => '1.3.6.1.4.1.2606.7.4.2.2.1.1',
  'cmcIIIVarIndex' => '1.3.6.1.4.1.2606.7.4.2.2.1.2',
  'cmcIIIVarName' => '1.3.6.1.4.1.2606.7.4.2.2.1.3',
  'cmcIIIVarType' => '1.3.6.1.4.1.2606.7.4.2.2.1.4',
  'cmcIIIVarTypeDefinition' => 'RITTAL-CMC-III-MIB::cmcIIIVarType',
  'cmcIIIVarUnit' => '1.3.6.1.4.1.2606.7.4.2.2.1.5',
  'cmcIIIVarDatatype' => '1.3.6.1.4.1.2606.7.4.2.2.1.6',
  'cmcIIIVarDatatypeDefinition' => 'RITTAL-CMC-III-MIB::cmcIIIVarDatatype',
  'cmcIIIVarScale' => '1.3.6.1.4.1.2606.7.4.2.2.1.7',
  'cmcIIIVarConstraints' => '1.3.6.1.4.1.2606.7.4.2.2.1.8',
  'cmcIIIVarSteps' => '1.3.6.1.4.1.2606.7.4.2.2.1.9',
  'cmcIIIVarValueStr' => '1.3.6.1.4.1.2606.7.4.2.2.1.10',
  'cmcIIIVarValueInt' => '1.3.6.1.4.1.2606.7.4.2.2.1.11',
  'cmcIIIVarLastChange' => '1.3.6.1.4.1.2606.7.4.2.2.1.12',
  'cmcIIIVarAccess' => '1.3.6.1.4.1.2606.7.4.2.2.1.13',
  'cmcIIIVarAccessDefinition' => 'RITTAL-CMC-III-MIB::cmcIIIVarAccess',
  'cmcIIIVarQuality' => '1.3.6.1.4.1.2606.7.4.2.2.1.14',
  'cmcIIIVarQualityDefinition' => 'RITTAL-CMC-III-MIB::cmcIIIVarQuality',
  'cmcIIIVarEntPhysicalIndex' => '1.3.6.1.4.1.2606.7.4.2.2.1.15',
  'cmcIIIMsgs' => '1.3.6.1.4.1.2606.7.4.3',
  'cmcIIIMsgInfo' => '1.3.6.1.4.1.2606.7.4.3.1',
  'cmcIIIOverallMsgStatus' => '1.3.6.1.4.1.2606.7.4.3.1.1',
  'cmcIIIOverallMsgStatusDefinition' => 'RITTAL-CMC-III-MIB::cmcIIIOverallMsgStatus',
  'cmcIIINumberOfMsgs' => '1.3.6.1.4.1.2606.7.4.3.1.2',
  'cmcIIILastChangeOverallMsgStatus' => '1.3.6.1.4.1.2606.7.4.3.1.3',
  'cmcIIILastChangeNumberOfMsgs' => '1.3.6.1.4.1.2606.7.4.3.1.4',
  'cmcIIILastChangeMsgSettings' => '1.3.6.1.4.1.2606.7.4.3.1.5',
  'cmcIIIMsgTable' => '1.3.6.1.4.1.2606.7.4.3.2',
  'cmcIIIMsgEntry' => '1.3.6.1.4.1.2606.7.4.3.2.1',
  'cmcIIIMsgDeviceIndex' => '1.3.6.1.4.1.2606.7.4.3.2.1.1',
  'cmcIIIMsgIndex' => '1.3.6.1.4.1.2606.7.4.3.2.1.2',
  'cmcIIIMsgText' => '1.3.6.1.4.1.2606.7.4.3.2.1.3',
  'cmcIIIMsgStatus' => '1.3.6.1.4.1.2606.7.4.3.2.1.4',
  'cmcIIIMsgStatusDefinition' => 'RITTAL-CMC-III-MIB::cmcIIIMsgStatus',
  'cmcIIIMsgRelay' => '1.3.6.1.4.1.2606.7.4.3.2.1.5',
  'cmcIIIMsgRelayDefinition' => 'RITTAL-CMC-III-MIB::cmcIIIMsgRelay',
  'cmcIIIMsgBeeper' => '1.3.6.1.4.1.2606.7.4.3.2.1.6',
  'cmcIIIMsgBeeperDefinition' => 'RITTAL-CMC-III-MIB::cmcIIIMsgBeeper',
  'cmcIIIMsgTrap' => '1.3.6.1.4.1.2606.7.4.3.2.1.7',
  'cmcIIIMsgSMTP' => '1.3.6.1.4.1.2606.7.4.3.2.1.8',
  'cmcIIIMsgSMS' => '1.3.6.1.4.1.2606.7.4.3.2.1.9',
  'cmcIIIMsgQuit' => '1.3.6.1.4.1.2606.7.4.3.2.1.10',
  'cmcIIIMsgQuitDefinition' => 'RITTAL-CMC-III-MIB::cmcIIIMsgQuit',
  'cmcIIIMsgDelay' => '1.3.6.1.4.1.2606.7.4.3.2.1.11',
  'cmcIIIMsgSchedAlarmOff' => '1.3.6.1.4.1.2606.7.4.3.2.1.12',
  'cmcIIIMsgQuality' => '1.3.6.1.4.1.2606.7.4.3.2.1.13',
  'cmcIIIMsgQualityDefinition' => 'RITTAL-CMC-III-MIB::cmcIIIMsgQuality',
  'cmcIIIMsgVarIdx' => '1.3.6.1.4.1.2606.7.4.3.2.1.14',
  'cmcIIIMsgVarValueIdx' => '1.3.6.1.4.1.2606.7.4.3.2.1.15',
  'cmcIIIMsgStatusText' => '1.3.6.1.4.1.2606.7.4.3.2.1.16',
  'cmcIIIDrcs' => '1.3.6.1.4.1.2606.7.4.4',
  'cmcIIIDrcInfo' => '1.3.6.1.4.1.2606.7.4.4.1',
  'cmcIIIOverallDrcStatus' => '1.3.6.1.4.1.2606.7.4.4.1.1',
  'cmcIIIOverallDrcStatusDefinition' => 'RITTAL-CMC-III-MIB::cmcIIIOverallDrcStatus',
  'cmcIIINumberOfDrcs' => '1.3.6.1.4.1.2606.7.4.4.1.2',
  'cmcIIIOverallLastChangeDrcStatus' => '1.3.6.1.4.1.2606.7.4.4.1.3',
  'cmcIIILastChangeNumberOfDrcs' => '1.3.6.1.4.1.2606.7.4.4.1.4',
  'cmcIIIOverallDrcUtilization' => '1.3.6.1.4.1.2606.7.4.4.1.5',
  'cmcIIIOverallDrcPower' => '1.3.6.1.4.1.2606.7.4.4.1.6',
  'cmcIIIDrcTable' => '1.3.6.1.4.1.2606.7.4.4.2',
  'cmcIIIDrcEntry' => '1.3.6.1.4.1.2606.7.4.4.2.1',
  'cmcIIIDrcIndex' => '1.3.6.1.4.1.2606.7.4.4.2.1.1',
  'cmcIIIDrcStatus' => '1.3.6.1.4.1.2606.7.4.4.2.1.2',
  'cmcIIIDrcStatusDefinition' => 'RITTAL-CMC-III-MIB::cmcIIIDrcStatus',
  'cmcIIILastChangeDrcStatus' => '1.3.6.1.4.1.2606.7.4.4.2.1.3',
  'cmcIIIDrcUnitName' => '1.3.6.1.4.1.2606.7.4.4.2.1.4',
  'cmcIIIDrcLocation' => '1.3.6.1.4.1.2606.7.4.4.2.1.5',
  'cmcIIIDrcBuilding' => '1.3.6.1.4.1.2606.7.4.4.2.1.6',
  'cmcIIIDrcLevel' => '1.3.6.1.4.1.2606.7.4.4.2.1.7',
  'cmcIIIDrcRoom' => '1.3.6.1.4.1.2606.7.4.4.2.1.8',
  'cmcIIIDrcRow' => '1.3.6.1.4.1.2606.7.4.4.2.1.9',
  'cmcIIIDrcRackNr' => '1.3.6.1.4.1.2606.7.4.4.2.1.10',
  'cmcIIIDrcMfgDate' => '1.3.6.1.4.1.2606.7.4.4.2.1.11',
  'cmcIIIDrcSerialNr' => '1.3.6.1.4.1.2606.7.4.4.2.1.12',
  'cmcIIIDrcOpHours' => '1.3.6.1.4.1.2606.7.4.4.2.1.13',
  'cmcIIIDrcNumberOfRU' => '1.3.6.1.4.1.2606.7.4.4.2.1.14',
  'cmcIIIDrcUtilization' => '1.3.6.1.4.1.2606.7.4.4.2.1.15',
  'cmcIIITotalDrcPower' => '1.3.6.1.4.1.2606.7.4.4.2.1.16',
  'cmcIIIDrcSwController' => '1.3.6.1.4.1.2606.7.4.4.2.1.17',
  'cmcIIIDrcSwBootloader' => '1.3.6.1.4.1.2606.7.4.4.2.1.18',
  'cmcIIIDrcFwController' => '1.3.6.1.4.1.2606.7.4.4.2.1.19',
  'cmcIIIDrcSwAntenna' => '1.3.6.1.4.1.2606.7.4.4.2.1.20',
  'cmcIIIDrcSwAntennaBL' => '1.3.6.1.4.1.2606.7.4.4.2.1.21',
  'cmcIIIDrcCommand' => '1.3.6.1.4.1.2606.7.4.4.2.1.22',
  'cmcIIIDrcCommandDefinition' => 'RITTAL-CMC-III-MIB::cmcIIIDrcCommand',
  'cmcIIITagTable' => '1.3.6.1.4.1.2606.7.4.4.3',
  'cmcIIITagEntry' => '1.3.6.1.4.1.2606.7.4.4.3.1',
  'cmcIIITagDrcIndex' => '1.3.6.1.4.1.2606.7.4.4.3.1.1',
  'cmcIIITagRuIndex' => '1.3.6.1.4.1.2606.7.4.4.3.1.2',
  'cmcIIITagStatus' => '1.3.6.1.4.1.2606.7.4.4.3.1.3',
  'cmcIIITagStatusDefinition' => 'RITTAL-CMC-III-MIB::cmcIIITagStatus',
  'cmcIIITagUID' => '1.3.6.1.4.1.2606.7.4.4.3.1.4',
  'cmcIIITagPosition' => '1.3.6.1.4.1.2606.7.4.4.3.1.5',
  'cmcIIITagUnitFormfactor' => '1.3.6.1.4.1.2606.7.4.4.3.1.6',
  'cmcIIITagOffset' => '1.3.6.1.4.1.2606.7.4.4.3.1.7',
  'cmcIIITagRuPosition' => '1.3.6.1.4.1.2606.7.4.4.3.1.8',
  'cmcIIITagDeviceClass' => '1.3.6.1.4.1.2606.7.4.4.3.1.9',
  'cmcIIITagDeviceClassDefinition' => 'RITTAL-CMC-III-MIB::cmcIIITagDeviceClass',
  'cmcIIITagDescName' => '1.3.6.1.4.1.2606.7.4.4.3.1.10',
  'cmcIIITagManufacturer' => '1.3.6.1.4.1.2606.7.4.4.3.1.11',
  'cmcIIITagType' => '1.3.6.1.4.1.2606.7.4.4.3.1.12',
  'cmcIIITagSerialNumber' => '1.3.6.1.4.1.2606.7.4.4.3.1.13',
  'cmcIIITagVendor' => '1.3.6.1.4.1.2606.7.4.4.3.1.14',
  'cmcIIITagMac1' => '1.3.6.1.4.1.2606.7.4.4.3.1.15',
  'cmcIIITagMac2' => '1.3.6.1.4.1.2606.7.4.4.3.1.16',
  'cmcIIITagService' => '1.3.6.1.4.1.2606.7.4.4.3.1.17',
  'cmcIIITagDeviceName' => '1.3.6.1.4.1.2606.7.4.4.3.1.18',
  'cmcIIITagInventoryCode' => '1.3.6.1.4.1.2606.7.4.4.3.1.19',
  'cmcIIITagPower' => '1.3.6.1.4.1.2606.7.4.4.3.1.20',
  'cmcIIITagCurrent' => '1.3.6.1.4.1.2606.7.4.4.3.1.21',
  'cmcIIITagVoltage' => '1.3.6.1.4.1.2606.7.4.4.3.1.22',
  'cmcIIITagLastService' => '1.3.6.1.4.1.2606.7.4.4.3.1.23',
  'cmcIIITagNextService' => '1.3.6.1.4.1.2606.7.4.4.3.1.24',
  'cmcIIITagLastUpdate' => '1.3.6.1.4.1.2606.7.4.4.3.1.25',
  'cmcIIITagNextUpdate' => '1.3.6.1.4.1.2606.7.4.4.3.1.26',
  'cmcIIITagInitialStart' => '1.3.6.1.4.1.2606.7.4.4.3.1.27',
  'cmcIIITagCustom' => '1.3.6.1.4.1.2606.7.4.4.3.1.28',
  'cmcIIITagCommand' => '1.3.6.1.4.1.2606.7.4.4.3.1.29',
  'cmcIIITagCommandDefinition' => 'RITTAL-CMC-III-MIB::cmcIIITagCommand',
  'cmcIIITagStart' => '1.3.6.1.4.1.2606.7.4.4.3.1.30',
  'cmcIIITagEnd' => '1.3.6.1.4.1.2606.7.4.4.3.1.31',
  'cmcIIITagAntennaMap' => '1.3.6.1.4.1.2606.7.4.4.3.1.32',
  'cmcIIIControl' => '1.3.6.1.4.1.2606.7.5',
  'cmcIIIQuitUnit' => '1.3.6.1.4.1.2606.7.5.1',
  'cmcIIIQuitUnitDefinition' => 'RITTAL-CMC-III-MIB::cmcIIIQuitUnit',
  'cmcIIISmsQueue' => '1.3.6.1.4.1.2606.7.5.2',
  'cmcIIIConformance' => '1.3.6.1.4.1.2606.7.6',
  'cmcIIIMibCompliances' => '1.3.6.1.4.1.2606.7.6.1',
  'cmcIIIMibGroups' => '1.3.6.1.4.1.2606.7.6.2',
};

$Monitoring::GLPlugin::SNMP::MibsAndOids::definitions->{'RITTAL-CMC-III-MIB'} = {
  'cmcIIILanguage' => {
    '1' => 'english',
    '2' => 'german',
    '3' => 'french',
    '4' => 'spanish',
    '5' => 'portuguese',
    '6' => 'russian',
    '7' => 'chinese',
    '8' => 'japanese',
  },
  'cmcIIIDevStatus' => {
    '1' => 'notAvail',
    '2' => 'ok',
    '3' => 'detect',
    '4' => 'lost',
    '5' => 'changed',
    '6' => 'error',
    '7' => 'fwUpdate',
    '8' => 'fwUpdateRun',
  },
  'cmcIIISetTempUnit' => {
    '1' => 'celsius',
    '2' => 'fahrenheit',
  },
  'cmcIIIUnitMsgRelay' => {
    '1' => 'disabled',
    '2' => 'enabled',
  },
  'cmcIIITimerDayOfWeek' => {
    '1' => 'sun',
    '2' => 'mon',
    '3' => 'tue',
    '4' => 'wed',
    '5' => 'thu',
    '6' => 'fri',
    '7' => 'sat',
    '8' => 'sat2sun',
    '9' => 'mon2fri',
    '10' => 'mon2sun',
  },
  'cmcIIIOverallDrcStatus' => {
    '1' => 'ok',
    '2' => 'warning',
    '3' => 'alarm',
  },
  'cmcIIIUnitStatus' => {
    '1' => 'ok',
    '2' => 'failed',
    '3' => 'overload',
  },
  'cmcIIIOverallMsgStatus' => {
    '1' => 'ok',
    '2' => 'warning',
    '3' => 'alarm',
  },
  'cmcIIIVarType' => {
    '1' => 'description',
    '2' => 'value',
    '3' => 'setHigh',
    '4' => 'setWarn',
    '5' => 'setLow',
    '6' => 'hysteresis',
    '7' => 'status',
    '8' => 'statusEnum',
    '9' => 'setWarnLow',
    '10' => 'unit',
    '11' => 'alarmDur',
    '12' => 'steps',
    '13' => 'configPar',
    '14' => 'category',
    '15' => 'logic',
    '16' => 'logicEnum',
    '17' => 'setCntrl',
    '18' => 'offset',
    '19' => 'type',
    '20' => 'output',
    '21' => 'outputdelay',
    '22' => 'outputaction',
    '23' => 'outdelayExec',
    '24' => 'outputEnum',
    '25' => 'config',
    '26' => 'configNum',
    '27' => 'configLcpFcs',
    '28' => 'assembly',
    '30' => 'sensitivity',
    '31' => 'accessTime',
    '32' => 'accessLogic',
    '33' => 'position',
    '34' => 'positionEnum',
    '40' => 'outputPWM',
    '41' => 'rotation',
    '42' => 'circuit',
    '43' => 'rizoneArray',
    '44' => 'socketType',
    '45' => 'customValue',
    '50' => 'productDate',
    '51' => 'orderNr',
    '52' => 'devName',
    '53' => 'devLocation',
    '54' => 'currentMin',
    '55' => 'currentMax',
    '80' => 'keycode',
    '81' => 'command',
    '82' => 'commandEnum',
    '83' => 'commandKeypad',
    '84' => 'commandService',
    '85' => 'commandRack',
    '90' => 'unitType',
    '91' => 'swVersion',
    '92' => 'serialNumber',
    '93' => 'mountPos',
    '94' => 'mountPosEnum',
    '95' => 'gsmStatus',
    '96' => 'connected',
    '97' => 'connectedEnum',
    '100' => 'grouping',
    '110' => 'scaleValue',
    '111' => 'scaleUnit',
    '112' => 'scaleStart',
    '113' => 'scaleEnd',
    '120' => 'commandCtrl',
    '121' => 'commandCtrlDP',
    '130' => 'location',
    '131' => 'building',
    '132' => 'level',
    '133' => 'room',
    '134' => 'row',
    '135' => 'rack',
    '136' => 'operatingTime',
    '137' => 'numberOfRU',
    '138' => 'tagsQuit',
    '139' => 'tagsChanged',
    '140' => 'uid',
    '254' => 'ignore',
    '255' => 'illegal',
  },
  'cmcIIITagCommand' => {
    '1' => 'noQuit',
    '2' => 'quit',
    '3' => 'ledAuto',
    '4' => 'ledOn',
  },
  'cmcIIINtpStatus' => {
    '1' => 'disabled',
    '2' => 'enabled',
  },
  'cmcIIIMsgStatus' => {
    '1' => 'notAvail',
    '2' => 'configChanged',
    '3' => 'error',
    '4' => 'ok',
    '5' => 'alarm',
    '6' => 'highWarn',
    '7' => 'lowAlarm',
    '8' => 'highAlarm',
    '9' => 'lowWarn',
    '10' => 'setOff',
    '11' => 'setOn',
    '12' => 'open',
    '13' => 'closed',
    '14' => 'locked',
    '15' => 'unlRemote',
    '16' => 'doorOpen',
    '17' => 'service',
    '18' => 'standby',
    '19' => 'busy',
    '20' => 'noAccess',
    '21' => 'lost',
    '22' => 'detected',
    '23' => 'lowVoltage',
    '24' => 'probeopen',
    '25' => 'probeshort',
    '26' => 'calibration',
    '27' => 'inactive',
    '28' => 'active',
    '29' => 'noPower',
    '30' => 'readOnly',
  },
  'cmcIIITagStatus' => {
    '1' => 'notAvail',
    '2' => 'configChanged',
    '3' => 'error',
    '4' => 'ok',
    '5' => 'alarm',
    '6' => 'highWarn',
    '7' => 'lowAlarm',
    '8' => 'highAlarm',
    '9' => 'lowWarn',
    '10' => 'setOff',
    '11' => 'setOn',
    '12' => 'open',
    '13' => 'closed',
    '14' => 'locked',
    '15' => 'unlRemote',
    '16' => 'doorOpen',
    '17' => 'service',
    '18' => 'standby',
    '19' => 'busy',
    '20' => 'noAccess',
    '21' => 'lost',
    '22' => 'detected',
    '23' => 'lowbatt',
    '24' => 'probeopen',
    '25' => 'probeshort',
    '26' => 'calibration',
    '27' => 'inactive',
    '28' => 'active',
    '29' => 'noPower',
  },
  'cmcIIIQuitUnit' => {
    '1' => 'noQuit',
    '2' => 'quit',
  },
  'cmcIIISmtpAuth' => {
    '1' => 'disabled',
    '2' => 'enabled',
  },
  'cmcIIIModbusStatus' => {
    '1' => 'shutdown',
    '2' => 'readonly',
    '3' => 'readwrite',
  },
  'cmcIIIDrcCommand' => {
    '1' => 'noQuit',
    '2' => 'quit',
    '3' => 'ledAuto',
    '4' => 'ledOnOccupied',
    '5' => 'ledOnFreeRU',
  },
  'cmcIIIUnitCurrentSource' => {
    '1' => 'unknown',
    '2' => 'acAdapter',
    '3' => 'terminalStrip',
    '4' => 'poe',
    '5' => 'usb',
  },
  'cmcIIIModbusAccess' => {
    '1' => 'noaccess',
    '2' => 'readonly',
    '3' => 'readwrite',
  },
  'cmcIIIVarAccess' => {
    '1' => 'none',
    '2' => 'readonly',
    '3' => 'readwrite',
    '4' => 'readwriteswitch',
    '5' => 'readwriteextended',
  },
  'cmcIIISetBeeper' => {
    '1' => 'off',
    '2' => 'on',
  },
  'cmcIIIVarQuality' => {
    '1' => 'undefined',
    '2' => 'ok',
    '3' => 'warning',
    '4' => 'alarm',
    '5' => 'info',
    '21' => 'undefinedNoValue',
    '22' => 'okNoValue',
    '23' => 'warningNoValue',
    '24' => 'alarmNoValue',
    '25' => 'infoNoValue',
  },
  'cmcIIIMsgQuit' => {
    '1' => 'auto',
    '2' => 'manual',
  },
  'cmcIIISmtpStatus' => {
    '1' => 'disabled',
    '2' => 'enabled',
  },
  'cmcIIITrapReceiverStatus' => {
    '1' => 'disabled',
    '2' => 'snmpv1',
    '3' => 'snmpv2c',
    '4' => 'snmpv3',
  },
  'cmcIIITimerFunction' => {
    '1' => 'disKeypads',
    '2' => 'unlDoors',
    '3' => 'disTrapRec1',
    '4' => 'disTrapRec2',
    '5' => 'disTrapRec3',
    '6' => 'disTrapRec4',
    '7' => 'disSMS1',
    '8' => 'disSMS2',
    '9' => 'disSMS3',
    '10' => 'disSMS4',
    '11' => 'schedule1',
    '12' => 'schedule2',
    '13' => 'schedule3',
    '14' => 'schedule4',
    '15' => 'schedule5',
    '16' => 'schedule6',
    '17' => 'schedule7',
    '18' => 'schedule8',
  },
  'cmcIIIOpcUaAccess' => {
    '1' => 'disabled',
    '2' => 'enabled',
  },
  'cmcIIIQuitRelay' => {
    '1' => 'disabled',
    '2' => 'enabled',
  },
  'cmcIIIMsgBeeper' => {
    '1' => 'disable',
    '2' => 'enable',
  },
  'cmcIIISysLogStatus' => {
    '1' => 'disabled',
    '2' => 'enabled',
  },
  'cmcIIITelnetAccess' => {
    '1' => 'disabled',
    '2' => 'enabled',
  },
  'cmcIIIFtpAccess' => {
    '1' => 'disabled',
    '2' => 'enabled',
  },
  'cmcIIIDevBus' => {
    '1' => 'canBus1',
    '2' => 'canBus2',
    '3' => 'virtual',
  },
  'cmcIIISmtpReceiverStatus' => {
    '1' => 'disabled',
    '2' => 'enabled',
  },
  'cmcIIIFunctionRelay' => {
    '1' => 'warningOnly',
    '2' => 'alarmOnly',
    '3' => 'warningOrAlarm',
  },
  'cmcIIILogicRelay' => {
    '1' => 'closeAtAlarm',
    '2' => 'openAtAlarm',
    '3' => 'disabled',
  },
  'cmcIIITimeControl' => {
    '1' => 'disabled',
    '2' => 'enabled',
  },
  'cmcIIIUnitType' => {
    '1' => 'unknown',
    '2' => 'pu',
    '3' => 'compact',
    '4' => 'lcp',
    '5' => 'pdu',
    '6' => 'rms',
  },
  'cmcIIITagDeviceClass' => {
    '1' => 'notDefined',
    '2' => 'blankingPlate',
    '3' => 'componentShelf',
    '4' => 'cableRouting',
    '5' => 'patchPanelCatX',
    '6' => 'patchPanelFibreOptic',
    '7' => 'ups',
    '8' => 'powerSupply',
    '9' => 'kvm',
    '10' => 'switch',
    '11' => 'monitorKeyboardDrawer',
    '12' => 'monitor',
    '13' => 'server',
    '14' => 'storage',
  },
  'cmcIIISmsStatus' => {
    '1' => 'disabled',
    '2' => 'enabled',
  },
  'cmcIIIDrcStatus' => {
    '1' => 'notAvail',
    '2' => 'configChanged',
    '3' => 'error',
    '4' => 'ok',
    '5' => 'alarm',
    '6' => 'highWarn',
    '7' => 'lowAlarm',
    '8' => 'highAlarm',
    '9' => 'lowWarn',
    '10' => 'setOff',
    '11' => 'setOn',
    '12' => 'open',
    '13' => 'closed',
    '14' => 'locked',
    '15' => 'unlRemote',
    '16' => 'doorOpen',
    '17' => 'service',
    '18' => 'standby',
    '19' => 'busy',
    '20' => 'noAccess',
    '21' => 'lost',
    '22' => 'detected',
    '23' => 'lowVoltage',
    '24' => 'probeopen',
    '25' => 'probeshort',
    '26' => 'calibration',
    '27' => 'inactive',
    '28' => 'active',
    '29' => 'noPower',
  },
  'cmcIIIShutdownEnabled' => {
    '1' => 'disabled',
    '2' => 'enabled',
  },
  'cmcIIISysLogFacility' => {
    '1' => 'local0',
    '2' => 'local1',
    '3' => 'local2',
    '4' => 'local3',
    '5' => 'local4',
    '6' => 'local5',
    '7' => 'local6',
    '8' => 'local7',
  },
  'cmcIIIMsgRelay' => {
    '1' => 'disable',
    '2' => 'enable',
  },
  'cmcIIIMsgQuality' => {
    '1' => 'undefined',
    '2' => 'ok',
    '3' => 'warning',
    '4' => 'alarm',
    '5' => 'info',
    '21' => 'undefinedNoValue',
    '22' => 'okNoValue',
    '23' => 'warningNoValue',
    '24' => 'alarmNoValue',
    '25' => 'infoNoValue',
  },
  'cmcIIISshAccess' => {
    '1' => 'disabled',
    '2' => 'enabled',
  },
  'cmcIIITimerStatus' => {
    '1' => 'switchedOff',
    '2' => 'switchedOn',
    '3' => 'noTime',
  },
  'cmcIIIOverallDevStatus' => {
    '1' => 'ok',
    '2' => 'warning',
    '3' => 'alarm',
    '4' => 'detected',
    '5' => 'lost',
    '6' => 'changed',
    '7' => 'update',
  },
  'cmcIIILdapStatus' => {
    '1' => 'disabled',
    '2' => 'enabled',
  },
  'cmcIIISmsReceiverStatus' => {
    '1' => 'disabled',
    '2' => 'enabled',
  },
  'cmcIIIVarDatatype' => {
    '1' => 'notAvail',
    '2' => 'int',
    '3' => 'string',
    '4' => 'enum',
  },
};



package Monitoring::GLPlugin::SNMP::MibsAndOids::RITTALCMCIIIPRODUCTSMIB;

$Monitoring::GLPlugin::SNMP::MibsAndOids::origin->{'RITTAL-CMC-III-PRODUCTS-MIB'} = {
  url => '',
  name => 'RITTAL-CMC-III-PRODUCTS-MIB',
};

$Monitoring::GLPlugin::SNMP::MibsAndOids::mibs_and_oids->{'RITTAL-CMC-III-PRODUCTS-MIB'} = {
  'rittal' => '1.3.6.1.4.1.2606',
  'cmcIII' => '1.3.6.1.4.1.2606.7',
  'cmcIIIProducts' => '1.3.6.1.4.1.2606.7.7',
  'cmcIIIProductUnit' => '1.3.6.1.4.1.2606.7.7.1',
  'cmcIIIProductUnitUnknown' => '1.3.6.1.4.1.2606.7.7.1.1',
  'cmcIIIProductUnitPUIII' => '1.3.6.1.4.1.2606.7.7.1.2',
  'cmcIIIProductUnitCompact' => '1.3.6.1.4.1.2606.7.7.1.3',
  'cmcIIIProductUnitLCP' => '1.3.6.1.4.1.2606.7.7.1.4',
  'cmcIIIProductUnitPDU' => '1.3.6.1.4.1.2606.7.7.1.5',
  'cmcIIIProductUnitRMS' => '1.3.6.1.4.1.2606.7.7.1.6',
  'cmcIIIProductPort' => '1.3.6.1.4.1.2606.7.7.2',
  'cmcIIIProductPortUnknown' => '1.3.6.1.4.1.2606.7.7.2.1',
  'cmcIIIProductPortLoopback' => '1.3.6.1.4.1.2606.7.7.2.2',
  'cmcIIIProductPortCanBus' => '1.3.6.1.4.1.2606.7.7.2.3',
  'cmcIIIProductPortEthernet' => '1.3.6.1.4.1.2606.7.7.2.4',
  'cmcIIIProductPortVirtualCanBus' => '1.3.6.1.4.1.2606.7.7.2.5',
  'cmcIIIProductPowerSupply' => '1.3.6.1.4.1.2606.7.7.3',
  'cmcIIIProductPowerSupplyUnknown' => '1.3.6.1.4.1.2606.7.7.3.1',
  'cmcIIIProductPowerSupplyAcAdapter' => '1.3.6.1.4.1.2606.7.7.3.2',
  'cmcIIIProductPowerSupplyTerminalStrip' => '1.3.6.1.4.1.2606.7.7.3.3',
  'cmcIIIProductPowerSupplyPOE' => '1.3.6.1.4.1.2606.7.7.3.4',
  'cmcIIIProductPowerSupplyUSB' => '1.3.6.1.4.1.2606.7.7.3.5',
  'cmcIIIProductChassis' => '1.3.6.1.4.1.2606.7.7.4',
  'cmcIIIProductChassisGateSensorUnknown' => '1.3.6.1.4.1.2606.7.7.4.256',
  'cmcIIIProductChassisGateSensorAccess' => '1.3.6.1.4.1.2606.7.7.4.273',
  'cmcIIIProductChassisGateSensorMotion' => '1.3.6.1.4.1.2606.7.7.4.277',
  'cmcIIIProductChassisGateSensorSmoke' => '1.3.6.1.4.1.2606.7.7.4.279',
  'cmcIIIProductChassisGateSensorAirflow' => '1.3.6.1.4.1.2606.7.7.4.281',
  'cmcIIIProductChassisGateSensorInputNO' => '1.3.6.1.4.1.2606.7.7.4.289',
  'cmcIIIProductChassisGateSensorInputNC' => '1.3.6.1.4.1.2606.7.7.4.290',
  'cmcIIIProductChassisGateSensorVoltage' => '1.3.6.1.4.1.2606.7.7.4.297',
  'cmcIIIProductChassisGateSensorTemp' => '1.3.6.1.4.1.2606.7.7.4.305',
  'cmcIIIProductChassisGateSensor4to20mA' => '1.3.6.1.4.1.2606.7.7.4.307',
  'cmcIIIProductChassisGateSensorFireError' => '1.3.6.1.4.1.2606.7.7.4.337',
  'cmcIIIProductChassisGateSensorFirePre' => '1.3.6.1.4.1.2606.7.7.4.338',
  'cmcIIIProductChassisGateSensorFireMain' => '1.3.6.1.4.1.2606.7.7.4.339',
  'cmcIIIProductChassisGateSensorLeakage' => '1.3.6.1.4.1.2606.7.7.4.345',
  'cmcIIIProductChassisGateSensorOutput' => '1.3.6.1.4.1.2606.7.7.4.401',
  'cmcIIIProductChassisGateSensorDoorMag' => '1.3.6.1.4.1.2606.7.7.4.402',
  'cmcIIIProductChassisGateLock' => '1.3.6.1.4.1.2606.7.7.4.512',
  'cmcIIIProductChassisTemperature' => '1.3.6.1.4.1.2606.7.7.4.768',
  'cmcIIIProductChassisTempHumi' => '1.3.6.1.4.1.2606.7.7.4.1024',
  'cmcIIIProductChassisVandalism' => '1.3.6.1.4.1.2606.7.7.4.1280',
  'cmcIIIProductChassisPressure' => '1.3.6.1.4.1.2606.7.7.4.1536',
  'cmcIIIProductChassisAccess' => '1.3.6.1.4.1.2606.7.7.4.1792',
  'cmcIIIProductChassisIOInput' => '1.3.6.1.4.1.2606.7.7.4.2048',
  'cmcIIIProductChassisGateUnit-Cfg1' => '1.3.6.1.4.1.2606.7.7.4.2304',
  'cmcIIIProductChassisGateUnit-Cfg2' => '1.3.6.1.4.1.2606.7.7.4.2305',
  'cmcIIIProductChassisGateUnit-Cfg3' => '1.3.6.1.4.1.2606.7.7.4.2306',
  'cmcIIIProductChassisGateUnit-Cfg4' => '1.3.6.1.4.1.2606.7.7.4.2307',
  'cmcIIIProductChassisPowerOld' => '1.3.6.1.4.1.2606.7.7.4.2560',
  'cmcIIIProductChassisDRC' => '1.3.6.1.4.1.2606.7.7.4.2816',
  'cmcIIIProductChassisUniInput' => '1.3.6.1.4.1.2606.7.7.4.3328',
  'cmcIIIProductChassisPower' => '1.3.6.1.4.1.2606.7.7.4.3584',
  'cmcIIIProductChassisSmoke' => '1.3.6.1.4.1.2606.7.7.4.4096',
  'cmcIIIProductChassisDCM' => '1.3.6.1.4.1.2606.7.7.4.4352',
  'cmcIIIProductChassisLeakage' => '1.3.6.1.4.1.2606.7.7.4.4608',
  'cmcIIIProductChassisLCPFan' => '1.3.6.1.4.1.2606.7.7.4.8192',
  'cmcIIIProductChassisLCPWtr' => '1.3.6.1.4.1.2606.7.7.4.8448',
  'cmcIIIProductChassisLCPFcs' => '1.3.6.1.4.1.2606.7.7.4.8704',
  'cmcIIIProductChassisLCPTsw' => '1.3.6.1.4.1.2606.7.7.4.8960',
  'cmcIIIProductChassisLCPUdx' => '1.3.6.1.4.1.2606.7.7.4.9216',
  'cmcIIIProductChassisLCP5' => '1.3.6.1.4.1.2606.7.7.4.9472',
  'cmcIIIProductChassisLCPMsrz' => '1.3.6.1.4.1.2606.7.7.4.9728',
  'cmcIIIProductChassisLCPT3' => '1.3.6.1.4.1.2606.7.7.4.9984',
  'cmcIIIProductChassisLCPFlush' => '1.3.6.1.4.1.2606.7.7.4.10240',
  'cmcIIIProductChassisLCP' => '1.3.6.1.4.1.2606.7.7.4.10496',
  'cmcIIIProductChassisPSM-M16' => '1.3.6.1.4.1.2606.7.7.4.12288',
  'cmcIIIProductChassisPSM-M32' => '1.3.6.1.4.1.2606.7.7.4.12544',
  'cmcIIIProductChassisPSM-MID-M16' => '1.3.6.1.4.1.2606.7.7.4.13312',
  'cmcIIIProductChassisPSM-MID-M32' => '1.3.6.1.4.1.2606.7.7.4.13568',
  'cmcIIIProductChassisPDU-MET-M-1P-16A-C20-12-00' => '1.3.6.1.4.1.2606.7.7.4.14336',
  'cmcIIIProductChassisPDU-MET-M-1P-16A-CEE-24-04' => '1.3.6.1.4.1.2606.7.7.4.14337',
  'cmcIIIProductChassisPDU-MET-M-1P-32A-CEE-24-04' => '1.3.6.1.4.1.2606.7.7.4.14338',
  'cmcIIIProductChassisPDU-MET-M-3P-16A-CEE-18-03' => '1.3.6.1.4.1.2606.7.7.4.14339',
  'cmcIIIProductChassisPDU-MET-M-3P-16A-CEE-24-06' => '1.3.6.1.4.1.2606.7.7.4.14340',
  'cmcIIIProductChassisPDU-MET-M-3P-32A-CEE-24-06' => '1.3.6.1.4.1.2606.7.7.4.14341',
  'cmcIIIProductChassisPDU-MET-M-3P-32A-CEE-36-06' => '1.3.6.1.4.1.2606.7.7.4.14342',
  'cmcIIIProductChassisPDU-MET-M-3P-16A-CEE-42-00' => '1.3.6.1.4.1.2606.7.7.4.14343',
  'cmcIIIProductChassisPDU-MET-M-3P-32A-CEE-48-00' => '1.3.6.1.4.1.2606.7.7.4.14344',
  'cmcIIIProductChassisPDU-MET-M-3P-63A-CEE-12-12' => '1.3.6.1.4.1.2606.7.7.4.14345',
  'cmcIIIProductChassisPDU-MET-M-UserDefined' => '1.3.6.1.4.1.2606.7.7.4.14591',
  'cmcIIIProductChassisPDU-SWI-M-1P-16A-C20-12-00' => '1.3.6.1.4.1.2606.7.7.4.14592',
  'cmcIIIProductChassisPDU-SWI-M-1P-16A-CEE-24-04' => '1.3.6.1.4.1.2606.7.7.4.14593',
  'cmcIIIProductChassisPDU-SWI-M-1P-32A-CEE-24-04' => '1.3.6.1.4.1.2606.7.7.4.14594',
  'cmcIIIProductChassisPDU-SWI-M-3P-16A-CEE-18-03' => '1.3.6.1.4.1.2606.7.7.4.14595',
  'cmcIIIProductChassisPDU-SWI-M-3P-16A-CEE-24-06' => '1.3.6.1.4.1.2606.7.7.4.14596',
  'cmcIIIProductChassisPDU-SWI-M-3P-32A-CEE-24-06' => '1.3.6.1.4.1.2606.7.7.4.14597',
  'cmcIIIProductChassisPDU-SWI-M-3P-32A-CEE-36-06' => '1.3.6.1.4.1.2606.7.7.4.14598',
  'cmcIIIProductChassisPDU-SWI-M-3P-16A-CEE-42-00' => '1.3.6.1.4.1.2606.7.7.4.14599',
  'cmcIIIProductChassisPDU-SWI-M-3P-32A-CEE-48-00' => '1.3.6.1.4.1.2606.7.7.4.14600',
  'cmcIIIProductChassisPDU-SWI-M-UserDefined' => '1.3.6.1.4.1.2606.7.7.4.14847',
  'cmcIIIProductChassisPDU-MAN-M-1P-16A-C20-12-00' => '1.3.6.1.4.1.2606.7.7.4.14848',
  'cmcIIIProductChassisPDU-MAN-M-1P-16A-CEE-24-04' => '1.3.6.1.4.1.2606.7.7.4.14849',
  'cmcIIIProductChassisPDU-MAN-M-1P-32A-CEE-24-04' => '1.3.6.1.4.1.2606.7.7.4.14850',
  'cmcIIIProductChassisPDU-MAN-M-3P-16A-CEE-18-03' => '1.3.6.1.4.1.2606.7.7.4.14851',
  'cmcIIIProductChassisPDU-MAN-M-3P-16A-CEE-24-06' => '1.3.6.1.4.1.2606.7.7.4.14852',
  'cmcIIIProductChassisPDU-MAN-M-3P-32A-CEE-24-06' => '1.3.6.1.4.1.2606.7.7.4.14853',
  'cmcIIIProductChassisPDU-MAN-M-3P-32A-CEE-36-06' => '1.3.6.1.4.1.2606.7.7.4.14854',
  'cmcIIIProductChassisPDU-MAN-M-3P-16A-CEE-42-00' => '1.3.6.1.4.1.2606.7.7.4.14855',
  'cmcIIIProductChassisPDU-MAN-M-3P-32A-CEE-48-00' => '1.3.6.1.4.1.2606.7.7.4.14856',
  'cmcIIIProductChassisPDU-MAN-M-UserDefined' => '1.3.6.1.4.1.2606.7.7.4.15103',
  'cmcIIIProductChassisPDU-MAN-S-1P-16A-C20-12-00' => '1.3.6.1.4.1.2606.7.7.4.15104',
  'cmcIIIProductChassisPDU-MAN-S-1P-16A-CEE-24-04' => '1.3.6.1.4.1.2606.7.7.4.15105',
  'cmcIIIProductChassisPDU-MAN-S-1P-32A-CEE-24-04' => '1.3.6.1.4.1.2606.7.7.4.15106',
  'cmcIIIProductChassisPDU-MAN-S-3P-16A-CEE-18-03' => '1.3.6.1.4.1.2606.7.7.4.15107',
  'cmcIIIProductChassisPDU-MAN-S-3P-16A-CEE-24-06' => '1.3.6.1.4.1.2606.7.7.4.15108',
  'cmcIIIProductChassisPDU-MAN-S-3P-32A-CEE-24-06' => '1.3.6.1.4.1.2606.7.7.4.15109',
  'cmcIIIProductChassisPDU-MAN-S-UserDefined' => '1.3.6.1.4.1.2606.7.7.4.15119',
  'cmcIIIProductChassisPDUu-MET-M-1P-13A-0UK-16-00' => '1.3.6.1.4.1.2606.7.7.4.15360',
  'cmcIIIProductChassisPDUu-MET-M-1P-16A-CEE-24-04' => '1.3.6.1.4.1.2606.7.7.4.15361',
  'cmcIIIProductChassisPDUu-MET-M-1P-32A-CEE-24-04' => '1.3.6.1.4.1.2606.7.7.4.15362',
  'cmcIIIProductChassisPDUu-MET-M-UserDefined' => '1.3.6.1.4.1.2606.7.7.4.15615',
  'cmcIIIProductChassisPDUu-SWI-M-1P-13A-0UK-16-00' => '1.3.6.1.4.1.2606.7.7.4.15616',
  'cmcIIIProductChassisPDUu-SWI-M-1P-16A-0UK-16-00' => '1.3.6.1.4.1.2606.7.7.4.15617',
  'cmcIIIProductChassisPDUu-SWI-M-1P-32A-CEE-16-04' => '1.3.6.1.4.1.2606.7.7.4.15618',
  'cmcIIIProductChassisPDUu-SWI-M-UserDefined' => '1.3.6.1.4.1.2606.7.7.4.15871',
  'cmcIIIProductChassisPDUu-MAN-M-1P-13A-0UK-16-00' => '1.3.6.1.4.1.2606.7.7.4.15872',
  'cmcIIIProductChassisPDUu-MAN-M-1P-16A-CEE-16-04' => '1.3.6.1.4.1.2606.7.7.4.15873',
  'cmcIIIProductChassisPDUu-MAN-M-1P-32A-CEE-16-04' => '1.3.6.1.4.1.2606.7.7.4.15874',
  'cmcIIIProductChassisPDUu-MAN-M-UserDefined' => '1.3.6.1.4.1.2606.7.7.4.16127',
  'cmcIIIProductChassisPDUu-MAN-S-1P-13A-0UK-16-00' => '1.3.6.1.4.1.2606.7.7.4.16128',
  'cmcIIIProductChassisPDUu-MAN-S-1P-16A-CEE-16-04' => '1.3.6.1.4.1.2606.7.7.4.16129',
  'cmcIIIProductChassisPDUu-MAN-S-1P-32A-CEE-16-04' => '1.3.6.1.4.1.2606.7.7.4.16130',
  'cmcIIIProductChassisPDUu-MAN-S-UserDefined' => '1.3.6.1.4.1.2606.7.7.4.16383',
  'cmcIIIProductChassisRiMatrixS-S6' => '1.3.6.1.4.1.2606.7.7.4.20481',
  'cmcIIIProductChassisRiMatrixS-D6' => '1.3.6.1.4.1.2606.7.7.4.20482',
  'cmcIIIProductChassisRiMatrixS-S9' => '1.3.6.1.4.1.2606.7.7.4.20483',
  'cmcIIIProductChassisRiMatrixS-D9' => '1.3.6.1.4.1.2606.7.7.4.20484',
  'cmcIIIProductChassisRiMatrixS-S6W' => '1.3.6.1.4.1.2606.7.7.4.20485',
  'cmcIIIProductChassisRiMatrixS-D6W' => '1.3.6.1.4.1.2606.7.7.4.20486',
  'cmcIIIProductChassisRiMatrixS-S9W' => '1.3.6.1.4.1.2606.7.7.4.20487',
  'cmcIIIProductChassisRiMatrixS-D9W' => '1.3.6.1.4.1.2606.7.7.4.20488',
  'cmcIIIProductChassisFireDetect' => '1.3.6.1.4.1.2606.7.7.4.28672',
  'cmcIIIProductChassisFireExt' => '1.3.6.1.4.1.2606.7.7.4.28928',
  'cmcIIIProductChassisFireExtSlave' => '1.3.6.1.4.1.2606.7.7.4.29184',
  'cmcIIIProductChassisFireExtOneU-MX' => '1.3.6.1.4.1.2606.7.7.4.29440',
  'cmcIIIProductChassisFireExtOneU-MX-ED' => '1.3.6.1.4.1.2606.7.7.4.29696',
  'cmcIIIProductChassisFireExtOneU-MX-DD' => '1.3.6.1.4.1.2606.7.7.4.29952',
  'cmcIIIProductChassisFireExtOneU-VSN' => '1.3.6.1.4.1.2606.7.7.4.30208',
  'cmcIIIProductChassisFireExtOneU-VSN-ED' => '1.3.6.1.4.1.2606.7.7.4.30464',
  'cmcIIIProductChassisFireExtOneU-VSN-DD' => '1.3.6.1.4.1.2606.7.7.4.30720',
  'cmcIIIProductChassisInternal' => '1.3.6.1.4.1.2606.7.7.4.32768',
  'cmcIIIProductChassisVirtualTwoLevel' => '1.3.6.1.4.1.2606.7.7.4.61440',
  'cmcIIIProductChassisVirtualAccess' => '1.3.6.1.4.1.2606.7.7.4.61696',
  'cmcIIIProductSensor' => '1.3.6.1.4.1.2606.7.7.5',
};




package Monitoring::GLPlugin::SNMP::MibsAndOids::RITTALCMCTCMIB;

$Monitoring::GLPlugin::SNMP::MibsAndOids::origin->{'RITTAL-CMC-TC-MIB'} = {
  url => '',
  name => 'RITTAL-CMC-TC-MIB',
};

$Monitoring::GLPlugin::SNMP::MibsAndOids::mib_ids->{'RITTAL-CMC-TC-MIB'} = 
  '1.3.6.1.4.1.2606.4';

$Monitoring::GLPlugin::SNMP::MibsAndOids::mibs_and_oids->{'RITTAL-CMC-TC-MIB'} = {
  'cmcTcMibRev' => '1.3.6.1.4.1.2606.4.1',
  'cmcTcMibMajRev' => '1.3.6.1.4.1.2606.4.1.1.0',
  'cmcTcMibMinRev' => '1.3.6.1.4.1.2606.4.1.2.0',
  'cmcTcMibCondition' => '1.3.6.1.4.1.2606.4.1.3.0',
  'cmcTcMibConditionDefinition' => {
    '2' => 'ok',
    '3' => 'minor',
    '4' => 'major',
    '5' => 'changed',
  },
  'cmcTcStatus' => '1.3.6.1.4.1.2606.4.2',
  'cmcTcStatusDeviceCMC' => '1.3.6.1.4.1.2606.4.2.1.0',
  'cmcTcStatusDeviceCMCDefinition' => 'RITTAL-CMC-TC-MIB::cmcTcStatusDeviceCMC',
  'cmcTcUnitsConnected' => '1.3.6.1.4.1.2606.4.2.2.0',
  'cmcTcStatusSensorUnit1' => '1.3.6.1.4.1.2606.4.2.3',
  'cmcTcUnit1TypeOfDevice' => '1.3.6.1.4.1.2606.4.2.3.1.0',
  'cmcTcUnit1TypeOfDeviceDefinition' => 'RITTAL-CMC-TC-MIB::TypeOfDevice',
  'cmcTcUnit1Text' => '1.3.6.1.4.1.2606.4.2.3.2.0',
  'cmcTcUnit1Serial' => '1.3.6.1.4.1.2606.4.2.3.3.0',
  'cmcTcUnit1Status' => '1.3.6.1.4.1.2606.4.2.3.4.0',
  'cmcTcUnit1StatusDefinition' => 'RITTAL-CMC-TC-MIB::cmcTcUnitxStatus',
  'cmcTcStatusUnit1Sensors' => '1.3.6.1.4.1.2606.4.2.3.5',
  'cmcTcUnit1NumberOfSensors' => '1.3.6.1.4.1.2606.4.2.3.5.1.0',
  'cmcTcUnit1SensorTable' => '1.3.6.1.4.1.2606.4.2.3.5.2',
  'cmcTcUnit1SensorEntry' => '1.3.6.1.4.1.2606.4.2.3.5.2.1',
  'unit1SensorIndex' => '1.3.6.1.4.1.2606.4.2.3.5.2.1.1',
  'unit1SensorType' => '1.3.6.1.4.1.2606.4.2.3.5.2.1.2',
  'unit1SensorTypeDefinition' => 'RITTAL-CMC-TC-MIB::unitxSensorType',
  'unit1SensorText' => '1.3.6.1.4.1.2606.4.2.3.5.2.1.3',
  'unit1SensorStatus' => '1.3.6.1.4.1.2606.4.2.3.5.2.1.4',
  'unit1SensorStatusDefinition' => 'RITTAL-CMC-TC-MIB::unitxSensorStatus',
  'unit1SensorValue' => '1.3.6.1.4.1.2606.4.2.3.5.2.1.5',
  'unit1SensorSetHigh' => '1.3.6.1.4.1.2606.4.2.3.5.2.1.6',
  'unit1SensorSetLow' => '1.3.6.1.4.1.2606.4.2.3.5.2.1.7',
  'unit1SensorSetWarn' => '1.3.6.1.4.1.2606.4.2.3.5.2.1.8',
  'cmcTcStatusUnit1Outputs' => '1.3.6.1.4.1.2606.4.2.3.6',
  'cmcTcUnit1NumberOfOutputs' => '1.3.6.1.4.1.2606.4.2.3.6.1.0',
  'cmcTcUnit1OutputTable' => '1.3.6.1.4.1.2606.4.2.3.6.2',
  'cmcTcUnit1OutputEntry' => '1.3.6.1.4.1.2606.4.2.3.6.2.1',
  'unit1OutputIndex' => '1.3.6.1.4.1.2606.4.2.3.6.2.1.1',
  'unit1OutputType' => '1.3.6.1.4.1.2606.4.2.3.6.2.1.2',
  'unit1OutputText' => '1.3.6.1.4.1.2606.4.2.3.6.2.1.3',
  'unit1OutputStatus' => '1.3.6.1.4.1.2606.4.2.3.6.2.1.4',
  'unit1OutputValue' => '1.3.6.1.4.1.2606.4.2.3.6.2.1.5',
  'unit1OutputSet' => '1.3.6.1.4.1.2606.4.2.3.6.2.1.6',
  'unit1OutputConfig' => '1.3.6.1.4.1.2606.4.2.3.6.2.1.7',
  'unit1OutputDelay' => '1.3.6.1.4.1.2606.4.2.3.6.2.1.8',
  'unit1OutputTimeoutAction' => '1.3.6.1.4.1.2606.4.2.3.6.2.1.9',
  'cmcTcStatusUnit1Msg' => '1.3.6.1.4.1.2606.4.2.3.7',
  'cmcTcUnit1NumberOfMsgs' => '1.3.6.1.4.1.2606.4.2.3.7.1.0',
  'cmcTcUnit1MsgTable' => '1.3.6.1.4.1.2606.4.2.3.7.2',
  'cmcTcUnit1MsgEntry' => '1.3.6.1.4.1.2606.4.2.3.7.2.1',
  'unit1MsgIndex' => '1.3.6.1.4.1.2606.4.2.3.7.2.1.1',
  'unit1MsgText' => '1.3.6.1.4.1.2606.4.2.3.7.2.1.2',
  'unit1MsgStatus' => '1.3.6.1.4.1.2606.4.2.3.7.2.1.3',
  'unit1MsgRelay' => '1.3.6.1.4.1.2606.4.2.3.7.2.1.4',
  'unit1MsgBeeper' => '1.3.6.1.4.1.2606.4.2.3.7.2.1.5',
  'unit1MsgTrap1' => '1.3.6.1.4.1.2606.4.2.3.7.2.1.6',
  'unit1MsgTrap2' => '1.3.6.1.4.1.2606.4.2.3.7.2.1.7',
  'unit1MsgTrap3' => '1.3.6.1.4.1.2606.4.2.3.7.2.1.8',
  'unit1MsgTrap4' => '1.3.6.1.4.1.2606.4.2.3.7.2.1.9',
  'unit1MsgQuit' => '1.3.6.1.4.1.2606.4.2.3.7.2.1.10',
  'cmcTcStatusSensorUnit2' => '1.3.6.1.4.1.2606.4.2.4',
  'cmcTcUnit2TypeOfDevice' => '1.3.6.1.4.1.2606.4.2.4.1.0',
  'cmcTcUnit2TypeOfDeviceDefinition' => 'RITTAL-CMC-TC-MIB::TypeOfDevice',
  'cmcTcUnit2Text' => '1.3.6.1.4.1.2606.4.2.4.2.0',
  'cmcTcUnit2Serial' => '1.3.6.1.4.1.2606.4.2.4.3.0',
  'cmcTcUnit2Status' => '1.3.6.1.4.1.2606.4.2.4.4.0',
  'cmcTcUnit2StatusDefinition' => 'RITTAL-CMC-TC-MIB::cmcTcUnitxStatus',
  'cmcTcStatusUnit2Sensors' => '1.3.6.1.4.1.2606.4.2.4.5',
  'cmcTcUnit2NumberOfSensors' => '1.3.6.1.4.1.2606.4.2.4.5.1.0',
  'cmcTcUnit2SensorTable' => '1.3.6.1.4.1.2606.4.2.4.5.2',
  'cmcTcUnit2SensorEntry' => '1.3.6.1.4.1.2606.4.2.4.5.2.1',
  'unit2SensorIndex' => '1.3.6.1.4.1.2606.4.2.4.5.2.1.1',
  'unit2SensorType' => '1.3.6.1.4.1.2606.4.2.4.5.2.1.2',
  'unit2SensorTypeDefinition' => 'RITTAL-CMC-TC-MIB::unitxSensorType',
  'unit2SensorText' => '1.3.6.1.4.1.2606.4.2.4.5.2.1.3',
  'unit2SensorStatus' => '1.3.6.1.4.1.2606.4.2.4.5.2.1.4',
  'unit2SensorStatusDefinition' => 'RITTAL-CMC-TC-MIB::unitxSensorStatus',
  'unit2SensorValue' => '1.3.6.1.4.1.2606.4.2.4.5.2.1.5',
  'unit2SensorSetHigh' => '1.3.6.1.4.1.2606.4.2.4.5.2.1.6',
  'unit2SensorSetLow' => '1.3.6.1.4.1.2606.4.2.4.5.2.1.7',
  'unit2SensorSetWarn' => '1.3.6.1.4.1.2606.4.2.4.5.2.1.8',
  'cmcTcStatusUnit2Outputs' => '1.3.6.1.4.1.2606.4.2.4.6',
  'cmcTcUnit2NumberOfOutputs' => '1.3.6.1.4.1.2606.4.2.4.6.1.0',
  'cmcTcUnit2OutputTable' => '1.3.6.1.4.1.2606.4.2.4.6.2',
  'cmcTcUnit2OutputEntry' => '1.3.6.1.4.1.2606.4.2.4.6.2.1',
  'unit2OutputIndex' => '1.3.6.1.4.1.2606.4.2.4.6.2.1.1',
  'unit2OutputType' => '1.3.6.1.4.1.2606.4.2.4.6.2.1.2',
  'unit2OutputText' => '1.3.6.1.4.1.2606.4.2.4.6.2.1.3',
  'unit2OutputStatus' => '1.3.6.1.4.1.2606.4.2.4.6.2.1.4',
  'unit2OutputValue' => '1.3.6.1.4.1.2606.4.2.4.6.2.1.5',
  'unit2OutputSet' => '1.3.6.1.4.1.2606.4.2.4.6.2.1.6',
  'unit2OutputConfig' => '1.3.6.1.4.1.2606.4.2.4.6.2.1.7',
  'unit2OutputDelay' => '1.3.6.1.4.1.2606.4.2.4.6.2.1.8',
  'unit2OutputTimeoutAction' => '1.3.6.1.4.1.2606.4.2.4.6.2.1.9',
  'cmcTcStatusUnit2Msg' => '1.3.6.1.4.1.2606.4.2.4.7',
  'cmcTcUnit2NumberOfMsgs' => '1.3.6.1.4.1.2606.4.2.4.7.1.0',
  'cmcTcUnit2MsgTable' => '1.3.6.1.4.1.2606.4.2.4.7.2',
  'cmcTcUnit2MsgEntry' => '1.3.6.1.4.1.2606.4.2.4.7.2.1',
  'unit2MsgIndex' => '1.3.6.1.4.1.2606.4.2.4.7.2.1.1',
  'unit2MsgText' => '1.3.6.1.4.1.2606.4.2.4.7.2.1.2',
  'unit2MsgStatus' => '1.3.6.1.4.1.2606.4.2.4.7.2.1.3',
  'unit2MsgRelay' => '1.3.6.1.4.1.2606.4.2.4.7.2.1.4',
  'unit2MsgBeeper' => '1.3.6.1.4.1.2606.4.2.4.7.2.1.5',
  'unit2MsgTrap1' => '1.3.6.1.4.1.2606.4.2.4.7.2.1.6',
  'unit2MsgTrap2' => '1.3.6.1.4.1.2606.4.2.4.7.2.1.7',
  'unit2MsgTrap3' => '1.3.6.1.4.1.2606.4.2.4.7.2.1.8',
  'unit2MsgTrap4' => '1.3.6.1.4.1.2606.4.2.4.7.2.1.9',
  'unit2MsgQuit' => '1.3.6.1.4.1.2606.4.2.4.7.2.1.10',
  'cmcTcStatusSensorUnit3' => '1.3.6.1.4.1.2606.4.2.5',
  'cmcTcUnit3TypeOfDevice' => '1.3.6.1.4.1.2606.4.2.5.1.0',
  'cmcTcUnit3TypeOfDeviceDefinition' => 'RITTAL-CMC-TC-MIB::TypeOfDevice',
  'cmcTcUnit3Text' => '1.3.6.1.4.1.2606.4.2.5.2.0',
  'cmcTcUnit3Serial' => '1.3.6.1.4.1.2606.4.2.5.3.0',
  'cmcTcUnit3Status' => '1.3.6.1.4.1.2606.4.2.5.4.0',
  'cmcTcUnit3StatusDefinition' => 'RITTAL-CMC-TC-MIB::cmcTcUnitxStatus',
  'cmcTcStatusUnit3Sensors' => '1.3.6.1.4.1.2606.4.2.5.5',
  'cmcTcUnit3NumberOfSensors' => '1.3.6.1.4.1.2606.4.2.5.5.1.0',
  'cmcTcUnit3SensorTable' => '1.3.6.1.4.1.2606.4.2.5.5.2',
  'cmcTcUnit3SensorEntry' => '1.3.6.1.4.1.2606.4.2.5.5.2.1',
  'unit3SensorIndex' => '1.3.6.1.4.1.2606.4.2.5.5.2.1.1',
  'unit3SensorType' => '1.3.6.1.4.1.2606.4.2.5.5.2.1.2',
  'unit3SensorTypeDefinition' => 'RITTAL-CMC-TC-MIB::unitxSensorType',
  'unit3SensorText' => '1.3.6.1.4.1.2606.4.2.5.5.2.1.3',
  'unit3SensorStatus' => '1.3.6.1.4.1.2606.4.2.5.5.2.1.4',
  'unit3SensorStatusDefinition' => 'RITTAL-CMC-TC-MIB::unitxSensorStatus',
  'unit3SensorValue' => '1.3.6.1.4.1.2606.4.2.5.5.2.1.5',
  'unit3SensorSetHigh' => '1.3.6.1.4.1.2606.4.2.5.5.2.1.6',
  'unit3SensorSetLow' => '1.3.6.1.4.1.2606.4.2.5.5.2.1.7',
  'unit3SensorSetWarn' => '1.3.6.1.4.1.2606.4.2.5.5.2.1.8',
  'cmcTcStatusUnit3Outputs' => '1.3.6.1.4.1.2606.4.2.5.6',
  'cmcTcUnit3NumberOfOutputs' => '1.3.6.1.4.1.2606.4.2.5.6.1.0',
  'cmcTcUnit3OutputTable' => '1.3.6.1.4.1.2606.4.2.5.6.2',
  'cmcTcUnit3OutputEntry' => '1.3.6.1.4.1.2606.4.2.5.6.2.1',
  'unit3OutputIndex' => '1.3.6.1.4.1.2606.4.2.5.6.2.1.1',
  'unit3OutputType' => '1.3.6.1.4.1.2606.4.2.5.6.2.1.2',
  'unit3OutputText' => '1.3.6.1.4.1.2606.4.2.5.6.2.1.3',
  'unit3OutputStatus' => '1.3.6.1.4.1.2606.4.2.5.6.2.1.4',
  'unit3OutputValue' => '1.3.6.1.4.1.2606.4.2.5.6.2.1.5',
  'unit3OutputSet' => '1.3.6.1.4.1.2606.4.2.5.6.2.1.6',
  'unit3OutputConfig' => '1.3.6.1.4.1.2606.4.2.5.6.2.1.7',
  'unit3OutputDelay' => '1.3.6.1.4.1.2606.4.2.5.6.2.1.8',
  'unit3OutputTimeoutAction' => '1.3.6.1.4.1.2606.4.2.5.6.2.1.9',
  'cmcTcStatusUnit3Msg' => '1.3.6.1.4.1.2606.4.2.5.7',
  'cmcTcUnit3NumberOfMsgs' => '1.3.6.1.4.1.2606.4.2.5.7.1.0',
  'cmcTcUnit3MsgTable' => '1.3.6.1.4.1.2606.4.2.5.7.2',
  'cmcTcUnit3MsgEntry' => '1.3.6.1.4.1.2606.4.2.5.7.2.1',
  'unit3MsgIndex' => '1.3.6.1.4.1.2606.4.2.5.7.2.1.1',
  'unit3MsgText' => '1.3.6.1.4.1.2606.4.2.5.7.2.1.2',
  'unit3MsgStatus' => '1.3.6.1.4.1.2606.4.2.5.7.2.1.3',
  'unit3MsgRelay' => '1.3.6.1.4.1.2606.4.2.5.7.2.1.4',
  'unit3MsgBeeper' => '1.3.6.1.4.1.2606.4.2.5.7.2.1.5',
  'unit3MsgTrap1' => '1.3.6.1.4.1.2606.4.2.5.7.2.1.6',
  'unit3MsgTrap2' => '1.3.6.1.4.1.2606.4.2.5.7.2.1.7',
  'unit3MsgTrap3' => '1.3.6.1.4.1.2606.4.2.5.7.2.1.8',
  'unit3MsgTrap4' => '1.3.6.1.4.1.2606.4.2.5.7.2.1.9',
  'unit3MsgQuit' => '1.3.6.1.4.1.2606.4.2.5.7.2.1.10',
  'cmcTcStatusSensorUnit4' => '1.3.6.1.4.1.2606.4.2.6',
  'cmcTcUnit4TypeOfDevice' => '1.3.6.1.4.1.2606.4.2.6.1.0',
  'cmcTcUnit4TypeOfDeviceDefinition' => 'RITTAL-CMC-TC-MIB::TypeOfDevice',
  'cmcTcUnit4Text' => '1.3.6.1.4.1.2606.4.2.6.2.0',
  'cmcTcUnit4Serial' => '1.3.6.1.4.1.2606.4.2.6.3.0',
  'cmcTcUnit4Status' => '1.3.6.1.4.1.2606.4.2.6.4.0',
  'cmcTcUnit4StatusDefinition' => 'RITTAL-CMC-TC-MIB::cmcTcUnitxStatus',
  'cmcTcStatusUnit4Sensors' => '1.3.6.1.4.1.2606.4.2.6.5',
  'cmcTcUnit4NumberOfSensors' => '1.3.6.1.4.1.2606.4.2.6.5.1.0',
  'cmcTcUnit4SensorTable' => '1.3.6.1.4.1.2606.4.2.6.5.2',
  'cmcTcUnit4SensorEntry' => '1.3.6.1.4.1.2606.4.2.6.5.2.1',
  'unit4SensorIndex' => '1.3.6.1.4.1.2606.4.2.6.5.2.1.1',
  'unit4SensorType' => '1.3.6.1.4.1.2606.4.2.6.5.2.1.2',
  'unit4SensorTypeDefinition' => 'RITTAL-CMC-TC-MIB::unitxSensorType',
  'unit4SensorText' => '1.3.6.1.4.1.2606.4.2.6.5.2.1.3',
  'unit4SensorStatus' => '1.3.6.1.4.1.2606.4.2.6.5.2.1.4',
  'unit4SensorStatusDefinition' => 'RITTAL-CMC-TC-MIB::unitxSensorStatus',
  'unit4SensorValue' => '1.3.6.1.4.1.2606.4.2.6.5.2.1.5',
  'unit4SensorSetHigh' => '1.3.6.1.4.1.2606.4.2.6.5.2.1.6',
  'unit4SensorSetLow' => '1.3.6.1.4.1.2606.4.2.6.5.2.1.7',
  'unit4SensorSetWarn' => '1.3.6.1.4.1.2606.4.2.6.5.2.1.8',
  'cmcTcStatusUnit4Outputs' => '1.3.6.1.4.1.2606.4.2.6.6',
  'cmcTcUnit4NumberOfOutputs' => '1.3.6.1.4.1.2606.4.2.6.6.1.0',
  'cmcTcUnit4OutputTable' => '1.3.6.1.4.1.2606.4.2.6.6.2',
  'cmcTcUnit4OutputEntry' => '1.3.6.1.4.1.2606.4.2.6.6.2.1',
  'unit4OutputIndex' => '1.3.6.1.4.1.2606.4.2.6.6.2.1.1',
  'unit4OutputType' => '1.3.6.1.4.1.2606.4.2.6.6.2.1.2',
  'unit4OutputText' => '1.3.6.1.4.1.2606.4.2.6.6.2.1.3',
  'unit4OutputStatus' => '1.3.6.1.4.1.2606.4.2.6.6.2.1.4',
  'unit4OutputValue' => '1.3.6.1.4.1.2606.4.2.6.6.2.1.5',
  'unit4OutputSet' => '1.3.6.1.4.1.2606.4.2.6.6.2.1.6',
  'unit4OutputConfig' => '1.3.6.1.4.1.2606.4.2.6.6.2.1.7',
  'unit4OutputDelay' => '1.3.6.1.4.1.2606.4.2.6.6.2.1.8',
  'unit4OutputTimeoutAction' => '1.3.6.1.4.1.2606.4.2.6.6.2.1.9',
  'cmcTcStatusUnit4Msg' => '1.3.6.1.4.1.2606.4.2.6.7',
  'cmcTcUnit4NumberOfMsgs' => '1.3.6.1.4.1.2606.4.2.6.7.1.0',
  'cmcTcUnit4MsgTable' => '1.3.6.1.4.1.2606.4.2.6.7.2',
  'cmcTcUnit4MsgEntry' => '1.3.6.1.4.1.2606.4.2.6.7.2.1',
  'unit4MsgIndex' => '1.3.6.1.4.1.2606.4.2.6.7.2.1.1',
  'unit4MsgText' => '1.3.6.1.4.1.2606.4.2.6.7.2.1.2',
  'unit4MsgStatus' => '1.3.6.1.4.1.2606.4.2.6.7.2.1.3',
  'unit4MsgRelay' => '1.3.6.1.4.1.2606.4.2.6.7.2.1.4',
  'unit4MsgBeeper' => '1.3.6.1.4.1.2606.4.2.6.7.2.1.5',
  'unit4MsgTrap1' => '1.3.6.1.4.1.2606.4.2.6.7.2.1.6',
  'unit4MsgTrap2' => '1.3.6.1.4.1.2606.4.2.6.7.2.1.7',
  'unit4MsgTrap3' => '1.3.6.1.4.1.2606.4.2.6.7.2.1.8',
  'unit4MsgTrap4' => '1.3.6.1.4.1.2606.4.2.6.7.2.1.9',
  'unit4MsgQuit' => '1.3.6.1.4.1.2606.4.2.6.7.2.1.10',
  'cmcTcStatusExtUnit' => '1.3.6.1.4.1.2606.4.2.7',
  'cmcTcValuesRelay' => '1.3.6.1.4.1.2606.4.2.7.1.0',
  'cmcTcValuesBeeper' => '1.3.6.1.4.1.2606.4.2.7.2.0',
  'cmcTcValuesTrap1' => '1.3.6.1.4.1.2606.4.2.7.3.0',
  'cmcTcValuesTrap2' => '1.3.6.1.4.1.2606.4.2.7.4.0',
  'cmcTcValuesTrap3' => '1.3.6.1.4.1.2606.4.2.7.5.0',
  'cmcTcValuesTrap4' => '1.3.6.1.4.1.2606.4.2.7.6.0',
  'cmcTcNumberOfValues' => '1.3.6.1.4.1.2606.4.2.7.7.0',
  'cmcTcValuesTable' => '1.3.6.1.4.1.2606.4.2.7.8',
  'cmcTcValuesEntry' => '1.3.6.1.4.1.2606.4.2.7.8.1',
  'valuesIndex' => '1.3.6.1.4.1.2606.4.2.7.8.1.1',
  'valuesText' => '1.3.6.1.4.1.2606.4.2.7.8.1.2',
  'valuesStatus' => '1.3.6.1.4.1.2606.4.2.7.8.1.3',
  'valuesValue' => '1.3.6.1.4.1.2606.4.2.7.8.1.4',
  'valuesSetHigh' => '1.3.6.1.4.1.2606.4.2.7.8.1.5',
  'valuesSetLow' => '1.3.6.1.4.1.2606.4.2.7.8.1.6',
  'cmcTcSetupGeneral' => '1.3.6.1.4.1.2606.4.3.1',
  'cmcTcSetTempUnit' => '1.3.6.1.4.1.2606.4.3.1.1.0',
  'cmcTcSetBeeper' => '1.3.6.1.4.1.2606.4.3.1.2.0',
  'cmcTcQuitRelay' => '1.3.6.1.4.1.2606.4.3.1.3.0',
  'cmcTcLogicRelay' => '1.3.6.1.4.1.2606.4.3.1.4.0',
  'cmcTcWebAccess' => '1.3.6.1.4.1.2606.4.3.1.5.0',
  'cmcTcSetupDate' => '1.3.6.1.4.1.2606.4.3.1.6.0',
  'cmcTcSetupTime' => '1.3.6.1.4.1.2606.4.3.1.7.0',
  'cmcTcTimerTable1' => '1.3.6.1.4.1.2606.4.3.1.8',
  'cmcTcTimerNumber' => '1.3.6.1.4.1.2606.4.3.1.8.1.0',
  'cmcTcTimerTable' => '1.3.6.1.4.1.2606.4.3.1.8.2',
  'cmcTcTimerEntry' => '1.3.6.1.4.1.2606.4.3.1.8.2.1',
  'cmcTcTimerIndex' => '1.3.6.1.4.1.2606.4.3.1.8.2.1.1',
  'cmcTcTimerStatus' => '1.3.6.1.4.1.2606.4.3.1.8.2.1.2',
  'cmcTcTimerDayOfWeek' => '1.3.6.1.4.1.2606.4.3.1.8.2.1.3',
  'cmcTcTimeOn' => '1.3.6.1.4.1.2606.4.3.1.8.2.1.4',
  'cmcTcTimeOff' => '1.3.6.1.4.1.2606.4.3.1.8.2.1.5',
  'cmcTcTimeControl' => '1.3.6.1.4.1.2606.4.3.1.8.2.1.6',
  'cmcTcTimerFunction' => '1.3.6.1.4.1.2606.4.3.1.8.2.1.7',
  'cmcTcTraps' => '1.3.6.1.4.1.2606.4.4.7',
  'cmcTcTraptableNumber' => '1.3.6.1.4.1.2606.4.4.7.1.0',
  'cmcTcTrapTableTable' => '1.3.6.1.4.1.2606.4.4.7.2',
  'cmcTcTrapTableEntry' => '1.3.6.1.4.1.2606.4.4.7.2.1',
  'trapIndex' => '1.3.6.1.4.1.2606.4.4.7.2.1.1',
  'trapStatus' => '1.3.6.1.4.1.2606.4.4.7.2.1.2',
  'trapIPaddress' => '1.3.6.1.4.1.2606.4.4.7.2.1.3',
  'cmcTcControl' => '1.3.6.1.4.1.2606.4.5',
  'cmcTcQuitUnit' => '1.3.6.1.4.1.2606.4.5.1.0',
};

$Monitoring::GLPlugin::SNMP::MibsAndOids::definitions->{'RITTAL-CMC-TC-MIB'} = {
  'cmcTcUnitxStatus' => {
    '0' => 'unknown_0',
    '1' => 'ok',
    '2' => 'error',
    '3' => 'changed',
    '4' => 'quit',
    '5' => 'timeout',
    '6' => 'detected',
    '7' => 'notAvail',
    '8' => 'lowPower',
  },
  'unitxSensorType' => {
    '1' => 'notAvail',
    '2' => 'failure',
    '3' => 'overflow',
    '4' => 'access',
    '5' => 'vibration',
    '6' => 'motion',
    '7' => 'smoke',
    '8' => 'airFlow',
    '9' => 'type6',
    '10' => 'temperature',
    '11' => 'current4to20',
    '12' => 'humidity',
    '13' => 'userNO',
    '14' => 'userNC',
    '15' => 'lock',
    '16' => 'unlock',
    '17' => 'voltOK',
    '18' => 'voltage',
    '19' => 'fanOK',
    '20' => 'readerKeypad',
    '21' => 'dutyPWM',
    '22' => 'fanStatus',
    '23' => 'leakage',
    '24' => 'warningRTT',
    '25' => 'alarmRTT',
    '26' => 'filterRTT',
    '27' => 'heatflowRCT',
    '28' => 'alarmRCT',
    '29' => 'warningRCT',
    '30' => 'currentPSM',
    '31' => 'statusPSM',
    '32' => 'positionPSM',
    '33' => 'airFlap',
    '34' => 'acoustic',
    '35' => 'detACfault',
    '36' => 'detACfirstAlarm',
    '37' => 'detACmainAlarm',
    '40' => 'rpm11LCP',
    '41' => 'rpm12LCP',
    '42' => 'rpm21LCP',
    '43' => 'rpm22LCP',
    '44' => 'rpm31LCP',
    '45' => 'rpm32LCP',
    '46' => 'rpm41LCP',
    '47' => 'rpm42LCP',
    '48' => 'airTemp11LCP',
    '49' => 'airTemp12LCP',
    '50' => 'airTemp21LCP',
    '51' => 'airTemp22LCP',
    '52' => 'airTemp31LCP',
    '53' => 'airTemp32LCP',
    '54' => 'airTemp41LCP',
    '55' => 'airTemp42LCP',
    '56' => 'temp1LCP',
    '57' => 'temp2LCP',
    '58' => 'waterInTemp',
    '59' => 'waterOutTemp',
    '60' => 'waterFlow',
    '61' => 'fanSpeed',
    '62' => 'valve',
    '63' => 'statusLCP',
    '64' => 'waterDelta',
    '65' => 'valveActual',
    '66' => 'contrTemp2',
    '67' => 'condensateDuration',
    '68' => 'condensateCycles',
    '72' => 'totalKWhPSM',
    '73' => 'totalKWPSM',
    '74' => 'frequencyPSM',
    '75' => 'voltagePSM',
    '76' => 'voltStatusPSM',
    '77' => 'amperePSM',
    '78' => 'ampStatusPSM',
    '79' => 'kWPSM',
    '80' => 'kWhPSM',
    '81' => 'kWhTempPSM',
    '100' => 'temperatureWL',
    '101' => 'temperature1WL',
    '102' => 'humidityWL',
    '103' => 'accessWL',
    '104' => 'userNOWL',
    '105' => 'userNCWL',
    '106' => 'analogWL',
  },
  'unitxSensorStatus' => {
    '1' => 'notAvail',
    '2' => 'lost',
    '3' => 'changed',
    '4' => 'ok',
    '5' => 'off',
    '6' => 'on',
    '7' => 'warning',
    '8' => 'tooLow',
    '9' => 'tooHigh',
  },
  'cmcTcStatusDeviceCMC' => {
    '1' => 'failed',
    '2' => 'ok',
  },
  'TypeOfDevice' => {
    '0' => 'unknown_0',
    '1' => 'notAvail',
    '2' => 'unitIO',
    '3' => 'unitAccess',
    '4' => 'unitClimate',
    '5' => 'unitFCS',
    '6' => 'unitRTT',
    '7' => 'unitRTC',
    '8' => 'unitPSM',
    '9' => 'unitPSM8',
    '10' => 'unitPSMMetered',
    '11' => 'unitIOWireless',
    '12' => 'unitPSM6Schuko',
    '13' => 'unitPSM6C19',
  },
};



package Monitoring::GLPlugin::SNMP::MibsAndOids::KELVINPCOWEBLCPDXMIB;

$Monitoring::GLPlugin::SNMP::MibsAndOids::origin->{'KELVIN-PCOWEB-LCP-DX-MIB'} = {
  url => '',
  name => 'KELVIN-PCOWEB-LCP-DX-MIB',
};

$Monitoring::GLPlugin::SNMP::MibsAndOids::mib_ids->{'KELVIN-PCOWEB-LCP-DX-MIB'} =
    '1.3.6.1.4.1.9839.2.1';

$Monitoring::GLPlugin::SNMP::MibsAndOids::mibs_and_oids->{'KELVIN-PCOWEB-LCP-DX-MIB'} = {
  'carel' => '1.3.6.1.4.1.9839',
  'systm' => '1.3.6.1.4.1.9839.1',
  'agentRelease' => '1.3.6.1.4.1.9839.1.1',
  'agentCode' => '1.3.6.1.4.1.9839.1.2',
  'instruments' => '1.3.6.1.4.1.9839.2',
  'pCOWebInfo' => '1.3.6.1.4.1.9839.2.0',
  'pCOStatusgroup' => '1.3.6.1.4.1.9839.2.0.10',
  'pCOId1-Status' => '1.3.6.1.4.1.9839.2.0.10.1',
  'pCOId1-StatusDefinition' => {
      0 => "offline",
      1 => "init",
      2 => "online",
  },
  'pCOErrorsNumbergroup' => '1.3.6.1.4.1.9839.2.0.11',
  'pCOId1-ErrorsNumber' => '1.3.6.1.4.1.9839.2.0.11.1',
  'kelvin-pCOWebMIB-LCP-DX' => '1.3.6.1.4.1.9839.2.1',
  'digitalObjects' => '1.3.6.1.4.1.9839.2.1.1',
  'din1' => '1.3.6.1.4.1.9839.2.1.1.1',
  'din2' => '1.3.6.1.4.1.9839.2.1.1.2',
  'din3' => '1.3.6.1.4.1.9839.2.1.1.3',
  'din4' => '1.3.6.1.4.1.9839.2.1.1.4',
  'din5' => '1.3.6.1.4.1.9839.2.1.1.5',
  'din6' => '1.3.6.1.4.1.9839.2.1.1.6',
  'din7' => '1.3.6.1.4.1.9839.2.1.1.7',
  'din8' => '1.3.6.1.4.1.9839.2.1.1.8',
  'din9' => '1.3.6.1.4.1.9839.2.1.1.9',
  'din10' => '1.3.6.1.4.1.9839.2.1.1.10',
  'dobj11' => '1.3.6.1.4.1.9839.2.1.1.11',
  'dobj12' => '1.3.6.1.4.1.9839.2.1.1.12',
  'dobj13' => '1.3.6.1.4.1.9839.2.1.1.13',
  'dobj14' => '1.3.6.1.4.1.9839.2.1.1.14',
  'dobj15' => '1.3.6.1.4.1.9839.2.1.1.15',
  'dobj16' => '1.3.6.1.4.1.9839.2.1.1.16',
  'dout1' => '1.3.6.1.4.1.9839.2.1.1.17',
  'dout2' => '1.3.6.1.4.1.9839.2.1.1.18',
  'dout3' => '1.3.6.1.4.1.9839.2.1.1.19',
  'dout4' => '1.3.6.1.4.1.9839.2.1.1.20',
  'dout5' => '1.3.6.1.4.1.9839.2.1.1.21',
  'dout6' => '1.3.6.1.4.1.9839.2.1.1.22',
  'dout7' => '1.3.6.1.4.1.9839.2.1.1.23',
  'dout8' => '1.3.6.1.4.1.9839.2.1.1.24',
  'dout9' => '1.3.6.1.4.1.9839.2.1.1.25',
  'dout10' => '1.3.6.1.4.1.9839.2.1.1.26',
  'dout11' => '1.3.6.1.4.1.9839.2.1.1.27',
  'dout12' => '1.3.6.1.4.1.9839.2.1.1.28',
  'bms-res-alarm' => '1.3.6.1.4.1.9839.2.1.1.29',
  'al-envelope' => '1.3.6.1.4.1.9839.2.1.1.30',
  'al-start-fail-lock' => '1.3.6.1.4.1.9839.2.1.1.31',
  'mal-start-failure-msk' => '1.3.6.1.4.1.9839.2.1.1.32',
  'mal-discharge-ht' => '1.3.6.1.4.1.9839.2.1.1.33',
  'dobj34' => '1.3.6.1.4.1.9839.2.1.1.34',
  'mal-dp-startup' => '1.3.6.1.4.1.9839.2.1.1.35',
  'mal-dp-lubrification-oil' => '1.3.6.1.4.1.9839.2.1.1.36',
  'mal-b1' => '1.3.6.1.4.1.9839.2.1.1.37',
  'mal-b2' => '1.3.6.1.4.1.9839.2.1.1.38',
  'mal-b3' => '1.3.6.1.4.1.9839.2.1.1.39',
  'mal-b4' => '1.3.6.1.4.1.9839.2.1.1.40',
  'mal-b5' => '1.3.6.1.4.1.9839.2.1.1.41',
  'mal-b6' => '1.3.6.1.4.1.9839.2.1.1.42',
  'mal-b7' => '1.3.6.1.4.1.9839.2.1.1.43',
  'mal-b8' => '1.3.6.1.4.1.9839.2.1.1.44',
  'mal-b9' => '1.3.6.1.4.1.9839.2.1.1.45',
  'mal-b10' => '1.3.6.1.4.1.9839.2.1.1.46',
  'mal-b11' => '1.3.6.1.4.1.9839.2.1.1.47',
  'mal-b12' => '1.3.6.1.4.1.9839.2.1.1.48',
  'analogObjects' => '1.3.6.1.4.1.9839.2.1.2',
  'b1-value' => '1.3.6.1.4.1.9839.2.1.2.1',
  'b2-value' => '1.3.6.1.4.1.9839.2.1.2.2',
  'b3-value' => '1.3.6.1.4.1.9839.2.1.2.3',
  'b4-value' => '1.3.6.1.4.1.9839.2.1.2.4',
  'b5-value' => '1.3.6.1.4.1.9839.2.1.2.5',
  'b6-value' => '1.3.6.1.4.1.9839.2.1.2.6',
  'b7-value' => '1.3.6.1.4.1.9839.2.1.2.7',
  'b8-value' => '1.3.6.1.4.1.9839.2.1.2.8',
  'b9-value' => '1.3.6.1.4.1.9839.2.1.2.9',
  'b10-value' => '1.3.6.1.4.1.9839.2.1.2.10',
  'b11-value' => '1.3.6.1.4.1.9839.2.1.2.11',
  'b12-value' => '1.3.6.1.4.1.9839.2.1.2.12',
  'evap-temp' => '1.3.6.1.4.1.9839.2.1.2.13',
  'cond-temp' => '1.3.6.1.4.1.9839.2.1.2.14',
  'aobj15' => '1.3.6.1.4.1.9839.2.1.2.15',
  'aobj16' => '1.3.6.1.4.1.9839.2.1.2.16',
  'aobj17' => '1.3.6.1.4.1.9839.2.1.2.17',
  'aobj18' => '1.3.6.1.4.1.9839.2.1.2.18',
  'aobj19' => '1.3.6.1.4.1.9839.2.1.2.19',
  'aobj20' => '1.3.6.1.4.1.9839.2.1.2.20',
  'medium-temp-out' => '1.3.6.1.4.1.9839.2.1.2.21',
  'medium-temp-in' => '1.3.6.1.4.1.9839.2.1.2.22',
  'rotor-speed-rps' => '1.3.6.1.4.1.9839.2.1.2.45',
  'motor-current' => '1.3.6.1.4.1.9839.2.1.2.46',
  'aobj47' => '1.3.6.1.4.1.9839.2.1.2.47',
  'setpoint-lcp' => '1.3.6.1.4.1.9839.2.1.2.48',
  'integerObjects' => '1.3.6.1.4.1.9839.2.1.3',
  'rotor-speed-hz' => '1.3.6.1.4.1.9839.2.1.3.1',
  'drive-status' => '1.3.6.1.4.1.9839.2.1.3.2',
  'error-code' => '1.3.6.1.4.1.9839.2.1.3.3',
  'drive-temp' => '1.3.6.1.4.1.9839.2.1.3.4',
  'bus-voltage' => '1.3.6.1.4.1.9839.2.1.3.5',
  'motor-voltage' => '1.3.6.1.4.1.9839.2.1.3.6',
  'power-req-0-1000-after-envelope' => '1.3.6.1.4.1.9839.2.1.3.7',
  'current-hour' => '1.3.6.1.4.1.9839.2.1.3.8',
  'current-minute' => '1.3.6.1.4.1.9839.2.1.3.9',
  'current-month' => '1.3.6.1.4.1.9839.2.1.3.10',
  'current-weekday' => '1.3.6.1.4.1.9839.2.1.3.11',
  'current-year' => '1.3.6.1.4.1.9839.2.1.3.12',
  'on-off-BMS' => '1.3.6.1.4.1.9839.2.1.3.13',
  'envelope-zone' => '1.3.6.1.4.1.9839.2.1.3.14',
  'ht-zone' => '1.3.6.1.4.1.9839.2.1.3.15',
  'cooling-capacity-after-envelope' => '1.3.6.1.4.1.9839.2.1.3.16',
  'valve-steps' => '1.3.6.1.4.1.9839.2.1.3.17',
  'y3-AOut3' => '1.3.6.1.4.1.9839.2.1.3.18',
  'current-day' => '1.3.6.1.4.1.9839.2.1.3.27',
  'fans-speed-percent' => '1.3.6.1.4.1.9839.2.1.3.28',
  'fans-speed-rpm' => '1.3.6.1.4.1.9839.2.1.3.29',
  'evd-valve-opening-percent' => '1.3.6.1.4.1.9839.2.1.3.30',
  'trapObjects' => '1.3.6.1.4.1.9839.2.1.100',
};

$Monitoring::GLPlugin::SNMP::MibsAndOids::definitions->{'KELVIN-PCOWEB-LCP-DX-MIB'} = {
};
package Monitoring::GLPlugin::SNMP::MibsAndOids::CARELUG40CDZMIB;

$Monitoring::GLPlugin::SNMP::MibsAndOids::origin->{'CAREL-UG40CDZ-MIB'} = {
  url => '',
  name => 'CAREL-UG40CDZ-MIB',
};

$Monitoring::GLPlugin::SNMP::MibsAndOids::mib_ids->{'CAREL-UG40CDZ-MIB'} =
    '1.3.6.1.4.1.9839.2.1';

$Monitoring::GLPlugin::SNMP::MibsAndOids::mibs_and_oids->{'CAREL-UG40CDZ-MIB'} = {
  'ug40cdzMIB' => '1.3.6.1.4.1.9839.2.1',
  'carel' => '1.3.6.1.4.1.9839',
  'systm' => '1.3.6.1.4.1.9839.1',
  'agentRelease' => '1.3.6.1.4.1.9839.1.1',
  'agentCode' => '1.3.6.1.4.1.9839.1.2',
  'instruments' => '1.3.6.1.4.1.9839.2',
  'webGateInfo' => '1.3.6.1.4.1.9839.2.0',
  'agentParameters' => '1.3.6.1.4.1.9839.2.0.1',
  'netSize' => '1.3.6.1.4.1.9839.2.0.1.1',
  'baudRate' => '1.3.6.1.4.1.9839.2.0.1.2',
  'unitTypeGroup' => '1.3.6.1.4.1.9839.2.0.2',
  'unit1-Type' => '1.3.6.1.4.1.9839.2.0.2.1',
  'unitCodeGroup' => '1.3.6.1.4.1.9839.2.0.3',
  'unit1-Code' => '1.3.6.1.4.1.9839.2.0.3.1',
  'unitSoftwareReleaseGroup' => '1.3.6.1.4.1.9839.2.0.4',
  'unit1-SoftwareRelease' => '1.3.6.1.4.1.9839.2.0.4.1',
  'unitMinSoftwareReleaseGroup' => '1.3.6.1.4.1.9839.2.0.5',
  'unit1-MinSoftwareRelease' => '1.3.6.1.4.1.9839.2.0.5.1',
  'unitMaxSoftwareReleaseGroup' => '1.3.6.1.4.1.9839.2.0.6',
  'unit1-MaxSoftwareRelease' => '1.3.6.1.4.1.9839.2.0.6.1',
  'unitNoAnswerCounterGroup' => '1.3.6.1.4.1.9839.2.0.7',
  'unit1-NoAnswerCounter' => '1.3.6.1.4.1.9839.2.0.7.1',
  'unitErrorChecksumCounterGroup' => '1.3.6.1.4.1.9839.2.0.8',
  'unit1-ErrorChecksumCounter' => '1.3.6.1.4.1.9839.2.0.8.1',
  'unitTimeoutCounterGroup' => '1.3.6.1.4.1.9839.2.0.9',
  'unit1-TimeoutCounter' => '1.3.6.1.4.1.9839.2.0.9.1',
  'ug40cdzMIB' => '1.3.6.1.4.1.9839.2.1',
  'digitalObjects' => '1.3.6.1.4.1.9839.2.1.1',
  'systemOn' => '1.3.6.1.4.1.9839.2.1.1.1',
  'compressore1' => '1.3.6.1.4.1.9839.2.1.1.2',
  'compressore2' => '1.3.6.1.4.1.9839.2.1.1.3',
  'compressore3' => '1.3.6.1.4.1.9839.2.1.1.4',
  'compressore4' => '1.3.6.1.4.1.9839.2.1.1.5',
  'heating1' => '1.3.6.1.4.1.9839.2.1.1.6',
  'heating2' => '1.3.6.1.4.1.9839.2.1.1.7',
  'hotGasCoil' => '1.3.6.1.4.1.9839.2.1.1.9',
  'dehumidification' => '1.3.6.1.4.1.9839.2.1.1.10',
  'humidification' => '1.3.6.1.4.1.9839.2.1.1.11',
  'emerg' => '1.3.6.1.4.1.9839.2.1.1.12',
  'alarmAccess' => '1.3.6.1.4.1.9839.2.1.1.20',
  'alarmRoomHT' => '1.3.6.1.4.1.9839.2.1.1.21',
  'alarmRoomLT' => '1.3.6.1.4.1.9839.2.1.1.22',
  'alarmRoomHH' => '1.3.6.1.4.1.9839.2.1.1.23',
  'alarmRoomLH' => '1.3.6.1.4.1.9839.2.1.1.24',
  'alarmRoomEAP' => '1.3.6.1.4.1.9839.2.1.1.25',
  'alarmFilter' => '1.3.6.1.4.1.9839.2.1.1.26',
  'alarmFlood' => '1.3.6.1.4.1.9839.2.1.1.27',
  'alarmAirFlow' => '1.3.6.1.4.1.9839.2.1.1.28',
  'alarmHeater' => '1.3.6.1.4.1.9839.2.1.1.29',
  'alarmHP1' => '1.3.6.1.4.1.9839.2.1.1.30',
  'alarmHP2' => '1.3.6.1.4.1.9839.2.1.1.31',
  'alarmLP1' => '1.3.6.1.4.1.9839.2.1.1.32',
  'alarmLP2' => '1.3.6.1.4.1.9839.2.1.1.33',
  'alarmEXV1' => '1.3.6.1.4.1.9839.2.1.1.34',
  'alarmEXV2' => '1.3.6.1.4.1.9839.2.1.1.35',
  'alarmPSE' => '1.3.6.1.4.1.9839.2.1.1.36',
  'alarmSmokeFire' => '1.3.6.1.4.1.9839.2.1.1.37',
  'alarmLAN' => '1.3.6.1.4.1.9839.2.1.1.38',
  'alarmHUHC' => '1.3.6.1.4.1.9839.2.1.1.39',
  'alarmHUPL' => '1.3.6.1.4.1.9839.2.1.1.40',
  'alarmHUWL' => '1.3.6.1.4.1.9839.2.1.1.41',
  'alarmCWDHT' => '1.3.6.1.4.1.9839.2.1.1.42',
  'alarmCWF' => '1.3.6.1.4.1.9839.2.1.1.43',
  'alarmCWFF' => '1.3.6.1.4.1.9839.2.1.1.44',
  'alarmCWHT' => '1.3.6.1.4.1.9839.2.1.1.45',
  'alarmRTS' => '1.3.6.1.4.1.9839.2.1.1.46',
  'alarmHWS' => '1.3.6.1.4.1.9839.2.1.1.47',
  'alarmCWS' => '1.3.6.1.4.1.9839.2.1.1.48',
  'alarmOTS' => '1.3.6.1.4.1.9839.2.1.1.49',
  'alarmDTS' => '1.3.6.1.4.1.9839.2.1.1.50',
  'alarmRHS' => '1.3.6.1.4.1.9839.2.1.1.51',
  'alarmCWOTS' => '1.3.6.1.4.1.9839.2.1.1.52',
  'alarmC1Hours' => '1.3.6.1.4.1.9839.2.1.1.53',
  'alarmC2Hours' => '1.3.6.1.4.1.9839.2.1.1.54',
  'alarmC3Hours' => '1.3.6.1.4.1.9839.2.1.1.55',
  'alarmC4Hours' => '1.3.6.1.4.1.9839.2.1.1.56',
  'alarmFilterHours' => '1.3.6.1.4.1.9839.2.1.1.57',
  'alarmHeat1Hours' => '1.3.6.1.4.1.9839.2.1.1.58',
  'alarmHeat2Hours' => '1.3.6.1.4.1.9839.2.1.1.59',
  'alarmHumHours' => '1.3.6.1.4.1.9839.2.1.1.60',
  'alarmUnitHours' => '1.3.6.1.4.1.9839.2.1.1.61',
  'alarmDI2' => '1.3.6.1.4.1.9839.2.1.1.62',
  'alarmDI4' => '1.3.6.1.4.1.9839.2.1.1.63',
  'alarmDI6' => '1.3.6.1.4.1.9839.2.1.1.64',
  'alarmHum' => '1.3.6.1.4.1.9839.2.1.1.65',
  'alarmGeneral' => '1.3.6.1.4.1.9839.2.1.1.66',
  'alarm2ndLevel' => '1.3.6.1.4.1.9839.2.1.1.67',
  'alarmA' => '1.3.6.1.4.1.9839.2.1.1.68',
  'alarmB' => '1.3.6.1.4.1.9839.2.1.1.69',
  'alarmC' => '1.3.6.1.4.1.9839.2.1.1.70',
  'selDXCW' => '1.3.6.1.4.1.9839.2.1.1.71',
  'selSW' => '1.3.6.1.4.1.9839.2.1.1.72',
  'systemOnOff' => '1.3.6.1.4.1.9839.2.1.1.75',
  'resetAlarm' => '1.3.6.1.4.1.9839.2.1.1.76',
  'resetHrsFilt' => '1.3.6.1.4.1.9839.2.1.1.77',
  'resetHrC1' => '1.3.6.1.4.1.9839.2.1.1.78',
  'resetHrC2' => '1.3.6.1.4.1.9839.2.1.1.79',
  'resetHrC3' => '1.3.6.1.4.1.9839.2.1.1.80',
  'resetHrC4' => '1.3.6.1.4.1.9839.2.1.1.81',
  'resetStC1' => '1.3.6.1.4.1.9839.2.1.1.82',
  'resetStC2' => '1.3.6.1.4.1.9839.2.1.1.83',
  'resetStC3' => '1.3.6.1.4.1.9839.2.1.1.84',
  'resetStC4' => '1.3.6.1.4.1.9839.2.1.1.85',
  'resetHrH1' => '1.3.6.1.4.1.9839.2.1.1.86',
  'resetHrH2' => '1.3.6.1.4.1.9839.2.1.1.87',
  'resetStH1' => '1.3.6.1.4.1.9839.2.1.1.88',
  'resetStH2' => '1.3.6.1.4.1.9839.2.1.1.89',
  'resetHrHU' => '1.3.6.1.4.1.9839.2.1.1.90',
  'resetStHU' => '1.3.6.1.4.1.9839.2.1.1.91',
  'resetHrACU' => '1.3.6.1.4.1.9839.2.1.1.92',
  'sleepmode' => '1.3.6.1.4.1.9839.2.1.1.95',
  'smtest' => '1.3.6.1.4.1.9839.2.1.1.96',
  'enablemeanT' => '1.3.6.1.4.1.9839.2.1.1.97',
  'analogObjects' => '1.3.6.1.4.1.9839.2.1.2',
  'roomTemp' => '1.3.6.1.4.1.9839.2.1.2.1',
  'outdoorTemp' => '1.3.6.1.4.1.9839.2.1.2.2',
  'deliveryTemp' => '1.3.6.1.4.1.9839.2.1.2.3',
  'cwTemp' => '1.3.6.1.4.1.9839.2.1.2.4',
  'hwTemp' => '1.3.6.1.4.1.9839.2.1.2.5',
  'roomRH' => '1.3.6.1.4.1.9839.2.1.2.6',
  'cwoTemp' => '1.3.6.1.4.1.9839.2.1.2.7',
  'evapPress1' => '1.3.6.1.4.1.9839.2.1.2.8',
  'evapPress2' => '1.3.6.1.4.1.9839.2.1.2.9',
  'suctTemp1' => '1.3.6.1.4.1.9839.2.1.2.10',
  'suctTemp2' => '1.3.6.1.4.1.9839.2.1.2.11',
  'evapTemp1' => '1.3.6.1.4.1.9839.2.1.2.12',
  'evapTemp2' => '1.3.6.1.4.1.9839.2.1.2.13',
  'ssh1' => '1.3.6.1.4.1.9839.2.1.2.14',
  'ssh2' => '1.3.6.1.4.1.9839.2.1.2.15',
  'coolRamp' => '1.3.6.1.4.1.9839.2.1.2.16',
  'heatRamp' => '1.3.6.1.4.1.9839.2.1.2.17',
  'fanSpeed' => '1.3.6.1.4.1.9839.2.1.2.18',
  'coolSetP' => '1.3.6.1.4.1.9839.2.1.2.20',
  'coolDiff' => '1.3.6.1.4.1.9839.2.1.2.21',
  'cool2SetP' => '1.3.6.1.4.1.9839.2.1.2.22',
  'heatSetP' => '1.3.6.1.4.1.9839.2.1.2.23',
  'heat2SetP' => '1.3.6.1.4.1.9839.2.1.2.24',
  'heatDiff' => '1.3.6.1.4.1.9839.2.1.2.25',
  'thrsHT' => '1.3.6.1.4.1.9839.2.1.2.26',
  'thrsLT' => '1.3.6.1.4.1.9839.2.1.2.27',
  'smCoolSetp' => '1.3.6.1.4.1.9839.2.1.2.28',
  'smHeatSetp' => '1.3.6.1.4.1.9839.2.1.2.29',
  'cwDehumSetp' => '1.3.6.1.4.1.9839.2.1.2.30',
  'cwHtThrsh' => '1.3.6.1.4.1.9839.2.1.2.31',
  'cwModeSetp' => '1.3.6.1.4.1.9839.2.1.2.32',
  'radcoolSpES' => '1.3.6.1.4.1.9839.2.1.2.33',
  'radcoolSpDX' => '1.3.6.1.4.1.9839.2.1.2.34',
  'delTempLimit' => '1.3.6.1.4.1.9839.2.1.2.35',
  'dtAutChgMLT' => '1.3.6.1.4.1.9839.2.1.2.36',
  'integerObjects' => '1.3.6.1.4.1.9839.2.1.3',
  'filterWorkH' => '1.3.6.1.4.1.9839.2.1.3.1',
  'unitWorkH' => '1.3.6.1.4.1.9839.2.1.3.2',
  'compr1WorkH' => '1.3.6.1.4.1.9839.2.1.3.3',
  'compr2WorkH' => '1.3.6.1.4.1.9839.2.1.3.4',
  'compr3WorkH' => '1.3.6.1.4.1.9839.2.1.3.5',
  'compr4WorkH' => '1.3.6.1.4.1.9839.2.1.3.6',
  'heat1WorkH' => '1.3.6.1.4.1.9839.2.1.3.7',
  'heat2WorkH' => '1.3.6.1.4.1.9839.2.1.3.8',
  'humiWorkH' => '1.3.6.1.4.1.9839.2.1.3.9',
  'dehumPband' => '1.3.6.1.4.1.9839.2.1.3.12',
  'humidPband' => '1.3.6.1.4.1.9839.2.1.3.13',
  'hhAlarmThrsh' => '1.3.6.1.4.1.9839.2.1.3.14',
  'lhAlarmThrsh' => '1.3.6.1.4.1.9839.2.1.3.15',
  'dehumSetp' => '1.3.6.1.4.1.9839.2.1.3.16',
  'smDehumSetp' => '1.3.6.1.4.1.9839.2.1.3.17',
  'humidSetp' => '1.3.6.1.4.1.9839.2.1.3.18',
  'smHumidSetp' => '1.3.6.1.4.1.9839.2.1.3.19',
  'pwOnDelay' => '1.3.6.1.4.1.9839.2.1.3.20',
  'regulDelay' => '1.3.6.1.4.1.9839.2.1.3.21',
  'lowPdelay' => '1.3.6.1.4.1.9839.2.1.3.22',
  'thAlarmdelay' => '1.3.6.1.4.1.9839.2.1.3.23',
  'regExcTime' => '1.3.6.1.4.1.9839.2.1.3.24',
  'stdbyTime' => '1.3.6.1.4.1.9839.2.1.3.25',
  'lanUnit' => '1.3.6.1.4.1.9839.2.1.3.27',
  'smCycleTime' => '1.3.6.1.4.1.9839.2.1.3.28',
  'exv1steps' => '1.3.6.1.4.1.9839.2.1.3.29',
  'exv2steps' => '1.3.6.1.4.1.9839.2.1.3.30',
};

$Monitoring::GLPlugin::SNMP::MibsAndOids::definitions->{'CAREL-UG40CDZ-MIB'} = {
};
package CheckRittalHealth::Rittal::CMCII::Component::SensorSubsystem;
use strict;
our @ISA = qw(Monitoring::GLPlugin::SNMP::Item);

sub init {
  my $self = shift;
  $self->get_snmp_table_objects('RITTAL-CMC-TC-MIB', [
      ['sensors', 'SensorTable', 'CheckRittalHealth::Rittal::CMCII::Component::SensorSubsystem::Sensor', sub { my $o = shift; $o->{unitSensorStatus} ne "notAvail" }],
  ]);
}

sub check {
  my $self = shift;
  if (! $self->check_messages) {
    $self->clear_all();
    $self->add_ok("all sensors are within their range");
  } else {

    $self->clear_all();
  }
}


package CheckRittalHealth::Rittal::CMCII::Component::SensorSubsystem::Sensor;
use strict;
our @ISA = qw(Monitoring::GLPlugin::SNMP::TableItem);

sub finish {
  my $self = shift;
  foreach (keys %{$self}) {
    next if ! /unit\dSensor/;
    if (/unit(\d+)(.*)/) {
      $self->{'unit'.$2} = $self->{$_};
      $self->{unit} = $1;
      delete $self->{$_};
    }
  }
  my @perftypes = qw(airFlow temperature humidity voltage rpm\d+LCP airTemp\d+LCP temp\d+LCP waterInTemp waterOutTemp waterFlow fanSpeed contrTemp2 frequencyPSM voltagePSM voltStatusPSM amperePSM kWPSM kWhPSM kWhTempPSM temperatureWL temperature1WL humidityWL);
  if (grep { $self->{unitSensorType} =~ /^$_$/ } @perftypes) {
    bless $self, "CheckRittalHealth::Rittal::CMCII::Component::SensorSubsystem::Sensor::Perf";
  }
}

sub check {
  my $self = shift;
  $self->add_info(sprintf "sensor %d.%d (%s) has status %s",
      $self->{unit}, $self->{unitSensorIndex}, $self->{unitSensorText}, $self->{unitSensorStatus});
  if ($self->{unitSensorStatus} eq "ok") {
    $self->add_ok();
  } elsif ($self->{unitSensorStatus} ne "ok") {
    $self->add_critical();
  }
}

package CheckRittalHealth::Rittal::CMCII::Component::SensorSubsystem::Sensor::Perf;
use strict;
our @ISA = qw(CheckRittalHealth::Rittal::CMCII::Component::SensorSubsystem::Sensor);

sub check {
  my $self = shift;
  $self->SUPER::check();
  my %perfdata = ();
  $perfdata{metric} = sprintf "%d.%d.%s", $self->{unit}, $self->{unitSensorIndex}, $self->{unitSensorText};
  $perfdata{label} = sprintf "%d.%d.%s", $self->{unit}, $self->{unitSensorIndex}, $self->{unitSensorText};
  if ($self->{unitSensorSetWarn}) {
    if ($self->{unitSensorSetLow}) {
      $perfdata{warning} = $self->{unitSensorSetLow}.":".$self->{unitSensorSetWarn};
    } else {
      $perfdata{warning} = $self->{unitSensorSetWarn};
    }
  }
  if ($self->{unitSensorSetHigh}) {
    $perfdata{critical} = $self->{unitSensorSetHigh};
  }
  $perfdata{value} = $self->{unitSensorValue};
  $self->add_perfdata(%perfdata);
}

package CheckRittalHealth::Rittal::CMCII::Component::UnitSubsystem;
use strict;
our @ISA = qw(Monitoring::GLPlugin::SNMP::Item);

sub init {
  my $self = shift;
  $self->{units} = [];
  foreach (1..4) {
    next if $self->opts->name && $self->opts->name ne $_;
    my $unit = CheckRittalHealth::Rittal::CMCII::Component::UnitSubsystem::Unit->new(unit => $_);
    push(@{$self->{units}}, $unit) if $unit->{cmcTcUnitStatus} ne "notAvail";
  }
}

sub check {
  my $self = shift;
  if ($self->mode =~ /device::units::list/) {
    foreach (@{$self->{units}}) {
      printf "unit %d (type %s, %d sensors)\n",
          $_->{unit}, $_->{cmcTcUnitTypeOfDevice}, scalar(@{$_->{sensors}});
    }
  } elsif ($self->mode =~ /device::sensors::list/) {
    foreach my $unit (@{$self->{units}}) {
      foreach (@{$unit->{sensors}}) {
        printf "unit %d sensor %d (type %s)\n",
            $unit->{unit}, $_->{unitSensorIndex}, $_->{unitSensorType};
      }
    }
  } elsif ($self->mode =~ /device::sensors::health/) {
    foreach (@{$self->{units}}) {
      $_->check();
    }
    if (! $self->check_messages) {
      $self->clear_all();
      $self->add_ok("all units are working fine");
    } else {
      $self->clear_ok(); # show only faulted sensors
    }
  }
}

package CheckRittalHealth::Rittal::CMCII::Component::UnitSubsystem::Unit;
use strict;
our @ISA = qw(Monitoring::GLPlugin::SNMP::Item);

sub init {
  my $self = shift;
  my %params = @_;
  $self->{unit} = $params{unit};
  $self->get_snmp_objects('RITTAL-CMC-TC-MIB', (
      'cmcTcUnit'.$self->{unit}.'TypeOfDevice',
      'cmcTcUnit'.$self->{unit}.'Text',
      'cmcTcUnit'.$self->{unit}.'Status',
      'cmcTcUnit'.$self->{unit}.'NumberOfSensors'
  ));
  foreach (qw(TypeOfDevice Text Status NumberOfSensors)) {
    $self->{'cmcTcUnit'.$_} = $self->{'cmcTcUnit'.$self->{unit}.$_};
    delete $self->{'cmcTcUnit'.$self->{unit}.$_};
  }
  $self->add_info(sprintf 'unit %d has status %s',
      $self->{unit}, $self->{'cmcTcUnitStatus'});
  if ($self->{'cmcTcUnitStatus'} ne "notAvail") {
    $self->get_snmp_tables('RITTAL-CMC-TC-MIB', [
        ['sensors', 'cmcTcUnit'.$self->{unit}.'SensorTable', 'CheckRittalHealth::Rittal::CMCII::Component::SensorSubsystem::Sensor', sub { my $o = shift; ($o->{unitSensorStatus} ne "notAvail") && (! $self->opts->name2 || ($self->opts->name2 && $self->opts->name2 eq $o->{unitSensorIndex})) }],
    ]);
  }
}

sub check {
  my $self = shift;
  $self->info(sprintf "%s -unit %d has status %s (%s)",
      $self->{cmcTcUnitTypeOfDevice}, $self->{unit}, $self->{cmcTcUnitStatus},
      $self->{cmcTcUnitText});
  if ($self->{cmcTcUnitStatus} =~ /^(ok|detected)$/) {
    $self->add_ok();
  } elsif ($self->{cmcTcUnitStatus} =~ /^(timeout|error)$/) {
    $self->add_critical();
  } elsif ($self->{cmcTcUnitStatus} =~ /^(timeout|error)$/) {
    $self->add_warning();
  }
  foreach (@{$self->{sensors}}) {
    $_->check();
  }
}

package CheckRittalHealth::Rittal::CMCII;
use strict;
our @ISA = qw(CheckRittalHealth::Rittal);


sub init {
  my $self = shift;
  $self->get_snmp_objects('RITTAL-CMC-TC-MIB',
      qw(cmcTcMibCondition cmcTcStatusDeviceCMC cmcTcUnitsConnected));
  if (! $self->{cmcTcMibCondition}) {
    $self->add_critical('snmpwalk returns no health data (rittal-cmc-mib)');
  }
  if ($self->mode =~ /device::(units|sensors)/) {
    if ($self->{cmcTcMibCondition} ne "ok") {
      $self->add_warning(sprintf "mib condition is %s fault", $self->{cmcTcMibCondition});
    }
    $self->analyze_and_check_unit_subsystem('CheckRittalHealth::Rittal::CMCII::Component::UnitSubsystem');
  } else {
    $self->no_such_mode();
  }
}



package CheckRittalHealth::Rittal::CMCIII::Component::DeviceSubsystem;
our @ISA = qw(Monitoring::GLPlugin::SNMP::Item);
use strict;

sub init {
  my $self = shift;
  $self->mult_snmp_max_msg_size(10);
  $self->get_snmp_objects('RITTAL-CMC-III-MIB',
      qw(cmcIIIUnitStatus cmcIIIUnitType cmcIIIUnitSerial
      cmcIIIUnitProd cmcIIISetTempUnit cmcIIIOverallDevStatus
      cmcIIINumberOfDevs cmcIIINumberOfVars));
  $self->get_snmp_tables('RITTAL-CMC-III-MIB', [
      ['devices', 'cmcIIIDevTable', 'CheckRittalHealth::Rittal::CMCIII::Component::DeviceSubsystem::Device'],
      ['variables', 'cmcIIIVarTable', 'CheckRittalHealth::Rittal::CMCIII::Component::DeviceSubsystem::Variable'],
  ]);
  #if ($self->filter_name($dev->{cmcIIIDevIndex})) {
  $self->assign();
}

sub assign {
  my $self = shift;
  foreach my $dev (@{$self->{devices}}) {
    $dev->{variables} = [];
    foreach my $var (@{$self->{variables}}) {
      if ($dev->{cmcIIIDevIndex} eq $var->{cmcIIIVarDeviceIndex}) {
        push(@{$dev->{variables}}, $var);
      }
    }
    @{$dev->{variables}} = sort {
        $a->{cmcIIIVarIndex} <=> $b->{cmcIIIVarIndex} 
    } @{$dev->{variables}};
  }
  @{$self->{devices}} = sort {
      $a->{cmcIIIDevIndex} <=> $b->{cmcIIIDevIndex}
  } @{$self->{devices}};
  foreach (@{$self->{devices}}) {
    $_->group_variables();
  }
}

sub check {
  my $self = shift;
  if ($self->mode =~ /device::devices::list/) {
    foreach (@{$self->{devices}}) {
      #printf "dev%d\n", $unit if $self->{"unit$unit"}->{cmcTcUnitStatus} ne "notAvail";
      printf "%s\n", Data::Dumper::Dumper($_);
    }
    $self->add_ok("have fun");
  } elsif ($self->mode =~ /device::variables::list/) {
    foreach (@{$self->{variables}}) {
      printf "%s\n", Data::Dumper::Dumper($_);
    }
    $self->add_ok("have fun");
  } elsif ($self->mode =~ /device::(units|sensors)/) {
    my $info = sprintf 'cmc-tc has %d devices connected, device status is %s',
        $self->{cmcIIINumberOfDevs}, $self->{cmcIIIOverallDevStatus};
    $self->add_info($info);
    if ($self->{cmcIIIOverallDevStatus} ne 'ok') {
      $self->add_critical(sprintf 'overall device status is %s',
          $self->{cmcIIIOverallDevStatus});
    } else {
      $self->add_ok();
    }
    foreach (@{$self->{devices}}) {
      $_->check();
    }
    delete $self->{variables};
  } else {
    $self->no_such_mode();
  }
}


package CheckRittalHealth::Rittal::CMCIII::Component::DeviceSubsystem::Device;
our @ISA = qw(Monitoring::GLPlugin::SNMP::TableItem);
use strict;

sub finish {
  my $self = shift;
  $self->{cmcIIIDevIndex} = $self->{indices}->[0];
  $self->{perf_variables} = [];
}

sub check {
  my $self = shift;
  $self->add_info(sprintf 'device %d (%s) has status %s',
      $self->{cmcIIIDevIndex}, $self->{cmcIIIDevName},
      $self->{cmcIIIDevStatus});
  if ($self->{cmcIIIDevStatus} ne 'ok') {
    if ($self->{cmcIIIDevStatusText}) {
      $self->add_critical($self->{cmcIIIDevStatusText});
    } else {
      $self->add_critical();
    }
  }
  foreach (@{$self->{variables}}) {
    $_->check();
  }
  foreach (@{$self->{perf_variables}}) {
    $_->check();
  }
}

sub group_variables {
  my $self = shift;
  my $all_variables = {};
  my $perf_variables = {};
  my $group_names = {};
  my $has_value = {};
  my $has_status = {};
  foreach my $var (@{$self->{variables}}) {
    $all_variables->{$var} = 1; # make an inventory
    if ($var->{cmcIIIVarName} =~ /(.*)\.Value$/) {
      my $prefix = $1;
      $has_value->{$prefix} = $var->{cmcIIIVarValueStr};
    }
  }
  foreach my $var (@{$self->{variables}}) {
    if ($var->{cmcIIIVarName} =~ /(.*)\.Status$/) {
      my $prefix = $1;
      $has_status->{$prefix} = $var->{cmcIIIVarValueStr};
    }
  }
  foreach (@{$self->{variables}}) {
    $_->{cmcIIIVarName} =~ /^(.*)\.(.*?)$/;
    my $var_item = $1;
    my $var_var = $2;
    # only total power, not power on the single lines
    next if $var_item !~ /(Total.*\.Power\.Active)|Temperature| Temp|Humidity|Supply|Access|Leakage|(Fuses\.Fuse\s+\d+)|(Speed.*Fan\d+)/;
    # skip electric supply, we want cooling/air supply only
    next if $var_item =~ /Supply.*(\d+V|\d+V\d+)$/;
    # looks like a sollwert
    # var 2/109 (Config.Fans.Fan1) has status 80 %
    next if $var_item =~ /Config\.Fan/;
    $perf_variables->{$var_item} = {} if ! exists $perf_variables->{$var_item};
    $perf_variables->{$var_item}->{valid} = 0 if ! exists $perf_variables->{$var_item}->{valid};
    $perf_variables->{$var_item}->{$var_var} = $_->{cmcIIIVarValueStr};
    if (exists $has_value->{$var_item}) {
      if ($var_var eq "Value") {
        # variable is cmcIIIVarName: Temperature.Value
        # cmcIIIVarValueStr is pretty ok, like
        # cmcIIIVarValueStr: 26.80 degree C
        # but we can do better:
        if ($_->{cmcIIIVarScale} > 0) {
          $perf_variables->{$var_item}->{$var_var} =
              $_->{cmcIIIVarValueInt} * $_->{cmcIIIVarScale};
        } elsif ($_->{cmcIIIVarScale} < 0) {
          $perf_variables->{$var_item}->{$var_var} =
              $_->{cmcIIIVarValueInt} / abs($_->{cmcIIIVarScale});
        } else {
          $perf_variables->{$var_item}->{$var_var} = $_->{cmcIIIVarValueInt};
        }
        if ($var_item =~ /Remote/ and
            # Das Folgende gilt fuer Fans,Valve uvm. nicht nur Temperaturen
            exists $has_status->{$var_item} and
            exists $has_value->{$var_item} and
            $has_status->{$var_item} eq "Off" and
            $has_value->{$var_item} =~ /^0/) {
            # z.b.
            # var 2/183 (Remote.Temperature.DescName) has status Remote Temperature
            # var 2/184 (Remote.Temperature.Value) has status 0.00 degree C
            # var 2/185 (Remote.Temperature.Timeout) has status 0 s
            # var 2/186 (Remote.Temperature.Mode) has status Off
            # var 2/187 (Remote.Temperature.Status) has status Off
            # das Zeug ist wahrsch. nicht mal eingesteckt.
            next;
        }
        $perf_variables->{$var_item}->{cmcIIIVarGroupName} = $var_item;
        $perf_variables->{$var_item}->{cmcIIIVarDeviceIndex} = $_->{cmcIIIVarDeviceIndex};
        $perf_variables->{$var_item}->{cmcIIIVarUnit} = $_->{cmcIIIVarUnit};
        $perf_variables->{$var_item}->{valid} = 1;
      }
    } elsif (exists $has_status->{$var_item}) {
      if ($var_var eq "Status" and
          $var_item =~ /Leakage|Fuses\.Fuse\s+\d+/) {
        # there is no variable cmcIIIVarName: Leakage.Value
        # we need to work with cmcIIIVarName: Leakage.Status
        # which has cmcIIIVarType: status, cmcIIIVarValueStr: OK and
        # cmcIIIVarValueInt: 4, whatever this value means. 
        # 8.1.25: and there can also be cmcIIIVarValueStr: Leakage Sensor
        $perf_variables->{$var_item} = {} if ! exists $perf_variables->{$var_item};
        $perf_variables->{$var_item}->{$var_var} = $_->{cmcIIIVarValueStr};
        $perf_variables->{$var_item}->{Value} = $_->{cmcIIIVarValueInt};
        $perf_variables->{$var_item}->{cmcIIIVarGroupName} = $var_item;
        $perf_variables->{$var_item}->{cmcIIIVarDeviceIndex} = $_->{cmcIIIVarDeviceIndex};
        $perf_variables->{$var_item}->{cmcIIIVarUnit} = $_->{cmcIIIVarUnit};
        $perf_variables->{$var_item}->{valid} = 1;
      } elsif ($var_var ne "Value" and index($_->{cmcIIIVarUnit}, "degree") != -1 and $var_item =~ /Temperature/) {
        # das sind jetzt die "Values" so eines Multi-Temperatur-Dingens
        # Im Gegensatz zu dem naechsten elsif, der einen Satz Variablen
        # gruppiert, welche nur eine .Value haben
        # var 2/6 (Air.Temperature.DescName) has status Air-Temperatures
        # var 2/7 (Air.Temperature.In-Top) has status 17.3 degree C
        # var 2/8 (Air.Temperature.In-Mid) has status 18.1 degree C
        # var 2/9 (Air.Temperature.In-Bot) has status 18.8 degree C
        # var 2/10 (Air.Temperature.Out-Top) has status 27.0 degree C
        # var 2/11 (Air.Temperature.Out-Mid) has status 26.4 degree C
        # var 2/12 (Air.Temperature.Out-Bot) has status 27.9 degree C
        # var 2/13 (Air.Temperature.Status) has status OK
        # var 2/14 (Air.Temperature.Category) has status 2
        # im Gegensatz zu
        # System.Temperature.DescName
        # System.Temperature.Value
        # System.Temperature.Status
        # ...
        # Temperature.DescName
        # Temperature.Value
        # Temperature.Status
        # ...
        # Coolant.Temperature.Supply.DescName
        # Coolant.Temperature.Supply.Value
        # Coolant.Temperature.Supply.Status
        # ...
        # Coolant.Temperature.Return.DescName
        # Coolant.Temperature.Return.Value
        # Coolant.Temperature.Return.Status
        # ...
        if ($_->{cmcIIIVarScale} > 0) {
          $perf_variables->{$var_item}->{$var_var} =
              $_->{cmcIIIVarValueInt} * $_->{cmcIIIVarScale};
        } elsif ($_->{cmcIIIVarScale} < 0) {
          $perf_variables->{$var_item}->{$var_var} =
              $_->{cmcIIIVarValueInt} / abs($_->{cmcIIIVarScale});
        } else {
          $perf_variables->{$var_item}->{$var_var} = $_->{cmcIIIVarValueInt};
        }
        # later in the check, we need to know which of the attributes of
        # a VariableGroup is a metric
        if (! exists $perf_variables->{$var_item}->{perf_vars}) {
          $perf_variables->{$var_item}->{perf_vars} = [$var_var];
        } else {
          push(@{$perf_variables->{$var_item}->{perf_vars}}, $var_var);
        }
        $perf_variables->{$var_item}->{cmcIIIVarGroupName} = $var_item;
        $perf_variables->{$var_item}->{cmcIIIVarDeviceIndex} = $_->{cmcIIIVarDeviceIndex};
        $perf_variables->{$var_item}->{cmcIIIVarUnit} = $_->{cmcIIIVarUnit};
        $perf_variables->{$var_item}->{valid} = 1;
      }
    }
  }
  # Duplicates ist eine Struktur mit DescName als Key und einer Liste aus
  # cmcIIIVarGroupName als Value.
  # Falls also mehrere gleichlautende DescName vergeben wurden (konkretes
  # Beispiel: alle Fans.Current Speed.Fan*.Value haben als DescName "Fan")
  # dann haengt unter dem DescName-Key ein Array mit langen Variablennamen.
  my $duplicates = {};
  foreach (sort keys %{$perf_variables}) {
    next if ! $perf_variables->{$_}->{valid};
    if (exists $duplicates->{$perf_variables->{$_}->{DescName}}) {
      push(@{$duplicates->{$perf_variables->{$_}->{DescName}}}, $perf_variables->{$_}->{cmcIIIVarGroupName});
    } else {
      $duplicates->{$perf_variables->{$_}->{DescName}} = [$perf_variables->{$_}->{cmcIIIVarGroupName}];
    }
  }
  my @to_del = ();
  foreach my $descname (keys %{$duplicates}) {
    # 1 Element, nicht doppelt
    push(@to_del, $descname) if scalar(@{$duplicates->{$descname}}) <= 1;
  }
  foreach (@to_del) {
    delete $duplicates->{$_};
  }
  # Jetzt werden aus denausfuehrlichen Variablennamen die gemeinsamen
  # Bestandteile entfernt.
  $duplicates = $self->remove_common_words($duplicates);
  foreach (sort keys %{$perf_variables}) {
    next if ! $perf_variables->{$_}->{valid};
    # if all the fans have DescName of "Fan", we need to take the numbered
    # version from var_item/cmcIIIVarGroupName
    # var 2/89 (Fans.Current Speed.Fan1.DescName) has status Fan
    # var 2/90 (Fans.Current Speed.Fan1.Value) has status 10 %
    # var 2/91 (Fans.Current Speed.Fan1.Status) has status OK
    # var 2/92 (Fans.Current Speed.Fan1.Category) has status 2
    # var 2/93 (Fans.Current Speed.Fan2.DescName) has status Fan
    # var 2/94 (Fans.Current Speed.Fan2.Value) has status 10 %
    # var 2/95 (Fans.Current Speed.Fan2.Status) has status OK
    # var 2/96 (Fans.Current Speed.Fan2.Category) has status 2
    # var 2/97 (Fans.Current Speed.Fan3.DescName) has status Fan
    # var 2/98 (Fans.Current Speed.Fan3.Value) has status 9 %
    # var 2/99 (Fans.Current Speed.Fan3.Status) has status OK
    # var 2/100 (Fans.Current Speed.Fan3.Category) has status 2
    # var 2/101 (Fans.Current Speed.Fan4.DescName) has status Fan
    # var 2/102 (Fans.Current Speed.Fan4.Value) has status 0 %
    # var 2/103 (Fans.Current Speed.Fan4.Status) has status Inactive
    # var 2/104 (Fans.Current Speed.Fan4.Category) has status 2
    #
    # same with temperatures. we need to add the location
    # var 2/16 (Air Temp.Server In.Top.DescName) has status Air Temperature
    # var 2/17 (Air Temp.Server In.Top.Value) has status 18.40 degree C
    # var 2/18 (Air Temp.Server In.Top.SetPtHighAlarm) has status 50.00 degree C
    # var 2/19 (Air Temp.Server In.Top.SetPtHighWarning) has status 40.00 degree C
    # var 2/20 (Air Temp.Server In.Top.SetPtLowWarning) has status 15.00 degree C
    # var 2/21 (Air Temp.Server In.Top.SetPtLowAlarm) has status 10.00 degree C
    # var 2/22 (Air Temp.Server In.Top.Hysteresis) has status 5.00 %
    # var 2/23 (Air Temp.Server In.Top.Status) has status OK
    # var 2/24 (Air Temp.Server In.Top.Category) has status 2
    # var 2/25 (Air Temp.Server In.Center.DescName) has status Air Temperature
    # var 2/26 (Air Temp.Server In.Center.Value) has status 18.60 degree C
    # var 2/27 (Air Temp.Server In.Center.SetPtHighAlarm) has status 50.00 degree C
    # var 2/28 (Air Temp.Server In.Center.SetPtHighWarning) has status 40.00 degree C
    # var 2/29 (Air Temp.Server In.Center.SetPtLowWarning) has status 15.00 degree C
    # var 2/30 (Air Temp.Server In.Center.SetPtLowAlarm) has status 10.00 degree C
    # var 2/31 (Air Temp.Server In.Center.Hysteresis) has status 5.00 %
    # var 2/32 (Air Temp.Server In.Center.Status) has status OK
    # var 2/33 (Air Temp.Server In.Center.Category) has status 2
    # DRECKSSCHEISSE!!!!! Rein, Raus, alles gleich!!!!
    # var 2/53 (Air Temp.Server Out.Top.DescName) has status Air Temperature
    # var 2/54 (Air Temp.Server Out.Top.Value) has status 22.10 degree C
    # var 2/55 (Air Temp.Server Out.Top.SetPtHighAlarm) has status 50.00 degree C
    # var 2/56 (Air Temp.Server Out.Top.SetPtHighWarning) has status 40.00 degree C
    # var 2/57 (Air Temp.Server Out.Top.SetPtLowWarning) has status 15.00 degree C
    # var 2/58 (Air Temp.Server Out.Top.SetPtLowAlarm) has status 10.00 degree C
    # var 2/59 (Air Temp.Server Out.Top.Hysteresis) has status 5.00 %
    # var 2/60 (Air Temp.Server Out.Top.Status) has status OK
    # var 2/61 (Air Temp.Server Out.Top.Category) has status 2

    # Wenn DescName doppelt, dann ersetzen durch den verkuerzten,
    # aber einzigartigen cmcIIIVarGroupName.
    if ($perf_variables->{$_}->{DescName} and
        exists $duplicates->{$perf_variables->{$_}->{DescName}}) {
      $perf_variables->{$_}->{DescName} = shift @{$duplicates->{$perf_variables->{$_}->{DescName}}};
    }
    push(@{$self->{perf_variables}}, 
        CheckRittalHealth::Rittal::CMCIII::Component::DeviceSubsystem::VariableGroup->new(%{$perf_variables->{$_}}));
  }
}

sub split_label {
  my ($self, $label, $separator) = @_;
  if ($separator eq ".") {
    $label =~ s/\./_____/g;
  } else {
    $label =~ s/./_____/g;
  }
  return split /_____/, $label;
}

sub find_common_words {
    my ($self, $split_on, @labels) = @_;
    my @split_labels = map { [$self->split_label($_, $split_on)] } @labels;
    my %word_count;
    # Count the occurrence of each word across all labels
    for my $label (@split_labels) {
        for my $word (@$label) {
            $word_count{$word}++;
        }
    }
    # Identify common words (appear in all labels)
    my @common_words;
    for my $word (keys %word_count) {
        push @common_words, $word if $word_count{$word} == @split_labels;
    }
    return @common_words;
}

sub remove_common_words {
    my ($self, $data) = @_;
    foreach my $key (keys %$data) {
        my $labels = $data->{$key};
        next unless @$labels;
        my @common_words = $self->find_common_words(".", @$labels);
        # Remove the common words from each label
        @$labels = map {
            my $label = $_;
            # Split label into words, remove common words, and rejoin them
            my @words = $self->split_label($label, ".");
            my @remaining_words = grep { my $word = $_; !grep { $_ eq $word } @common_words } @words;
            join('.', @remaining_words);
        } @$labels;
        @$labels = map {
          if ($_ =~ /^$key/) {
            # 'Fan' => ['Fans.Current Speed.Fan1', 'Fans.Current Speed.Fan2',
            # becomes ['Fan1', 'Fan2'...
            $_;
          } else {
            # 'Air Temperature' => ['Air Temp.Server In.Average',
            #                       'Air Temp.Server In.Bottom',
            # becomes
            #     'Air Temperature Server In.Average',
            #     'Air Temperature Server In.Bottom',
            $key." ".$_;
          }
        } @$labels;
    }
    return $data;
}

package CheckRittalHealth::Rittal::CMCIII::Component::DeviceSubsystem::Variable;
our @ISA = qw(Monitoring::GLPlugin::SNMP::TableItem);
use strict;

sub finish {
  my $self = shift;
  $self->{cmcIIIVarDeviceIndex} = $self->{indices}->[0];
  $self->{cmcIIIVarIndex} = $self->{indices}->[1];
  if ($self->{cmcIIIVarValueStr} =~ /^(?:[0-9a-f]{2} )+[0-9a-f]{2}$/i) {
    $self->{cmcIIIVarValueStr} =~ s/\s//g;
    $self->{cmcIIIVarValueStr} =~ s/(([0-9a-f][0-9a-f])+)/pack('H*', $1)/ie;
  }
  if ($self->{cmcIIIVarUnit} =~ /^(?:[0-9a-f]{2} )+[0-9a-f]{2}$/i) {
    $self->{cmcIIIVarUnit} =~ s/\s//g;
    $self->{cmcIIIVarUnit} =~ s/(([0-9a-f][0-9a-f])+)/pack('H*', $1)/ie;
  }
}

sub check {
  my $self = shift;
  $self->add_info(sprintf 'var %d/%d (%s) has status %s',
      $self->{cmcIIIVarDeviceIndex}, $self->{cmcIIIVarIndex},
      $self->{cmcIIIVarName}, $self->{cmcIIIVarValueStr});
}

package CheckRittalHealth::Rittal::CMCIII::Component::DeviceSubsystem::VariableGroup;
our @ISA = qw(Monitoring::GLPlugin::SNMP::TableItem);
use strict;

sub finish {
  my $self = shift;
if (! $self->{DescName}) {
 printf "SCHEIS %s\n", Data::Dumper::Dumper($self);
}
  $self->{DescName} ||= $self->{cmcIIIVarGroupName}; # undef ist mir schon untergekommen
#
#
# JA TOLL, GENAU DANN, WENN ICH DEN SCHEISS AUSROLLEN WILL, KOMMT SOWAS DAHER:
#$VAR1 = bless( {
#  'DescName' => 'Air-Temperatures',
#  'Out-Top' => '27.2 degree C',
#  'Status' => 'OK',
#  'name' => 'dev__Air-Temperatures',
#  'Out-Mid' => '26.6 degree C',
#  'In-Mid' => '18.7 degree C',
#  'In-Top' => '18.0 degree C',
#  'Category' => '2 ',
#  'Out-Bot' => '28.0 degree C',
#  'In-Bot' => '19.5 degree C'
#}, 'CheckRittalHealth::Rittal::CMCIII::Component::DeviceSubsystem::VariableGroup' );
#
#
if (! $self->{DescName}) {
 printf "%s\n", Data::Dumper::Dumper($self);
}
  $self->{name} = "dev ".$self->{cmcIIIVarDeviceIndex}." ".$self->{DescName};
  $self->{name} =~ s/\s/_/g;
  foreach (qw(cmcIIIVarUnit)) {
    if (defined $self->{$_}) {
      $self->{$_} =~ s/[^%\w]//g;
    }
  }
  foreach (qw(SetPtHighAlarm SetPtHighWarning SetPtLowAlarm SetPtLowWarning Value cmcIIIVarUnit)) {
    if (defined $self->{$_}) {
      $self->{$_} =~ s/^(-*[\d\.]+).*/$1/g;
    }
  }
  if ($self->{cmcIIIVarGroupName} =~ /Temperature/ and $self->{DescName} =~ /Temperatures/) {
    bless $self, 'CheckRittalHealth::Rittal::CMCIII::Component::DeviceSubsystem::MultiTemperatureGroup';
  } elsif ($self->{cmcIIIVarGroupName} =~ /Temperature/) {
    bless $self, 'CheckRittalHealth::Rittal::CMCIII::Component::DeviceSubsystem::TemperatureGroup';
  } elsif ($self->{cmcIIIVarGroupName} =~ /Humidity/) {
    bless $self, 'CheckRittalHealth::Rittal::CMCIII::Component::DeviceSubsystem::HumidityGroup';
  } elsif ($self->{cmcIIIVarGroupName} =~ /Supply/) {
    bless $self, 'CheckRittalHealth::Rittal::CMCIII::Component::DeviceSubsystem::SupplyGroup';
  } elsif ($self->{cmcIIIVarGroupName} =~ /Leakage/) {
    bless $self, 'CheckRittalHealth::Rittal::CMCIII::Component::DeviceSubsystem::LeakageGroup';
  } elsif ($self->{cmcIIIVarGroupName} =~ /Access/) {
    bless $self, 'CheckRittalHealth::Rittal::CMCIII::Component::DeviceSubsystem::AccessGroup';
  } elsif ($self->{cmcIIIVarGroupName} =~ /Total\.Power\.Active/) {
    bless $self, 'CheckRittalHealth::Rittal::CMCIII::Component::DeviceSubsystem::PowerGroup';
  } elsif ($self->{cmcIIIVarGroupName} =~ /Fuse/) {
    bless $self, 'CheckRittalHealth::Rittal::CMCIII::Component::DeviceSubsystem::FuseGroup';
  } elsif ($self->{cmcIIIVarGroupName} =~ /Fans.*Speed/) {
    bless $self, 'CheckRittalHealth::Rittal::CMCIII::Component::DeviceSubsystem::FanGroup';
  } else {
}
}

sub check {
  my $self = shift;
  $self->add_info(sprintf '%s has status %s',
      $self->{name}, $self->{Status}
  );
  if ($self->{Status} ne "OK" and $self->{Status} ne "n.a." and
      # kuehl genug, blaest nicht
      not ($self->{Status} eq "Inactive" and $self->{DescName} =~ /Fan/)) {
    $self->add_critical();
  }
  if ($self->{SetPtLowWarning} || $self->{SetPtHighWarning} ||
      $self->{SetPtLowAlarm} || $self->{SetPtHighAlarm}) {
    # die setzen wir nur, wenn's danach aussieht, als haette einer was
    # eingetragen. 'dev_2_Power_Active'=63;0:0;0:0;; sieht bloed aus.
    $self->set_thresholds(metric => $self->{name},
        warning => $self->{SetPtLowWarning}.":".$self->{SetPtHighWarning},
        critical => $self->{SetPtLowAlarm}.":".$self->{SetPtHighAlarm});
  }
  $self->add_perfdata(label => $self->{name},
      uom => $self->{cmcIIIVarUnit} eq "%" ? 
          $self->{cmcIIIVarUnit} : undef,
      value => $self->{Value});
}

package CheckRittalHealth::Rittal::CMCIII::Component::DeviceSubsystem::FanGroup;
our @ISA = qw(CheckRittalHealth::Rittal::CMCIII::Component::DeviceSubsystem::VariableGroup);
use strict;

package CheckRittalHealth::Rittal::CMCIII::Component::DeviceSubsystem::TemperatureGroup;
our @ISA = qw(CheckRittalHealth::Rittal::CMCIII::Component::DeviceSubsystem::VariableGroup);
use strict;

package CheckRittalHealth::Rittal::CMCIII::Component::DeviceSubsystem::MultiTemperatureGroup;
our @ISA = qw(CheckRittalHealth::Rittal::CMCIII::Component::DeviceSubsystem::VariableGroup);
use strict;

sub check {
  my $self = shift;
  foreach (@{$self->{perf_vars}}) {
    $self->add_perfdata(label => $self->{name}.'_'.$_,
        value => $self->{$_});
  }
}

package CheckRittalHealth::Rittal::CMCIII::Component::DeviceSubsystem::HumidityGroup;
our @ISA = qw(CheckRittalHealth::Rittal::CMCIII::Component::DeviceSubsystem::VariableGroup);
use strict;

package CheckRittalHealth::Rittal::CMCIII::Component::DeviceSubsystem::SupplyGroup;
our @ISA = qw(CheckRittalHealth::Rittal::CMCIII::Component::DeviceSubsystem::VariableGroup);
use strict;

package CheckRittalHealth::Rittal::CMCIII::Component::DeviceSubsystem::LeakageGroup;
our @ISA = qw(CheckRittalHealth::Rittal::CMCIII::Component::DeviceSubsystem::VariableGroup);
use strict;

sub check {
  my $self = shift;
  $self->add_info(sprintf '%s has status %s',
      $self->{name}, $self->{Status}
  );
  if ($self->{Status} ne "OK" and $self->{Status} ne "n.a.") {
    $self->add_critical();
  }
  # Zum Kotzen sowas. Weil einer unbedingt seine Lecksensoren im Grafana
  # anzeigen will. Wozu gibt's eigentlich Monitoring?
  # Wenn Nagios Alarm schlägt, dann hol Eimer und Lappen und wisch auf!
  # Aber die Herrschaften starren lieber auf Dashboards und kommen
  # sich cool vor. Wie mich das ankotzt :-(((
  $self->add_perfdata(label => $self->{name},
      value => $self->{Status} eq "OK" ? 0 : 1,
  ) if $self->{Status} ne "n.a.";
}

package CheckRittalHealth::Rittal::CMCIII::Component::DeviceSubsystem::FuseGroup;
our @ISA = qw(CheckRittalHealth::Rittal::CMCIII::Component::DeviceSubsystem::VariableGroup);
use strict;

sub check {
  my $self = shift;
  $self->add_info(sprintf '%s has status %s',
      $self->{name}, $self->{Status}
  );
  if ($self->{Status} ne "OK" and $self->{Status} ne "n.a.") {
    $self->add_critical();
  }
  # Siehe Lecksensoren. Muss unbedingt ins Grafana, unbedingt!
  $self->add_perfdata(label => $self->{name},
      value => $self->{Status} eq "OK" ? 0 : 1,
  ) if $self->{Status} ne "n.a.";
}

package CheckRittalHealth::Rittal::CMCIII::Component::DeviceSubsystem::PowerGroup;
our @ISA = qw(CheckRittalHealth::Rittal::CMCIII::Component::DeviceSubsystem::VariableGroup);
use strict;


package CheckRittalHealth::Rittal::CMCIII::Component::DeviceSubsystem::AccessGroup;
our @ISA = qw(CheckRittalHealth::Rittal::CMCIII::Component::DeviceSubsystem::VariableGroup);
use strict;

sub check {
  my $self = shift;
  $self->add_info(sprintf '%s has status %s',
      $self->{name}, $self->{Status}
  );
  if ($self->{Status} eq "Open") {
    $self->add_critical_mitigation();
  }
  $self->add_perfdata(label => $self->{name},
      value => $self->{Status} eq "Open" ? 1 : 0,
  );
}
package CheckRittalHealth::Rittal::CMCIII::Component::MessageSubsystem;
our @ISA = qw(Monitoring::GLPlugin::SNMP::Item);
use strict;

sub init {
  my $self = shift;
  $self->get_snmp_objects('RITTAL-CMC-III-MIB',
      qw(cmcIIIOverallMsgStatus cmcIIINumberOfMsgs));
  $self->get_snmp_tables('RITTAL-CMC-III-MIB', [
      ['messages', 'cmcIIIMsgTable', 'CheckRittalHealth::Rittal::CMCIII::Component::MessageSubsystem::Message'],
  ]);
}

sub check {
  my $self = shift;
  if ($self->mode =~ /device::devices::list/) {
    foreach (@{$self->{devices}}) {
      #printf "dev%d\n", $unit if $self->{"unit$unit"}->{cmcTcUnitStatus} ne "notAvail";
      printf "%s\n", Data::Dumper::Dumper($_);
    }
    $self->add_ok("have fun");
  } elsif ($self->mode =~ /device::variables::list/) {
    foreach (@{$self->{variables}}) {
      printf "%s\n", Data::Dumper::Dumper($_);
    }
    $self->add_ok("have fun");
  } elsif ($self->mode =~ /device::(units|sensors)/) {
    my $info = sprintf 'message status is %s',
        $self->{cmcIIIOverallMsgStatus};
    $self->add_info($info);
    if ($self->{cmcIIIOverallMsgStatus} ne 'ok') {
      $self->add_critical();
    } else {
      #$self->add_ok();
    }
    foreach (@{$self->{messages}}) {
      $_->check();
    }
    #delete $self->{variables};
  } else {
    $self->no_such_mode();
  }
}


package CheckRittalHealth::Rittal::CMCIII::Component::MessageSubsystem::Message;
our @ISA = qw(Monitoring::GLPlugin::SNMP::TableItem);
use strict;

sub finish {
  my $self = shift;
  $self->{cmcIIIDevIndex} = $self->{indices}->[0];
  if ($self->{cmcIIIMsgStatusText} =~ /^(?:[0-9a-f]{2} )+[0-9a-f]{2}$/i) {
    $self->{cmcIIIMsgStatusText} =~ s/\s//g;
    $self->{cmcIIIMsgStatusText} =~ s/(([0-9a-f][0-9a-f])+)/pack('H*', $1)/ie;
  }
}

sub check {
  my $self = shift;
  $self->add_info(sprintf '%s message: %s',
      $self->{cmcIIIMsgQuality}, $self->{cmcIIIMsgStatusText});
  if ($self->{cmcIIIMsgQuality} =~ /^warning/) {
    $self->add_warning();
  } elsif ($self->{cmcIIIMsgQuality} =~ /^alarm/) {
    $self->add_critical();
  } elsif ($self->{cmcIIIMsgQuality} =~ /^undefined/) {
    $self->add_unknown();
  }
}

package CheckRittalHealth::Rittal::CMCIII;
our @ISA = qw(CheckRittalHealth::Rittal);
use strict;


sub init {
  my $self = shift;
  if ($self->mode =~ /device::(units|sensors)/) {
    $self->get_snmp_objects('RITTAL-CMC-III-MIB',
        qw(cmcIIIUnitStatus));
    if (! $self->{cmcIIIUnitStatus}) {
      $self->add_critical('snmpwalk returns no health data (rittal-cmc-mib)');
    } else {
      $self->analyze_and_check_device_subsystem('CheckRittalHealth::Rittal::CMCIII::Component::DeviceSubsystem');
      $self->analyze_and_check_message_subsystem('CheckRittalHealth::Rittal::CMCIII::Component::MessageSubsystem');
    }
  } else {
    $self->no_such_mode();
  }
}

sub check {
  my $self = shift;

}

package CheckRittalHealth::Rittal;
our @ISA = qw(CheckRittalHealth::Device);
use strict;

sub init {
  my $self = shift;
  if ($self->{productname} =~ /Rittal CMC.*III/ ||
      $self->implements_mib('RITTAL-CMC-III-MIB')) {
    bless $self, 'CheckRittalHealth::Rittal::CMCIII';
    $self->debug('using CheckRittalHealth::Rittal::CMCIII');
  } elsif ($self->{productname} =~ /Rittal CMC/ ||
      $self->implements_mib('RITTAL-CMC-TC-MIB')) {
    bless $self, 'CheckRittalHealth::Rittal::CMCII';
    $self->debug('using CheckRittalHealth::Rittal::CMCII');
  } else {
    $self->no_such_model();
  }
  $self->init();
}

package CheckRittalHealth::Carel::pCOWeb::Component::SensorSubsystem;
use strict;
our @ISA = qw(Monitoring::GLPlugin::SNMP::Item);

sub init {
  my $self = shift;
  # Keine Ahnung, was dieser Rotz in diesem Plugin verloren hat
  # Zumindest eine leise Ahnung, von wem es ausging.
  $self->get_snmp_objects('KELVIN-PCOWEB-LCP-DX-MIB', qw(
      gentRelease  agentCode  pCOId1-Status  pCOId1-ErrorsNumber  din1  din2  din3  din4  din5  din6  din7  din8  din9  din10  dobj11  dobj12  dobj13  dobj14  dobj15  dobj16  dout1  dout2  dout3  dout4  dout5  dout6  dout7  dout8  dout9  dout10  dout11  dout12  bms-res-alarm  al-envelope  al-start-fail-lock  mal-start-failure-msk  mal-discharge-ht  dobj34  mal-dp-startup  mal-dp-lubrification-oil  mal-b1  mal-b2  mal-b3  mal-b4  mal-b5  mal-b6  mal-b7  mal-b8  mal-b9  mal-b10  mal-b11  mal-b12  b1-value  b2-value  b3-value  b4-value  b5-value  b6-value  b7-value  b8-value  b9-value  b10-value  b11-value  b12-value  evap-temp  cond-temp  aobj15  aobj16  aobj17  aobj18  aobj19  aobj20  medium-temp-out  medium-temp-in  rotor-speed-rps  motor-current  aobj47  setpoint-lcp  rotor-speed-hz  drive-status  error-code  drive-temp  bus-voltage  motor-voltage  power-req-0-1000-after-envelope  current-hour  current-minute  current-month  current-weekday  current-year  on-off-BMS  envelope-zone  ht-zone  cooling-capacity-after-envelope  valve-steps  y3-AOut3  current-day  fans-speed-percent  fans-speed-rpm  evd-valve-opening-percent
  ));
}

sub check {
  my $self = shift;
  $self->add_info("pCOId1 status is ".$self->{"pCOId1-Status"});
  if ($self->{"pCOId1-Status"} ne "online") {
    $self->add_warning();
  }
  if (defined $self->{"fans-speed-percent"}) {
    $self->set_thresholds(metric => "fans_speed_pct",
        warning => 80,
        critical => 95
    );
    $self->add_message($self->check_thresholds(metric => "fans_speed_pct",
        value => $self->{"fans-speed-percent"}
    ));
  }
}


package CheckRittalHealth::Carel::pCOWeb::Component::SensorSubsystem::Sensor;
use strict;
our @ISA = qw(Monitoring::GLPlugin::SNMP::TableItem);

sub finish {
  my $self = shift;
  foreach (keys %{$self}) {
    next if ! /unit\dSensor/;
    if (/unit(\d+)(.*)/) {
      $self->{'unit'.$2} = $self->{$_};
      $self->{unit} = $1;
      delete $self->{$_};
    }
  }
  my @perftypes = qw(airFlow temperature humidity voltage rpm\d+LCP airTemp\d+LCP temp\d+LCP waterInTemp waterOutTemp waterFlow fanSpeed contrTemp2 frequencyPSM voltagePSM voltStatusPSM amperePSM kWPSM kWhPSM kWhTempPSM temperatureWL temperature1WL humidityWL);
  if (grep { $self->{unitSensorType} =~ /^$_$/ } @perftypes) {
    bless $self, "CheckRittalHealth::Carel::pCOWeb::Component::SensorSubsystem::Sensor::Perf";
  }
}

sub check {
  my $self = shift;
  $self->add_info(sprintf "sensor %d.%d (%s) has status %s",
      $self->{unit}, $self->{unitSensorIndex}, $self->{unitSensorText}, $self->{unitSensorStatus});
  if ($self->{unitSensorStatus} eq "ok") {
    $self->add_ok();
  } elsif ($self->{unitSensorStatus} ne "ok") {
    $self->add_critical();
  }
}

package CheckRittalHealth::Carel::pCOWeb::Component::SensorSubsystem::Sensor::Perf;
use strict;
our @ISA = qw(CheckRittalHealth::Carel::pCOWeb::Component::SensorSubsystem::Sensor);

sub check {
  my $self = shift;
  $self->SUPER::check();
  my %perfdata = ();
  $perfdata{metric} = sprintf "%d.%d.%s", $self->{unit}, $self->{unitSensorIndex}, $self->{unitSensorText};
  $perfdata{label} = sprintf "%d.%d.%s", $self->{unit}, $self->{unitSensorIndex}, $self->{unitSensorText};
  if ($self->{unitSensorSetWarn}) {
    if ($self->{unitSensorSetLow}) {
      $perfdata{warning} = $self->{unitSensorSetLow}.":".$self->{unitSensorSetWarn};
    } else {
      $perfdata{warning} = $self->{unitSensorSetWarn};
    }
  }
  if ($self->{unitSensorSetHigh}) {
    $perfdata{critical} = $self->{unitSensorSetHigh};
  }
  $perfdata{value} = $self->{unitSensorValue};
  $self->add_perfdata(%perfdata);
}

package CheckRittalHealth::Carel::pCOWeb;
use strict;
our @ISA = qw(CheckRittalHealth::Carel);


sub init {
  my $self = shift;
  if ($self->mode =~ /device::(sensors)/) {
die;
    $self->analyze_and_check_sensor_subsystem('CheckRittalHealth::Carel::pCOWeb::Component::SensorSubsystem');
  } else {
    $self->no_such_mode();
  }
}



package CheckRittalHealth::Carel;
our @ISA = qw(CheckRittalHealth::Device);
use strict;

sub init {
  my $self = shift;
  if ($self->implements_mib('KELVIN-PCOWEB-LCP-DX-MIB') and
      $self->get_snmp_object("KELVIN-PCOWEB-LCP-DX-MIB", "current-year") == 21) {
    bless $self, 'CheckRittalHealth::Carel::pCOWeb';
    $self->debug('using CheckRittalHealth::Carel::pCOWeb');
  } else {
    $self->no_such_model();
  }
  $self->init();
}

package CheckRittalHealth::Device;
our @ISA = qw(Monitoring::GLPlugin::SNMP);
use strict;

sub classify {
  my $self = shift;
  if (! ($self->opts->hostname || $self->opts->snmpwalk)) {
    $self->add_unknown('either specify a hostname or a snmpwalk file');
  } else {
    $self->check_snmp_and_model();
    if ($self->opts->servertype) {
      $self->{productname} = 'storeever' if $self->opts->servertype eq 'storeever';
    }
    if (! $self->check_messages()) {
      if ($self->opts->verbose && $self->opts->verbose) {
        printf "I am a %s\n", $self->{productname};
      }
      if ($self->opts->mode =~ /^my-/) {
        $self->load_my_extension();
      } elsif ($self->{productname} =~ /Rittal/i) {
        bless $self, 'CheckRittalHealth::Rittal';
        $self->debug('using CheckRittalHealth::Rittal');
      } elsif ($self->implements_mib("CAREL-UG40CDZ-MIB")) {
        bless $self, 'CheckRittalHealth::Carel';
        $self->debug('using CheckRittalHealth::Carel');
      } else {
        if (my $class = $self->discover_suitable_class()) {
          bless $self, $class;
          $self->debug('using '.$class);
        } else {
          bless $self, 'CheckRittalHealth::Generic';
          $self->debug('using CheckRittalHealth::Generic');
        }
      }
    }
  }
  return $self;
}


package CheckRittalHealth::Generic;
our @ISA = qw(CheckRittalHealth::Device);
use strict;

sub init {
  my $self = shift;
  if ($self->mode =~ /something specific/) {
  } else {
    bless $self, 'Monitoring::GLPlugin::SNMP';
    $self->no_such_mode();
  }
}

package main;
package CheckRittalHealth;
use strict;
no warnings qw(once);

sub run_plugin {
  my $plugin_class = (caller(0))[0]."::Device";
  if ( ! grep /BEGIN/, keys %Monitoring::GLPlugin::) {
    eval {
      require Monitoring::GLPlugin;
      require Monitoring::GLPlugin::SNMP;
    };
    if ($@) {
      printf "UNKNOWN - module Monitoring::GLPlugin was not found. Either build a standalone version of this plugin or set PERL5LIB\n";
      printf "%s\n", $@;
      exit 3;
    }
  }
  my $plugin = $plugin_class->new(
      shortname => '',
      usage => 'Usage: %s [ -v|--verbose ] [ -t <timeout> ] '.
          '--mode <what-to-do> '.
          '--hostname <network-component> --community <snmp-community>'.
          '  ...]',
      version => '4.1.1',
      blurb => 'This plugin checks various parameters of rittal cmc ',
      url => 'http://labs.consol.de/nagios/check_rittal_health',
      timeout => 60,
  );

  $plugin->add_mode(
      internal => 'device::sensors::health',
      spec => 'hardware-health',
      alias => ['overall-health'],
      help => 'Check the state of the sensors',
  );
  $plugin->add_mode(
      internal => 'device::units::list',
      spec => 'list-units',
      alias => undef,
      help => 'Show the units of the cmc ii and update the name cache',
  );
  $plugin->add_mode(
      internal => 'device::sensors::list',
      spec => 'list-sensors',
      alias => undef,
      help => 'Show the sensors of the cmc ii and update the name cache',
  );
  $plugin->add_mode(
      internal => 'device::devices::list',
      spec => 'list-devices',
      alias => undef,
      help => 'Show the devices of the cmc ii and update the name cache',
  );
  $plugin->add_snmp_modes();
  $plugin->add_snmp_args();
  $plugin->add_default_args();
  $plugin->mod_arg("name",
      help => "--name
     The name (number) of a unit",
  );
  $plugin->mod_arg("name2",
      help => "--name2
     The name (number) of a sensor",
  );
  
  $plugin->getopts();
  $plugin->classify();
  $plugin->validate_args();

  if (! $plugin->check_messages()) {
    $plugin->init();
    if (! $plugin->check_messages()) {
      $plugin->add_ok($plugin->get_summary())
          if $plugin->get_summary();
      $plugin->add_ok($plugin->get_extendedinfo(" "))
          if $plugin->get_extendedinfo();
    }
  }
  my ($code, $message) = $plugin->opts->multiline ?
      $plugin->check_messages(join => "\n", join_all => ', ') :
      $plugin->check_messages(join => ', ', join_all => ', ');
  $message .= sprintf "\n%s\n", $plugin->get_info("\n")
      if $plugin->opts->verbose >= 1;

  $plugin->nagios_exit($code, $message);
}

1;

join('', map { ucfirst } split(/_/, (split(/\//, (split ' ', "check_rittal_health" // '')[0]))[-1]))->run_plugin();
