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

package Nagios::MiniPlugin;

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

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

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

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

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

our %STATUS_TEXT = reverse %ERRORS;

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

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

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

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

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

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

sub add_perfdata {
  my ($self, %args) = @_;
  my $str = $args{label}.'='.$args{value};
  if ($args{uom}) {
    $str .= $args{uom};
  }
  if ($args{warning}) {
    $str .= ';'.$args{warning};
  }
  if ($args{critical}) {
    $str .= ';'.$args{critical};
  }
  push @{$self->{perfdata}}, $str;
}


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

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

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

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

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

  return ($code, $message);
}

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

package Nagios::MiniPlugin::Getopt;

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

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

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

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

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

  $self
}

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

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

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

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

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

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

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

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


package SNMP::Utils;

use strict;

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

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

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

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

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

}

package HP::Proliant::Component::EventSubsystem;
our @ISA = qw(HP::Proliant::Component);

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

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    runtime => $params{runtime},
    rawdata => $params{rawdata},
    method => $params{method},
    condition => $params{condition},
    status => $params{status},
    events => [],
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
    boottime => 0,
  };
  bless $self, $class;
  if ($self->{method} eq 'snmp') {
    $self = HP::Proliant::Component::EventSubsystem::SNMP->new(%params);
    my $sysUpTime = SNMP::Utils::get_object(
        $self->{rawdata}, '1.3.6.1.2.1.1.3.0') || 3600*24*100;
    $self->{boottime} = int(time - $sysUpTime / 100);
  } elsif ($self->{method} eq 'cli') {
    $self = HP::Proliant::Component::EventSubsystem::CLI->new(%params);
    my $uptime = do { local (@ARGV, $/) = "/proc/uptime"; my $x = <>; close ARGV; $x };
    # also watch 10 minutes of booting before the operating system starts ticking
    $self->{boottime} = time - int((split(/\s+/, $uptime))[0]) - 600;
  } else {
    die "unknown method";
  }
  # repair dates
  my $lasttime = 0;
  for my $event (reverse @{$self->{events}}) {
    if ($event->{cpqHeEventLogUpdateTime} != 0) {
      $lasttime = $event->{cpqHeEventLogUpdateTime};
    } else {
      $event->{cpqHeEventLogUpdateTime} = $lasttime;
    }
  }
  # maybe the most recent events had zero timestamps.
  # fill them up with timestamps from the past.
  for my $event (@{$self->{events}}) {
    if ($event->{cpqHeEventLogUpdateTime} != 0) {
      $lasttime = $event->{cpqHeEventLogUpdateTime};
    } else {
      $event->{cpqHeEventLogUpdateTime} = $lasttime;
    }
  }
  # we need the boottime in the event's check method
  for my $event (@{$self->{events}}) {
    $event->{boottime} = $self->{boottime};
  }
  return $self;
}

sub check {
  my $self = shift;
  my $errorfound = 0;
  $self->add_info('checking events');
  if (scalar (@{$self->{events}}) == 0) {
    #$self->overall_check(); 
    $self->add_info('no events found');
  } else {
    foreach (sort { $a->{cpqHeEventLogEntryNumber} <=> $b->{cpqHeEventLogEntryNumber}}
        @{$self->{events}}) {
      $_->check($self->{warningtime}, $self->{criticaltime});
    }
  }
}

sub dump {
  my $self = shift;
  foreach (sort { $a->{cpqHeEventLogEntryNumber} <=> $b->{cpqHeEventLogEntryNumber} } @{$self->{events}}) {
    $_->dump();
  }
}


package HP::Proliant::Component::EventSubsystem::Event;
our @ISA = qw(HP::Proliant::Component::EventSubsystem);

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

{
  our $interesting_events = {
    # POST Error: 201-Memory Error Multi-bit error occurred during memory initialization, Board 1, Bank B. Bank containing DIMM(s) has been disabled..
    # POST Error: 201-Memory Error Single-bit error occured during memory initialization, Board 1, DIMM 1. Bank containing DIMM(s) has been disabled..
    # POST Error: 207-Memory initialization error on Memory Board 5 DIMM 7. The operating system may not have access to all of the memory installed in the system..
    # POST Error: 207-Invalid Memory Configuration-Mismatched DIMMs within DIMM Bank Memory in Bank A Not Utilized..  
    # POST Error: 210 - Quick Path Interconnect (QPI) Link Degradation. A QPI link is operating in a degraded performace state..
    'POST Messages' => [
      '201-Memory', '207-Memory',
      '210\s*-\s*Quick Path Interconnect.*degraded.*'
    ],
    'Main Memory' => [
      'Corrected Memory Error threshold exceeded',
      'Uncorrectable Memory Error',
    ],
  };
}

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    runtime => $params{runtime},
    cpqHeEventLogEntryNumber => $params{cpqHeEventLogEntryNumber},
    cpqHeEventLogEntrySeverity => lc $params{cpqHeEventLogEntrySeverity},
    cpqHeEventLogEntryClass => $params{cpqHeEventLogEntryClass},
    cpqHeEventLogEntryCount => $params{cpqHeEventLogEntryCount} || 1,
    cpqHeEventLogInitialTime => $params{cpqHeEventLogInitialTime},
    cpqHeEventLogUpdateTime => $params{cpqHeEventLogUpdateTime},
    cpqHeEventLogErrorDesc => $params{cpqHeEventLogErrorDesc},
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  if (! $self->{cpqHeEventLogInitialTime}) {
    $self->{cpqHeEventLogInitialTime} = $self->{cpqHeEventLogUpdateTime};
  }
  # 
  #
  #++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  #                      |warn                 |crit                          |now
  #
  #<----- ignore -------><----- warning ------><---------- critical --------->
  #
  # If we have --eventrange <warnlookback>/<critlookback>
  #  Very young events are shown as critical
  #  If the event gets older, it is shown as warning
  #  At some time, the event is no longer shown
  # Without --eventrange the event is shown as critical until you manually repair it
  if ($params{runtime}->{options}->{eventrange}) {
    my ($warningrange, $criticalrange) = split(/\//, $params{runtime}->{options}->{eventrange});
    if (! $criticalrange) {
      $criticalrange = $warningrange;
    }
    if ($criticalrange =~ /^(\d+)[s]*$/) {
      $criticalrange = $1;
    } elsif ($criticalrange =~ /^(\d+)m$/) {
      $criticalrange = $1 * 60;
    } elsif ($criticalrange =~ /^(\d+)h$/) {
      $criticalrange = $1 * 3600;
    } elsif ($criticalrange =~ /^(\d+)d$/) {
      $criticalrange = $1 * 3600 * 24;
    } else {
      die "range has to be <number>[smhd]";
    }
    if ($warningrange =~ /^(\d+)[s]*$/) {
      $warningrange = $1;
    } elsif ($warningrange =~ /^(\d+)m$/) {
      $warningrange = $1 * 60;
    } elsif ($warningrange =~ /^(\d+)h$/) {
      $warningrange = $1 * 3600;
    } elsif ($warningrange =~ /^(\d+)d$/) {
      $warningrange = $1 * 3600 * 24;
    } else {
      die "range has to be <number>[smhd]";
    }
    $self->{warningtime} = time - $warningrange;
    $self->{criticaltime} = time - $criticalrange;
  } else {
    $self->{warningtime} = 0;
    $self->{criticaltime} = 0;
  }
  bless $self, $class;
  return $self;
}

sub check {
  my $self = shift;
  $self->blacklist('evt', $self->{cpqHeEventLogEntryNumber});
  # only check severity "critical" and "caution"
  # optional: only check interesting events
  # POST events only if they date maximum from reboot-5min
  # younger than critical? -> critical
  # 
  $self->add_info(sprintf "Event: %d Added: %s Class: (%s) %s %s",
      $self->{cpqHeEventLogEntryNumber},
      $self->{cpqHeEventLogUpdateTime},
      $self->{cpqHeEventLogEntryClass},
      $self->{cpqHeEventLogEntrySeverity},
      $self->{cpqHeEventLogErrorDesc});
  if ($self->{cpqHeEventLogEntrySeverity} eq "caution" ||
      $self->{cpqHeEventLogEntrySeverity} eq "critical") {
    # also watch 10 minutes of booting before the operating system starts ticking
    if ($self->{cpqHeEventLogUpdateTime} >= $self->{boottime}) {
      foreach my $class (keys %{$HP::Proliant::Component::EventSubsystem::Event::interesting_events}) {
        foreach my $pattern (@{$HP::Proliant::Component::EventSubsystem::Event::interesting_events->{$class}}) {
          if ($self->{cpqHeEventLogErrorDesc} =~ /$pattern/) {
            if ($self->{cpqHeEventLogUpdateTime} < $self->{warningtime}) {
              # you didn't care for this problem too long.
              # don't say i didn't warn you.
              if (0) {
                # auto-ack?
              } 
              last;
            } elsif ($self->{cpqHeEventLogUpdateTime} < $self->{criticaltime}) {
              $self->add_message(WARNING, $self->{info});
              last;
            } else {
              $self->add_message(CRITICAL, $self->{info});
              last;
            }
          }
        }
      }
    }
  } else {
    # info, repair...
  }
}

sub dump { 
  my $self = shift;
  printf "[EVENT_%s]\n", $self->{cpqHeEventLogEntryNumber};
  foreach (qw(cpqHeEventLogEntryNumber cpqHeEventLogEntrySeverity
      cpqHeEventLogEntryCount cpqHeEventLogInitialTime
      cpqHeEventLogUpdateTime cpqHeEventLogErrorDesc)) {
    if ($_ =~ /.*Time$/) {
      printf "%s: %s\n", $_, scalar localtime $self->{$_};
    } else {
      printf "%s: %s\n", $_, $self->{$_};
    }
  }
  printf "info: %s\n\n", $self->{info};
}



package HP::Proliant::Component::EventSubsystem::CLI;
our @ISA = qw(HP::Proliant::Component::EventSubsystem);

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

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


sub init {
  my $self = shift;
  my %params = @_;
  my %tmpevent = (
    runtime => $params{runtime},
  );
  my $inblock = 0;
  foreach (grep(/^iml/, split(/\n/, $self->{rawdata}))) {
    s/^iml\s*//g;
    if (/^Event:\s+(\d+)\s+[\w]+:\s+(\d+)\/(\d+)\/(\d+)\s+(\d+):(\d+)/) {
      # Event: 31 Added: 09/22/2011 05:11
      #         1         2  3    4  5  6
      $tmpevent{cpqHeEventLogEntryNumber} = $1;
      if ($4 == 0) {
        # Event: 29 Added: 00/00/0000 00:00
        $tmpevent{cpqHeEventLogUpdateTime} = 0;
      } else {
        eval {
          $tmpevent{cpqHeEventLogUpdateTime} = timelocal(0, $6, $5, $3, $2 - 1, $4);
        };
        if ($@) {
          # Event: 10 Added: 27/27/2027 27:27
          $tmpevent{cpqHeEventLogUpdateTime} = 0;
        }
      }
      $inblock = 1;
    } elsif (/^(\w+):\s+(.*?)\s+\-\s+(.*)/) {
      $tmpevent{cpqHeEventLogEntrySeverity} = $1;
      $tmpevent{cpqHeEventLogEntryClass} = $2;
      $tmpevent{cpqHeEventLogErrorDesc} = $3;
      if ($tmpevent{cpqHeEventLogErrorDesc} =~ /.*?:\s+(\d+)/) {
          $tmpevent{cpqHeEventLogEntryCode} = $1;
      } else {
          $tmpevent{cpqHeEventLogEntryCode} = 0;
      }
    } elsif (/^\s*$/) {
      if ($inblock) {
        $inblock = 0;
        push(@{$self->{events}},
            HP::Proliant::Component::EventSubsystem::Event->new(%tmpevent));
        %tmpevent = (
          runtime => $params{runtime},
        );
      }
    }
  }
  if ($inblock) {
    push(@{$self->{events}},
        HP::Proliant::Component::EventSubsystem::Event->new(%tmpevent));
  }
}



package HP::Proliant::Component::EventSubsystem::SNMP;
our @ISA = qw(HP::Proliant::Component::EventSubsystem
    HP::Proliant::Component::SNMP);

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

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

sub overall_init {
  my $self = shift;
  my %params = @_;
  my $snmpwalk = $params{rawdata};
  # overall
  my $cpqHeEventLogSupported  = '1.3.6.1.4.1.232.6.2.11.1.0';
  my $cpqHeEventLogSupportedValue = {
    1 => 'other',
    2 => 'notSupported',
    3 => 'supported',
    4 => 'clear',
  };
  my $cpqHeEventLogCondition  = '1.3.6.1.4.1.232.6.2.11.2.0';
  my $cpqHeEventLogConditionValue = {
    1 => 'other',
    2 => 'ok',
    3 => 'degraded',
    4 => 'failed',
  };
  $self->{eventsupp} = SNMP::Utils::get_object_value(
      $snmpwalk, $cpqHeEventLogSupported,
      $cpqHeEventLogSupportedValue);
  $self->{eventstatus} = SNMP::Utils::get_object_value(
      $snmpwalk, $cpqHeEventLogCondition,
      $cpqHeEventLogConditionValue);
  $self->{eventsupp} |= lc $self->{eventsupp};
  $self->{eventstatus} |= lc $self->{eventstatus};
}

sub init {
  my $self = shift;
  my %params = @_;
  my $snmpwalk = $self->{rawdata};
  my $oids = {
      cpqHeEventLogEntry => "1.3.6.1.4.1.232.6.2.11.3.1",
      cpqHeEventLogEntryNumber => "1.3.6.1.4.1.232.6.2.11.3.1.1",
      cpqHeEventLogEntrySeverity => "1.3.6.1.4.1.232.6.2.11.3.1.2",
      cpqHeEventLogEntryClass => "1.3.6.1.4.1.232.6.2.11.3.1.3",
      cpqHeEventLogEntryCode => "1.3.6.1.4.1.232.6.2.11.3.1.4",
      cpqHeEventLogEntryCount => "1.3.6.1.4.1.232.6.2.11.3.1.5",
      cpqHeEventLogInitialTime => "1.3.6.1.4.1.232.6.2.11.3.1.6",
      cpqHeEventLogUpdateTime => "1.3.6.1.4.1.232.6.2.11.3.1.7",
      cpqHeEventLogErrorDesc => "1.3.6.1.4.1.232.6.2.11.3.1.8",

      cpqHeEventLogEntryClassValue => {
          #  2 Fan Failure (Fan 1, Location I/O Board)
          #    Internal Storage System Overheating (Slot 0, Zone 1, Location Storage, Temperature Unknown)
          #    System Fans Not Redundant (Location I/O Board)
          #  MY MUSTARD: only critical events should lead to an alert, if at all. The caution events mean "loss of redundancy".
          #              We monitor temperatures and fan status anyway.
          #2 => "",
          #  3 Corrected Memory Error threshold exceeded (System Memory, Memory Module 1)
          #    Uncorrectable Memory Error detected by ROM-based memory validation (Board 1, Memory Module 4)
          #  MY MUSTARD: threshold exceeded is caution. Uncorrectable errors are critical. Both should be detected anyway.
          3 => "Main Memory",
          #  4 Accelerator Cache Memory Parity Error (Socket 1)
          #4 => "",
          #  5 Processor Correctable error threshold exceeded (Board 0, Processor 2)
          #5 => "",
          #  6 Unrecoverable Intermodule Bus error (Error code 0x00000000)
          #6 => "",
          #  8 PCI Bus Error (Slot 0, Bus 0, Device 0, Function 0)
          8 => "PCI Bus",
          # 10 1720-S.M.A.R.T. Hard Drive Detects Imminent Failure
          #    POST Error: 201-Memory Error Multi-bit error occurred during memory initialization, Board 1, Bank B. Bank containing DIMM(s) has been disabled..
          #    POST Error: 201-Memory Error Single-bit error occured during memory initialization, Board 1, DIMM 1. Bank containing DIMM(s) has been disabled..
          #    POST Error: 207-Memory Configuration Warning - memory boards should be installed sequentially.
          #    POST Error: 210-Memory Board Failure on board 4.
          #    POST Error: 210-Memory Board Power Fault on board 3.
          #    POST Error: 207-Memory initialization error on Memory Board 5 DIMM 7. The operating system may not have access to all of the memory installed in the system..         
          #    POST Error: 207-Invalid Memory Configuration-Mismatched DIMMs within DIMM Bank Memory in Bank A Not Utilized..
          10 => "POST Messages",
          11 => "Power Subsystem",
          13 => "ASR",
          # 14 Automatic Operating System Shutdown Initiated Due to Overheat Condition
          #    Automatic Operating System Shutdown Initiated Due to Fan Failure
          #    Blue Screen Trap (BugCheck, STOP: 0x00000050 (0x9CB2C5B4, 0x00000001, 0x00000004, 0x00000000))
          #    Operating System failure (BugCheck, STOP: 0x000000AB (0x00000005, 0x00000488, 0x00000000, 0x00000002))
          14 => "OS Class",
          # 15 Unknown Event (Class 15, Code 255)
          #15 => "",
          # 17 Network Adapter Link Down (Slot 0, Port 4)
          #    Network Adapters Redundancy Reduced (Slot 0, Port 1)
          17 => "Network Adapter",
          # 19 Drive Array Device Failure (Slot 0, Bus 2, Bay 4)
          #    Internal SAS Enclosure Device Failure (Bay 1, Box 1, Port 2I, Slot 1)
          #19 => "",
          # 20 An Unrecoverable System Error (NMI) has occurred
          #    Unrecoverable System Error has occurred (Error code 0x01AE0E2F, 0x00000000)
          20 => "Unrecoverable System Error",
          # 32 ROM flashed (New version: 01/09/2008)
          32 => "System Revision",
          # 33 IML Cleared (Administrator)
          #    IML cleared through HP ProLiant Health Agent (cmahealthd)
          #    Insight Diagnostics Note: Physisches Festplattenlaufwerk 5, Controller Steckplatz 0-Diagnosis: Fehlgeschlagen
          33 => "Maintenance Note",
          # 34 New Chassis Connected (Enclosure Address 27AC)
          #    Loss Of Chassis Connectivity (Enclosure Serial Number 8004******)
          #    Server Blade Enclosure Server Blade Inserted (Slot 16, Enclosure Address 0000)
          #34 => "",
      },
      cpqHeEventLogEntrySeverityValue => {
          2 => "informational",
          3 => "infoWithAlert",
          6 => "repaired",
          9 => "caution",
          15 => "critical",
      },
      # Time 
      # 07 D8 09 02 11 11
  };
  # INDEX { cpqHeEventLogEntryNumber }
  foreach ($self->get_entries($oids, 'cpqHeEventLogEntry')) {
    if ($_->{cpqHeEventLogInitialTime} =~ /^(([0-9a-fA-F]{2})( [0-9a-fA-F]{2})*)\s*$/) {
      $_->{cpqHeEventLogInitialTime} =~ s/ //;
      my  ($year, $month, $day, $hour, $min) = map { hex($_) } split(/\s+/, $_->{cpqHeEventLogInitialTime});
      if ($year == 0) {
        $_->{cpqHeEventLogInitialTime} = 0;
      } else {
        eval {
          $_->{cpqHeEventLogInitialTime} = timelocal(0, $min, $hour, $day, $month - 1, $year);
        };
        if ($@) {
          $_->{cpqHeEventLogInitialTime} = 0;
        }
      }
    } elsif ($_->{cpqHeEventLogInitialTime} =~ /^0x([0-9a-fA-F]{4})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})/) {
      my  ($year, $month, $day, $hour, $min) = map { hex($_) } ($1, $2, $3, $4, $5);
      if ($year == 0) {
        $_->{cpqHeEventLogInitialTime} = 0;
      } else {
        eval {
          $_->{cpqHeEventLogInitialTime} = timelocal(0, $min, $hour, $day, $month - 1, $year);
        };
        if ($@) {
          $_->{cpqHeEventLogInitialTime} = 0;
        }
      }
    } elsif ($_->{cpqHeEventLogInitialTime} =~ /^\0\0\0\0\0\0/) {
      $_->{cpqHeEventLogInitialTime} = 0;
    }
    if ($_->{cpqHeEventLogUpdateTime} =~ /^(([0-9a-fA-F]{2})( [0-9a-fA-F]{2})*)\s*$/) {
      $_->{cpqHeEventLogUpdateTime} =~ s/ //;
      my  ($year, $month, $day, $hour, $min) = map { hex($_) } split(/\s+/, $_->{cpqHeEventLogUpdateTime});
      if ($year == 0) {
        $_->{cpqHeEventLogUpdateTime} = 0;
      } else {
        eval {
          $_->{cpqHeEventLogUpdateTime} = timelocal(0, $min, $hour, $day, $month - 1, $year);
        };
        if ($@) {
          $_->{cpqHeEventLogUpdateTime} = 0;
        }
      }
    } elsif ($_->{cpqHeEventLogUpdateTime} =~ /^0x([0-9a-fA-F]{4})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})/) {
      my  ($year, $month, $day, $hour, $min) = map { hex($_) } ($1, $2, $3, $4, $5);
      if ($year == 0) {
        $_->{cpqHeEventLogUpdateTime} = 0;
      } else {
        eval {
          $_->{cpqHeEventLogUpdateTime} = timelocal(0, $min, $hour, $day, $month - 1, $year);
        };
        if ($@) {
          $_->{cpqHeEventLogUpdateTime} = 0;
        }
      }
    } elsif ($_->{cpqHeEventLogUpdateTime} =~ /^\0\0\0\0\0\0/) {
      $_->{cpqHeEventLogUpdateTime} = 0;
    }
    if ($_->{cpqHeEventLogErrorDesc} =~ /^(([0-9a-fA-F]{2})(\s+[0-9a-fA-F]{2})*)\s*$/) {
      $_->{cpqHeEventLogErrorDesc} = join "", map { chr($_) } map { if (hex($_) > 127) { 20; } else { hex($_) } } split(/\s+/, $_->{cpqHeEventLogErrorDesc});
    }
    push(@{$self->{events}},
        HP::Proliant::Component::EventSubsystem::Event->new(%{$_}));
  }
}

sub overall_check {
  my $self = shift;
  my $result = 0;
  $self->blacklist('oe', '');
  if ($self->{eventsupp} && $self->{eventsupp} eq "supported") {
    if ($self->{eventstatus} eq "ok") {
      $result = 0;
      $self->add_info('eventlog system is ok');
    } else {
      $result = 0;
      $self->add_info(sprintf "eventlog system is %s", $self->{eventstatus});
    }
  } else {
    $result = 0;
    $self->add_info('no event status found');
  }
}


package HP::Proliant::Component::PowersupplySubsystem;
our @ISA = qw(HP::Proliant::Component);

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

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    runtime => $params{runtime},
    rawdata => $params{rawdata},
    method => $params{method},
    condition => $params{condition},
    status => $params{status},
    powersupplies => [],
    powerconverters => [],
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  bless $self, $class;
  if ($self->{method} eq 'snmp') {
    return HP::Proliant::Component::PowersupplySubsystem::SNMP->new(%params);
  } elsif ($self->{method} eq 'cli') {
    return HP::Proliant::Component::PowersupplySubsystem::CLI->new(%params);
  } else {
    die "unknown method";
  }
  return $self;
}

sub check {
  my $self = shift;
  my $errorfound = 0;
  $self->add_info('checking power supplies');
  if (scalar (@{$self->{powersupplies}}) == 0) {
    #$self->overall_check();
  } else {
    foreach (@{$self->{powersupplies}}) {
      $_->check();
    }
  }
}

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


package HP::Proliant::Component::PowersupplySubsystem::Powersupply;
our @ISA = qw(HP::Proliant::Component::PowersupplySubsystem);

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

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    runtime => $params{runtime},
    cpqHeFltTolPowerSupplyChassis => $params{cpqHeFltTolPowerSupplyChassis},
    cpqHeFltTolPowerSupplyBay => $params{cpqHeFltTolPowerSupplyBay},
    cpqHeFltTolPowerSupplyPresent => $params{cpqHeFltTolPowerSupplyPresent},
    cpqHeFltTolPowerSupplyCondition => $params{cpqHeFltTolPowerSupplyCondition},
    cpqHeFltTolPowerSupplyRedundant => $params{cpqHeFltTolPowerSupplyRedundant},
    cpqHeFltTolPowerSupplyCapacityUsed => $params{cpqHeFltTolPowerSupplyCapacityUsed} || 0,
    cpqHeFltTolPowerSupplyCapacityMaximum => $params{cpqHeFltTolPowerSupplyCapacityMaximum} || 0,
    blacklisted => 0,
    info => undef,
    extendexinfo => undef,
  };
  bless $self, $class;
  return $self;
}

sub check {
  my $self = shift;
  $self->blacklist('p', $self->{cpqHeFltTolPowerSupplyBay});
  if ($self->{cpqHeFltTolPowerSupplyPresent} eq "present") {
    if ($self->{cpqHeFltTolPowerSupplyCondition} ne "ok") {
      if ($self->{cpqHeFltTolPowerSupplyCondition} eq "other") {
        $self->add_info(sprintf "powersupply %d is missing",
            $self->{cpqHeFltTolPowerSupplyBay});
      } else {
        $self->add_info(sprintf "powersupply %d needs attention (%s)",
            $self->{cpqHeFltTolPowerSupplyBay},
            $self->{cpqHeFltTolPowerSupplyCondition});
      }
      $self->add_message(CRITICAL, $self->{info});
    } else {
      $self->add_info(sprintf "powersupply %d is %s",
          $self->{cpqHeFltTolPowerSupplyBay},
          $self->{cpqHeFltTolPowerSupplyCondition});
    }
    $self->add_extendedinfo(sprintf "ps_%s=%s",
        $self->{cpqHeFltTolPowerSupplyBay},
        $self->{cpqHeFltTolPowerSupplyCondition});
    if ($self->{cpqHeFltTolPowerSupplyCapacityUsed} &&
        $self->{cpqHeFltTolPowerSupplyCapacityMaximum}) {
      if ($self->{runtime}->{options}->{perfdata}) {
        $self->{runtime}->{plugin}->add_perfdata(
            label => sprintf("pc_%s", $self->{cpqHeFltTolPowerSupplyBay}),
            value => $self->{cpqHeFltTolPowerSupplyCapacityUsed},
            warning => $self->{cpqHeFltTolPowerSupplyCapacityMaximum},
            critical => $self->{cpqHeFltTolPowerSupplyCapacityMaximum}
        );
      }
    } elsif ($self->{cpqHeFltTolPowerSupplyCapacityUsed}) {
      if ($self->{runtime}->{options}->{perfdata}) {
        $self->{runtime}->{plugin}->add_perfdata(
            label => sprintf("pc_%s", $self->{cpqHeFltTolPowerSupplyBay}),
            value => $self->{cpqHeFltTolPowerSupplyCapacityUsed}
        );
      }
    }
  } else {
    $self->add_info(sprintf "powersupply %d is %s",
        $self->{cpqHeFltTolPowerSupplyBay},
        $self->{cpqHeFltTolPowerSupplyPresent});
    $self->add_extendedinfo(sprintf "ps_%s=%s",
        $self->{cpqHeFltTolPowerSupplyBay},
        $self->{cpqHeFltTolPowerSupplyPresent});
  }
}


sub dump {
  my $self = shift;
  printf "[PS_%s]\n", $self->{cpqHeFltTolPowerSupplyBay};
  foreach (qw(cpqHeFltTolPowerSupplyBay cpqHeFltTolPowerSupplyChassis
      cpqHeFltTolPowerSupplyPresent cpqHeFltTolPowerSupplyCondition
      cpqHeFltTolPowerSupplyRedundant cpqHeFltTolPowerSupplyCapacityUsed
      cpqHeFltTolPowerSupplyCapacityMaximum)) {
    printf "%s: %s\n", $_, $self->{$_};
  }
  printf "info: %s\n\n", $self->{info};
}


package HP::Proliant::Component::PowersupplySubsystem::Powerconverter;
our @ISA = qw(HP::Proliant::Component::PowersupplySubsystem);

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

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    runtime => $params{runtime},

    cpqHePowerConvEntry => $params{cpqHePowerConvEntry},
    cpqHePowerConvChassis => $params{cpqHePowerConvChassis},
    cpqHePowerConvIndex => $params{cpqHePowerConvIndex},
    cpqHePowerConvPresent => $params{cpqHePowerConvPresent},
    cpqHePowerConvRedundant => $params{cpqHePowerConvRedundant},
    cpqHePowerConvCondition => $params{cpqHePowerConvCondition},
    cpqHePowerConvHwLocation => $params{cpqHePowerConvHwLocation},
    blacklisted => 0,
    info => undef,
    extendexinfo => undef,
  };
  bless $self, $class;
  return $self;
}

sub check {
  my $self = shift;
  $self->blacklist('pc', $self->{cpqHePowerConvIndex});
  if ($self->{cpqHePowerConvPresent} eq "present") {
    if ($self->{cpqHePowerConvCondition} ne "ok") {
      if ($self->{cpqHePowerConvCondition} eq "other") {
        $self->add_info(sprintf "powerconverter %d is missing",
            $self->{cpqHePowerConvIndex});
      } else {
        $self->add_info(sprintf "powerconverter %d needs attention (%s)",
            $self->{cpqHePowerConvIndex},
            $self->{cpqHePowerConvCondition});
      }
      $self->add_message(CRITICAL, $self->{info});
    } else {
      $self->add_info(sprintf "powerconverter %d is %s",
          $self->{cpqHePowerConvIndex},
          $self->{cpqHePowerConvCondition});
    }
    $self->add_extendedinfo(sprintf "pc_%s=%s",
        $self->{cpqHePowerConvIndex},
        $self->{cpqHePowerConvCondition});
  } else {
    $self->add_info(sprintf "powerconverter %d is %s",
        $self->{cpqHePowerConvIndex},
        $self->{cpqHePowerConvPresent});
    $self->add_extendedinfo(sprintf "pc_%s=%s",
        $self->{cpqHePowerConvIndex},
        $self->{cpqHePowerConvPresent});
  }
}


sub dump {
  my $self = shift;
  printf "[PS_%s]\n", ($self->{cpqHePowerConvChassis} ? $self->{cpqHePowerConvChassis}.":" : "").$self->{cpqHePowerConvIndex};
  foreach (qw(cpqHePowerConvIndex cpqHePowerConvPresent cpqHePowerConvRedundant cpqHePowerConvCondition)) {
    printf "%s: %s\n", $_, $self->{$_};
  }
  printf "info: %s\n\n", $self->{info};
}



package HP::Proliant::Component::PowersupplySubsystem::CLI;
our @ISA = qw(HP::Proliant::Component::PowersupplySubsystem);

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

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

sub init {
  my $self = shift;
  my %params = @_;
  my %tmpps = (
    runtime => $self->{runtime},
    cpqHeFltTolPowerSupplyChassis => 1,
  );
  my $inblock = 0;
  foreach (grep(/^powersupply/, split(/\n/, $self->{rawdata}))) {
    s/^powersupply\s*//g;
    if (/^Power supply #(\d+)/) {
      if ($inblock) {
        $inblock = 0;
        push(@{$self->{powersupplies}},
            HP::Proliant::Component::PowersupplySubsystem::Powersupply->new(%tmpps));
        %tmpps = (
          runtime => $self->{runtime},
          cpqHeFltTolPowerSupplyChassis => 1,
        );
      }
      $tmpps{cpqHeFltTolPowerSupplyBay} = $1;
      $inblock = 1;
    } elsif (/\s*Present\s+:\s+(\w+)/) {
      $tmpps{cpqHeFltTolPowerSupplyPresent} = lc $1 eq 'yes' ? 'present' :
          lc $1 eq 'no' ? 'absent': 'other';
    } elsif (/\s*Redundant\s*:\s+(\w+)/) {
      $tmpps{cpqHeFltTolPowerSupplyRedundant} = lc $1 eq 'yes' ? 'redundant' :
          lc $1 eq 'no' ? 'notRedundant' : 'other';
    } elsif (/\s*Condition\s*:\s+(\w+)/) {
      $tmpps{cpqHeFltTolPowerSupplyCondition} = lc $1;
    } elsif (/\s*Power\s*:\s+(\d+)/) {
      $tmpps{cpqHeFltTolPowerSupplyCapacityUsed} = $1;
    } elsif (/\s*Power Supply not present/) {
      $tmpps{cpqHeFltTolPowerSupplyPresent} = "absent";
      $tmpps{cpqHeFltTolPowerSupplyCondition} = "other";
      $tmpps{cpqHeFltTolPowerSupplyRedundant} = "notRedundant";
    } elsif (/^\s*$/) {
      if ($inblock) {
        $inblock = 0;
        push(@{$self->{powersupplies}},
            HP::Proliant::Component::PowersupplySubsystem::Powersupply->new(%tmpps));
        %tmpps = (
          runtime => $self->{runtime},
          cpqHeFltTolPowerSupplyChassis => 1,
        );
      }
    }
  }
  if ($inblock) {
    push(@{$self->{powersupplies}},
        HP::Proliant::Component::PowersupplySubsystem::Powersupply->new(%tmpps));
    %tmpps = (
      runtime => $params{runtime},
    );
  }
}


package HP::Proliant::Component::PowersupplySubsystem::SNMP;
our @ISA = qw(HP::Proliant::Component::PowersupplySubsystem
    HP::Proliant::Component::SNMP);

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

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

sub init {
  my $self = shift;
  my %params = @_;
  my $snmpwalk = $self->{rawdata};
  my $oids = {
      cpqHeFltTolPowerSupplyEntry => "1.3.6.1.4.1.232.6.2.9.3.1",
      cpqHeFltTolPowerSupplyChassis => "1.3.6.1.4.1.232.6.2.9.3.1.1",
      cpqHeFltTolPowerSupplyBay => "1.3.6.1.4.1.232.6.2.9.3.1.2",
      cpqHeFltTolPowerSupplyPresent => "1.3.6.1.4.1.232.6.2.9.3.1.3",
      cpqHeFltTolPowerSupplyCondition => "1.3.6.1.4.1.232.6.2.9.3.1.4",
      cpqHeFltTolPowerSupplyRedundant => "1.3.6.1.4.1.232.6.2.9.3.1.9",
      cpqHeFltTolPowerSupplyPresentValue => {
          1 => "other",
          2 => "absent",
          3 => "present",
      },
      cpqHeFltTolPowerSupplyConditionValue => {
          1 => "other",
          2 => "ok",
          3 => "degraded",
          4 => "failed",
      },
      cpqHeFltTolPowerSupplyCapacityUsed => '1.3.6.1.4.1.232.6.2.9.3.1.7',
      cpqHeFltTolPowerSupplyCapacityMaximum => '1.3.6.1.4.1.232.6.2.9.3.1.8',
      cpqHeFltTolPowerSupplyRedundantValue => {
          1 => "other",
          2 => "notRedundant",
          3 => "redundant",
      },
  };

  # INDEX { cpqHeFltTolPowerSupplyChassis, cpqHeFltTolPowerSupplyBay }
  foreach ($self->get_entries($oids, 'cpqHeFltTolPowerSupplyEntry')) {
    push(@{$self->{powersupplies}},
        HP::Proliant::Component::PowersupplySubsystem::Powersupply->new(%{$_}));
  }

  $oids = {
      cpqHePowerConvEntry => "1.3.6.1.4.1.232.6.2.13.3.1",
      cpqHePowerConvChassis => "1.3.6.1.4.1.232.6.2.13.3.1.1",
      cpqHePowerConvIndex => "1.3.6.1.4.1.232.6.2.13.3.1.2",
      cpqHePowerConvPresent => "1.3.6.1.4.1.232.6.2.13.3.1.3",
      cpqHePowerConvRedundant => "1.3.6.1.4.1.232.6.2.13.3.1.6",
      cpqHePowerConvCondition => "1.3.6.1.4.1.232.6.2.13.3.1.8",
      cpqHePowerConvPresentValue => {
          1 => "other",
          2 => "absent",
          3 => "present",
      },
      cpqHePowerConvRedundantValue => {
          1 => "other",
          2 => "notRedundant",
          3 => "redundant",
      },
      cpqHePowerConvConditionValue => {
          1 => "other",
          2 => "ok",
          3 => "degraded",
          4 => "failed",
      },
      cpqHePowerConvHwLocation => "1.3.6.1.4.1.232.6.2.13.3.1.9",
  };

  # INDEX { cpqHePowerConvChassis cpqHePowerConvIndex }
  foreach ($self->get_entries($oids, 'cpqHePowerConvEntry')) {
    push(@{$self->{powerconverters}},
        HP::Proliant::Component::PowersupplySubsystem::Powerconverter->new(%{$_}));
  }
  # keine ahnung, was man damit machen kann

}

package HP::Proliant::Component::TemperatureSubsystem;
our @ISA = qw(HP::Proliant::Component);

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

sub new {
  my $class = shift;
################################## custom_thresholds
  my %params = @_;
  my $self = {
    runtime => $params{runtime},
    rawdata => $params{rawdata},
    method => $params{method},
    condition => $params{condition},
    status => $params{status},
    temperatures => [],
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  bless $self, $class;
  if ($params{runtime}->{options}->{customthresholds}) {
    if (-f $params{runtime}->{options}->{customthresholds}) {
      $params{runtime}->{options}->{customthresholds} = 
          do { local (@ARGV, $/) = 
              $params{runtime}->{options}->{customthresholds}; <> };
    }
    foreach my $ct_items
        (split(/\//, $params{runtime}->{options}->{customthresholds})) {
      if ($ct_items =~ /^(\d+):(\d+)$/) {
        $params{runtime}->{options}->{thresholds}->{$1} = $2;
      } else {
        die sprintf "invalid threshold %s", $ct_items;
      }
    }
  }
  if ($self->{method} eq 'snmp') {
    return HP::Proliant::Component::TemperatureSubsystem::SNMP->new(%params);
  } elsif ($self->{method} eq 'cli') {
    return HP::Proliant::Component::TemperatureSubsystem::CLI->new(%params);
  } else {
    die "unknown method";
  }
  return $self;
}

sub check {
  my $self = shift;
  my $errorfound = 0;
  $self->add_info('checking temperatures');
  if (scalar (@{$self->{temperatures}}) == 0) {
    #$self->overall_check(); 
    $self->add_info('no temperatures found');
  } else {
    foreach (sort { $a->{cpqHeTemperatureIndex} <=> $b->{cpqHeTemperatureIndex}}
        @{$self->{temperatures}}) {
      $_->check();
    }
  }
}

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


package HP::Proliant::Component::TemperatureSubsystem::Temperature;
our @ISA = qw(HP::Proliant::Component::TemperatureSubsystem);

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

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    runtime => $params{runtime},
    cpqHeTemperatureChassis => $params{cpqHeTemperatureChassis},
    cpqHeTemperatureIndex => $params{cpqHeTemperatureIndex},
    cpqHeTemperatureLocale => $params{cpqHeTemperatureLocale},
    cpqHeTemperatureCelsius => $params{cpqHeTemperatureCelsius},
    cpqHeTemperatureThresholdCelsius => $params{cpqHeTemperatureThresholdCelsius},
    cpqHeTemperatureCondition => $params{cpqHeTemperatureCondition},
    cpqHeTemperatureThresholdType => $params{cpqHeTemperatureThresholdType} || "unknown",
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  bless $self, $class;
  if ($params{runtime}->{options}->{celsius}) {
    $self->{cpqHeTemperatureUnits} = 'C';
    $self->{cpqHeTemperature} = $self->{cpqHeTemperatureCelsius};
    $self->{cpqHeTemperatureThreshold} = 
        $self->{cpqHeTemperatureThresholdCelsius};
  } else {
    $self->{cpqHeTemperatureUnits} = 'F';
    $self->{cpqHeTemperature} = 
        (($self->{cpqHeTemperatureCelsius} * 9) / 5) + 32;
    $self->{cpqHeTemperatureThreshold} = 
        (($self->{cpqHeTemperatureThresholdCelsius} * 9) / 5) + 32;
  }
  my $index = $self->{cpqHeTemperatureIndex};
  if (exists $params{runtime}->{options}->{thresholds}->{$index}) {
    $self->{cpqHeTemperatureThreshold} = 
        $params{runtime}->{options}->{thresholds}->{$index};
        
  }
  if ($self->{cpqHeTemperatureThresholdCelsius} == -99) {
    bless $self, 'HP::Proliant::Component::TemperatureSubsystem::SoSTemperature';
  } elsif ($self->{cpqHeTemperatureThresholdCelsius} == 0) {
    # taucht auf, seit man gen8 ueber das ilo abfragen kann
    bless $self, 'HP::Proliant::Component::TemperatureSubsystem::SoSTemperature';
  }
  return $self;
}

sub check {
  my $self = shift;
  $self->blacklist('t', $self->{cpqHeTemperatureIndex});
  if ($self->{cpqHeTemperature} > $self->{cpqHeTemperatureThreshold}) {
    $self->add_info(sprintf "%d %s temperature too high (%d%s, %d max)",
        $self->{cpqHeTemperatureIndex}, $self->{cpqHeTemperatureLocale},
        $self->{cpqHeTemperature}, $self->{cpqHeTemperatureUnits},
        $self->{cpqHeTemperatureThreshold});
    $self->add_message(CRITICAL, $self->{info});
  } elsif ($self->{cpqHeTemperature} < 0) {
    # #21 SCSI_BACKPLANE_ZONE -1C/31F 60C/140F OK - can't be true
    $self->add_info(sprintf "%d %s temperature too low (%d%s)",
        $self->{cpqHeTemperatureIndex}, $self->{cpqHeTemperatureLocale},
        $self->{cpqHeTemperature}, $self->{cpqHeTemperatureUnits});
    $self->add_message(CRITICAL, $self->{info});
  } else {
    $self->add_info(sprintf "%d %s temperature is %d%s (%d max)",
        $self->{cpqHeTemperatureIndex}, $self->{cpqHeTemperatureLocale}, 
        $self->{cpqHeTemperature}, $self->{cpqHeTemperatureUnits},
        $self->{cpqHeTemperatureThreshold});
  }
  if ($self->{runtime}->{options}->{perfdata} == 2) {
    $self->{runtime}->{plugin}->add_perfdata(
        label => sprintf('temp_%s', $self->{cpqHeTemperatureIndex}),
        value => $self->{cpqHeTemperature},
        warning => $self->{cpqHeTemperatureThreshold},
        critical => $self->{cpqHeTemperatureThreshold}
    );
  } elsif ($self->{runtime}->{options}->{perfdata} == 1) {
    $self->{runtime}->{plugin}->add_perfdata(
        label => sprintf('temp_%s_%s', $self->{cpqHeTemperatureIndex},
            $self->{cpqHeTemperatureLocale}),
        value => $self->{cpqHeTemperature},
        warning => $self->{cpqHeTemperatureThreshold},
        critical => $self->{cpqHeTemperatureThreshold}
    );
  } 
  $self->add_extendedinfo(sprintf "temp_%s=%d",
      $self->{cpqHeTemperatureIndex},
      $self->{cpqHeTemperature});
}

sub dump { 
  my $self = shift;
  printf "[TEMP_%s]\n", $self->{cpqHeTemperatureIndex};
  foreach (qw(cpqHeTemperatureChassis cpqHeTemperatureIndex 
      cpqHeTemperatureLocale cpqHeTemperatureCelsius cpqHeTemperatureThreshold
      cpqHeTemperatureThresholdType cpqHeTemperatureCondition)) {
    printf "%s: %s\n", $_, $self->{$_};
  }
  printf "info: %s\n\n", $self->{info};
}


package HP::Proliant::Component::TemperatureSubsystem::SoSTemperature;
our @ISA = qw(HP::Proliant::Component::TemperatureSubsystem::Temperature);

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

sub check {
  my $self = shift;
  $self->blacklist('t', $self->{cpqHeTemperatureIndex});
  $self->add_info(sprintf "%d %s temperature is %d%s (no thresh.)",
      $self->{cpqHeTemperatureIndex}, $self->{cpqHeTemperatureLocale}, 
      $self->{cpqHeTemperature}, $self->{cpqHeTemperatureUnits});
  if ($self->{runtime}->{options}->{perfdata} == 2) {
    $self->{runtime}->{plugin}->add_perfdata(
        label => sprintf('temp_%s', $self->{cpqHeTemperatureIndex}),
        value => $self->{cpqHeTemperature},
    );
  } elsif ($self->{runtime}->{options}->{perfdata} == 1) {
    $self->{runtime}->{plugin}->add_perfdata(
        label => sprintf('temp_%s_%s', $self->{cpqHeTemperatureIndex},
            $self->{cpqHeTemperatureLocale}),
        value => $self->{cpqHeTemperature},
    );
  } 
  $self->add_extendedinfo(sprintf "temp_%s=%d",
      $self->{cpqHeTemperatureIndex},
      $self->{cpqHeTemperature});
}



package HP::Proliant::Component::TemperatureSubsystem::CLI;
our @ISA = qw(HP::Proliant::Component::TemperatureSubsystem);

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

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

sub init {
  my $self = shift;
  my %params = @_;
  my $tempcnt = 1;
  foreach (grep(/^temp/, split(/\n/, $params{rawdata}))) {
    s/^temp\s*//g;
    if (/^#(\d+)\s+([\w_\/\-#]+)\s+(-*\d+)C\/(\d+)F\s+(\d+)C\/(\d+)F/) {
      my %params = ();
      $params{runtime} = $self->{runtime};
      $params{cpqHeTemperatureChassis} = 1;
      $params{cpqHeTemperatureIndex} = $1;
      $params{cpqHeTemperatureLocale} = lc $2;
      $params{cpqHeTemperatureCelsius} = $3;
      $params{cpqHeTemperatureThresholdCelsius} = $5;
      $params{cpqHeTemperatureCondition} = 'unknown';
      push(@{$self->{temperatures}},
          HP::Proliant::Component::TemperatureSubsystem::Temperature->new(
              %params));
    } elsif (/^#(\d+)\s+([\w_\/\-#]+)\s+\-\s+(\d+)C\/(\d+)F/) {
      # #3        CPU#2                -       0C/0F
      $self->trace(2, sprintf "skipping temperature %s", $_);
    } elsif (/^#(\d+)\s+([\w_\/\-#]+)\s+(\d+)C\/(\d+)F\s+\-/) {
      # #3        CPU#2                0C/0F       -
      $self->trace(2, sprintf "skipping temperature %s", $_);
    } elsif (/^#(\d+)\s+([\w_\/\-#]+)\s+\-\s+\-/) {
      # #3        CPU#2                -       -
      $self->trace(2, sprintf "skipping temperature %s", $_);
    } elsif (/^#(\d+)/) {
      $self->trace(0, sprintf "send this to lausser: %s", $_);
    }
  }
}


package HP::Proliant::Component::TemperatureSubsystem::SNMP;
our @ISA = qw(HP::Proliant::Component::TemperatureSubsystem
    HP::Proliant::Component::SNMP);

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

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

sub overall_init {
  my $self = shift;
  my %params = @_;
  my $snmpwalk = $params{rawdata};
  # overall
  my $cpqHeThermalTempStatus  = '1.3.6.1.4.1.232.6.2.6.3.0';
  my $cpqHeThermalTempStatusValue = {
    1 => 'other',
    2 => 'ok',
    3 => 'degraded',
    4 => 'failed',
  };
  $self->{tempstatus} = lc SNMP::Utils::get_object_value(
      $snmpwalk, $cpqHeThermalTempStatus,
      $cpqHeThermalTempStatusValue);
  $self->{tempstatus} |= lc $self->{tempstatus};
}

sub init {
  my $self = shift;
  my %params = @_;
  my $snmpwalk = $self->{rawdata};
  my $oids = {
      cpqHeTemperatureEntry => "1.3.6.1.4.1.232.6.2.6.8.1",
      cpqHeTemperatureChassis => "1.3.6.1.4.1.232.6.2.6.8.1.1",
      cpqHeTemperatureIndex => "1.3.6.1.4.1.232.6.2.6.8.1.2",
      cpqHeTemperatureLocale => "1.3.6.1.4.1.232.6.2.6.8.1.3",
      cpqHeTemperatureCelsius => "1.3.6.1.4.1.232.6.2.6.8.1.4",
      cpqHeTemperatureThresholdCelsius => "1.3.6.1.4.1.232.6.2.6.8.1.5",
      cpqHeTemperatureCondition => "1.3.6.1.4.1.232.6.2.6.8.1.6",
      cpqHeTemperatureThresholdType => "1.3.6.1.4.1.232.6.2.6.8.1.7",
      cpqHeTemperatureLocaleValue => {
          1 => "other",
          2 => "unknown",
          3 => "system",
          4 => "systemBoard",
          5 => "ioBoard",
          6 => "cpu",
          7 => "memory",
          8 => "storage",
          9 => "removableMedia",
          10 => "powerSupply",
          11 => "ambient",
          12 => "chassis",
          13 => "bridgeCard",
      },
      cpqHeTemperatureConditionValue => {
          1 => 'other',
          2 => 'ok',
          3 => 'degraded',
          4 => 'failed',
      },
      cpqHeTemperatureThresholdTypeValue => {
          1 => 'other',
          5 => 'blowout',
          9 => 'caution',
          15 => 'critical',
      },
  };
  # INDEX { cpqHeTemperatureChassis, cpqHeTemperatureIndex }
  foreach ($self->get_entries($oids, 'cpqHeTemperatureEntry')) {
    # sieht aus, als wurden die gar nicht existieren.
    # im ilo4 werden sie als n/a angezeigt
    next if $_->{cpqHeTemperatureThresholdType} eq "caution" && $_->{cpqHeTemperatureThresholdCelsius} == 0;
    push(@{$self->{temperatures}},
        HP::Proliant::Component::TemperatureSubsystem::Temperature->new(%{$_}));
  }
}

sub overall_check {
  my $self = shift;
  my $result = 0;
  $self->blacklist('ots', '');
  if ($self->{tempstatus}) {
    if ($self->{tempstatus} eq "ok") {
      $result = 0;
      $self->add_info('all temp sensors are within normal operating range');
    } elsif ($self->{tempstatus} eq "degraded") {
      $result = 1;
      $self->add_info('a temp sensor is outside of normal operating range');
    } elsif ($self->{tempstatus} eq "failed") {
      $result = 2;
      $self->add_info('a temp sensor detects a condition that could permanently
damage the system');
    } elsif ($self->{tempstatus} eq "other") {
      $result = 0;
      $self->add_info('temp sensing is not supported by this system or driver');
    }
  } else {
    $result = 0;
    $self->add_info('no global temp status found');
  }
}


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

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

sub new {
  my $class = shift;
################################## scrapiron ##########
  my %params = @_;
  my $self = {
    runtime => $params{runtime},
    rawdata => $params{rawdata},
    method => $params{method},
    condition => $params{condition},
    status => $params{status},
    cpus => [],
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  bless $self, $class;
  if ($self->{method} eq 'snmp') {
    return HP::Proliant::Component::CpuSubsystem::SNMP->new(%params);
  } elsif ($self->{method} eq 'cli') {
    return HP::Proliant::Component::CpuSubsystem::CLI->new(%params);
  } else {
    die "unknown method";
  }
  return $self;
}

sub check {
  my $self = shift;
  my $errorfound = 0;
  $self->add_info('checking cpus');
  if (scalar (@{$self->{cpus}}) == 0) {
    # sachen gibts.....
  #  $self->overall_check(); # sowas ist mir nur einmal untergekommen
  } else {
    foreach (@{$self->{cpus}}) {
      $_->check();
    }
  }
}

sub num_cpus {
  my $self = shift;
  return scalar @{$self->{cpus}};
}

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


package HP::Proliant::Component::CpuSubsystem::Cpu;
our @ISA = qw(HP::Proliant::Component::CpuSubsystem);

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

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    runtime => $params{runtime},
    cpqSeCpuSlot => $params{cpqSeCpuSlot},
    cpqSeCpuUnitIndex => $params{cpqSeCpuUnitIndex},
    cpqSeCpuName => $params{cpqSeCpuName},
    cpqSeCpuStatus => $params{cpqSeCpuStatus},
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  bless $self, $class;
  return $self;
}

sub check {
  my $self = shift;
  $self->blacklist('c', $self->{cpqSeCpuUnitIndex});
  if ($self->{cpqSeCpuStatus} ne "ok") {
    if ($self->{runtime}->{options}{scrapiron} &&
        ($self->{cpqSeCpuStatus} eq "unknown")) {
      $self->add_info(sprintf "cpu %d probably ok (%s)",
          $self->{cpqSeCpuUnitIndex}, $self->{cpqSeCpuStatus});
    } else {
      $self->add_info(sprintf "cpu %d needs attention (%s)",
          $self->{cpqSeCpuUnitIndex}, $self->{cpqSeCpuStatus});
      $self->add_message(CRITICAL, $self->{info});
    }
  } else {
    $self->add_info(sprintf "cpu %d is %s", 
        $self->{cpqSeCpuUnitIndex}, $self->{cpqSeCpuStatus});
  }
  $self->add_extendedinfo(sprintf "cpu_%s=%s",
      $self->{cpqSeCpuUnitIndex}, $self->{cpqSeCpuStatus});
}

sub dump {
  my $self = shift;
  printf "[CPU_%s]\n", $self->{cpqSeCpuUnitIndex};
  foreach (qw(cpqSeCpuSlot cpqSeCpuUnitIndex cpqSeCpuName cpqSeCpuStatus)) {
    printf "%s: %s\n", $_, $self->{$_};
  }
  printf "info: %s\n", $self->{info};
  printf "\n";
}
package HP::Proliant::Component::CpuSubsystem::CLI;
our @ISA = qw(HP::Proliant::Component::CpuSubsystem);

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

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

sub init {
  my $self = shift;
  my %params = @_;
  my %tmpcpu = (
    runtime => $params{runtime},
  );
  my $inblock = 0;
  foreach (grep(/^server/, split(/\n/, $self->{rawdata}))) {
    if (/Processor:\s+(\d+)/) {
      $tmpcpu{cpqSeCpuUnitIndex} = $1;
      $inblock = 1;
    } elsif (/Name\s*:\s+(.+?)\s*$/) {
      $tmpcpu{cpqSeCpuName} = $1;
    } elsif (/Status\s*:\s+(.+?)\s*$/) {
      $tmpcpu{cpqSeCpuStatus} = lc $1;
    } elsif (/Socket\s*:\s+(.+?)\s*$/) {
      $tmpcpu{cpqSeCpuSlot} = $1;
    } elsif (/^server\s*$/) {
      if ($inblock) {
        $inblock = 0;
        push(@{$self->{cpus}},
            HP::Proliant::Component::CpuSubsystem::Cpu->new(%tmpcpu));
        %tmpcpu = (
          runtime => $params{runtime},
        );
      }
    }
  }
  if ($inblock) {
    push(@{$self->{cpus}},
        HP::Proliant::Component::CpuSubsystem::Cpu->new(%tmpcpu));
  }
}


package HP::Proliant::Component::CpuSubsystem::SNMP;
our @ISA = qw(HP::Proliant::Component::CpuSubsystem
    HP::Proliant::Component::SNMP);

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

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

sub init {
  my $self = shift;
  my $snmpwalk = $self->{rawdata};
  # CPQSTDEQ-MIB
  my $oids = {
      cpqSeCpuEntry => '1.3.6.1.4.1.232.1.2.2.1.1',
      cpqSeCpuUnitIndex => '1.3.6.1.4.1.232.1.2.2.1.1.1',
      cpqSeCpuSlot => '1.3.6.1.4.1.232.1.2.2.1.1.2',
      cpqSeCpuName => '1.3.6.1.4.1.232.1.2.2.1.1.3',
      cpqSeCpuStatus => '1.3.6.1.4.1.232.1.2.2.1.1.6',
      cpqSeCpuStatusValue => {
          1 => "unknown",
          2 => "ok",
          3 => "degraded",
          4 => "failed",
          5 => "disabled",
      },
  };

  # INDEX { cpqSeCpuUnitIndex }
  foreach ($self->get_entries($oids, 'cpqSeCpuEntry')) {
    push(@{$self->{cpus}},
        HP::Proliant::Component::CpuSubsystem::Cpu->new(%{$_}));
  }
}


package HP::Proliant::Component::FanSubsystem;
our @ISA = qw(HP::Proliant::Component);

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

sub new {
  my $class = shift;
################################## fan_redundancy ##########
  my %params = @_;
  my $self = {
    runtime => $params{runtime},
    rawdata => $params{rawdata},
    method => $params{method},
    condition => $params{condition},
    status => $params{status},
    fans => [],
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  bless $self, $class;
  if ($self->{method} eq 'snmp') {
    return HP::Proliant::Component::FanSubsystem::SNMP->new(%params);
  } elsif ($self->{method} eq 'cli') {
    return HP::Proliant::Component::FanSubsystem::CLI->new(%params);
  } else {
    die 'unknown method';
  }
  return $self;
}

sub check {
  my $self = shift;
  my $errorfound = 0;
  $self->add_info('checking fans');
  $self->blacklist('ff', '');
  if (scalar (@{$self->{fans}}) == 0) {
    $self->overall_check(); # sowas ist mir nur einmal untergekommen
    # die maschine hatte alles in allem nur 2 oids (cpqHeFltTolFanChassis)
    # SNMPv2-SMI::enterprises.232.6.2.6.7.1.1.0.1 = INTEGER: 0
    # SNMPv2-SMI::enterprises.232.6.2.6.7.1.1.0.2 = INTEGER: 0
  } else {
    my $overallhealth = $self->overall_check(); 
    foreach (@{$self->{fans}}) {
      $_->{overallhealth} = $overallhealth;
      $_->check();
    }
  }
}

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

sub get_fan_by_index {
  my $self = shift;
  my $index;
  foreach (@{$self->{fans}}) {
    return $_ if exists $_->{cpqHeFltTolFanIndex} && 
        $_->{cpqHeFltTolFanIndex} == $index;
  }
  return undef;
}


package HP::Proliant::Component::FanSubsystem::Fan;
our @ISA = qw(HP::Proliant::Component::FanSubsystem);

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

sub new {
  my $class = shift;
  my %params = @_;
  if (exists $params{cpqHeFltTolFanRedundant}) {
    return HP::Proliant::Component::FanSubsystem::Fan::FTol->new(%params);
  } else {
    return HP::Proliant::Component::FanSubsystem::Fan::Thermal->new(%params);
  }
}


package HP::Proliant::Component::FanSubsystem::Fan::FTol;
our @ISA = qw(HP::Proliant::Component::FanSubsystem::Fan);


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

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    runtime => $params{runtime},
    cpqHeFltTolFanChassis => $params{cpqHeFltTolFanChassis},
    cpqHeFltTolFanIndex => $params{cpqHeFltTolFanIndex},
    cpqHeFltTolFanLocale => $params{cpqHeFltTolFanLocale},
    cpqHeFltTolFanPresent => $params{cpqHeFltTolFanPresent},
    cpqHeFltTolFanType => $params{cpqHeFltTolFanType},
    cpqHeFltTolFanSpeed => $params{cpqHeFltTolFanSpeed},
    cpqHeFltTolFanRedundant => $params{cpqHeFltTolFanRedundant},
    cpqHeFltTolFanRedundantPartner => $params{cpqHeFltTolFanRedundantPartner},
    cpqHeFltTolFanCondition => $params{cpqHeFltTolFanCondition},
    cpqHeFltTolFanPctMax => $params{cpqHeFltTolFanPctMax}, #!!!
    cpqHeFltTolFanHotPlug => $params{cpqHeFltTolFanHotPlug}, #!!!
    partner => $params{partner},
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  bless $self, $class;
  if (($self->{cpqHeFltTolFanRedundant} eq 'redundant') &&
     ((! defined $self->{cpqHeFltTolFanRedundantPartner}) ||
     (! $self->{cpqHeFltTolFanRedundantPartner}))) {
    $self->{cpqHeFltTolFanRedundant} = 'notRedundant';
      # cpqHeFltTolFanRedundantPartner=0: partner not avail
  }
  return $self;
} 

sub check { 
  my $self = shift;
  $self->blacklist('f', $self->{cpqHeFltTolFanIndex});
  $self->add_info(sprintf 'fan %d is %s, speed is %s, pctmax is %s%%, '.
      'location is %s, redundance is %s, partner is %s',
      $self->{cpqHeFltTolFanIndex}, $self->{cpqHeFltTolFanPresent},
      $self->{cpqHeFltTolFanSpeed}, $self->{cpqHeFltTolFanPctMax},
      $self->{cpqHeFltTolFanLocale}, $self->{cpqHeFltTolFanRedundant},
      $self->{cpqHeFltTolFanRedundantPartner});
  $self->add_extendedinfo(sprintf 'fan_%s=%d%%',
      $self->{cpqHeFltTolFanIndex}, $self->{cpqHeFltTolFanPctMax});
  if ($self->{cpqHeFltTolFanPresent} eq 'present') {
    if ($self->{cpqHeFltTolFanSpeed} eq 'high') { 
      $self->add_info(sprintf 'fan %d (%s) runs at high speed',
          $self->{cpqHeFltTolFanIndex}, $self->{cpqHeFltTolFanLocale});
      $self->add_message(CRITICAL, $self->{info});
    } elsif ($self->{cpqHeFltTolFanSpeed} ne 'normal') {
      $self->add_info(sprintf 'fan %d (%s) needs attention',
          $self->{cpqHeFltTolFanIndex}, $self->{cpqHeFltTolFanLocale});
      $self->add_message(CRITICAL, $self->{info});
    }
    if ($self->{cpqHeFltTolFanCondition} eq 'failed') {
      $self->add_info(sprintf 'fan %d (%s) failed',
          $self->{cpqHeFltTolFanIndex}, $self->{cpqHeFltTolFanLocale});
      $self->add_message(CRITICAL, $self->{info});
    } elsif ($self->{cpqHeFltTolFanCondition} eq 'degraded') {
      $self->add_info(sprintf 'fan %d (%s) degraded',
          $self->{cpqHeFltTolFanIndex}, $self->{cpqHeFltTolFanLocale});
      $self->add_message(WARNING, $self->{info});
    } elsif ($self->{cpqHeFltTolFanCondition} ne 'ok' &&
        $self->{cpqHeFltTolFanCondition} ne 'other') {
      $self->add_info(sprintf 'fan %d (%s) is not ok',
          $self->{cpqHeFltTolFanIndex}, $self->{cpqHeFltTolFanLocale});
      $self->add_message(WARNING, $self->{info});
    }
    if ($self->{cpqHeFltTolFanRedundant} eq 'notRedundant') {
      # sieht so aus, als waere notRedundant und partner=0 normal z.b. dl360
      # das duerfte der fall sein, wenn nur eine cpu verbaut wurde und
      # statt einem redundanten paar nur dummies drinstecken.
      # "This specifies if the fan is in a redundant configuration"
      # notRedundant heisst also sowohl nicht redundant wegen ausfall
      # des partners als auch von haus aus nicht redundant ausgelegt
      if ($self->{cpqHeFltTolFanRedundantPartner}) {
        # nicht redundant, hat aber einen partner. da muss man genauer
        # hinschauen
        #if (my $partner = $self->{partner}) {
        #}
        if ($self->{overallhealth}) {
          # da ist sogar das system der meinung, dass etwas faul ist
          if (! $self->{runtime}->{options}->{ignore_fan_redundancy}) {
            $self->add_info(sprintf 'fan %d (%s) is not redundant',
                $self->{cpqHeFltTolFanIndex}, $self->{cpqHeFltTolFanLocale});
            $self->add_message(WARNING, $self->{info});
          }
        } else {
          # das ist wohl so gewollt, dass einzelne fans eingebaut werden,
          # obwohl redundante paerchen vorgesehen sind.
          # scheint davon abzuhaengen, wieviele cpus geordert wurden.
        }
      }
    } elsif ($self->{cpqHeFltTolFanRedundant} eq 'other') {
      #seen on a dl320 g5p with bios from 2008.
      # maybe redundancy is not supported at all
    }
  } elsif ($self->{cpqHeFltTolFanPresent} eq 'failed') { # from cli
    $self->add_info(sprintf 'fan %d (%s) failed',
        $self->{cpqHeFltTolFanIndex}, $self->{cpqHeFltTolFanLocale});
    $self->add_message(CRITICAL, $self->{info});
  } elsif ($self->{cpqHeFltTolFanPresent} eq 'absent') {
    $self->add_info(sprintf 'fan %d (%s) needs attention (is absent)',
        $self->{cpqHeFltTolFanIndex}, $self->{cpqHeFltTolFanLocale});
    # weiss nicht, ob absent auch kaputt bedeuten kann
    # wenn nicht, dann wuerde man sich hier dumm und daemlich blacklisten
    #$self->add_message(CRITICAL, $self->{info});
    $self->add_message(WARNING, $self->{info}) if $self->{overallhealth};
  }
  if ($self->{runtime}->{options}->{perfdata}) {
    $self->{runtime}->{plugin}->add_perfdata(
        label => sprintf('fan_%s', $self->{cpqHeFltTolFanIndex}),
        value => $self->{cpqHeFltTolFanPctMax},
        uom => '%',
    );
  }
}

sub dump {
  my $self = shift;
  printf "[FAN_%s]\n", $self->{cpqHeFltTolFanIndex};
  foreach (qw(cpqHeFltTolFanChassis cpqHeFltTolFanIndex cpqHeFltTolFanLocale
      cpqHeFltTolFanPresent cpqHeFltTolFanType cpqHeFltTolFanSpeed
      cpqHeFltTolFanRedundant cpqHeFltTolFanRedundantPartner
      cpqHeFltTolFanCondition cpqHeFltTolFanHotPlug)) {
    printf "%s: %s\n", $_, $self->{$_};
  }
  printf "info: %s\n", $self->{info};
  printf "\n";
}


package HP::Proliant::Component::FanSubsystem::Fan::Thermal;
our @ISA = qw(HP::Proliant::Component::FanSubsystem::Fan);

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

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    runtime => $params{runtime},
    cpqHeThermalFanIndex => $params{cpqHeThermalFanIndex},
    cpqHeThermalFanRequired => $params{cpqHeThermalFanRequired},
    cpqHeThermalFanPresent => $params{cpqHeThermalFanPresent},
    cpqHeThermalFanCpuFan => $params{cpqHeThermalFanCpuFan},
    cpqHeThermalFanStatus => $params{cpqHeThermalFanStatus},
    cpqHeThermalFanHwLocation => $params{cpqHeThermalFanHwLocation},
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  bless $self, $class;
  return $self;
}

sub check {
  my $self = shift;
}

sub dump {
  my $self = shift;
  printf "[FAN_%s]\n", $self->{cpqHeThermalFanIndex};
  foreach (qw(cpqHeThermalFanIndex cpqHeThermalFanRequired 
      cpqHeThermalFanPresent cpqHeThermalFanCpuFan cpqHeThermalFanStatus
      cpqHeThermalFanHwLocation)) {
    printf "%s: %s\n", $_, $self->{$_};
  }
  printf "info: %s\n", $self->{info};
  printf "\n";
}
package HP::Proliant::Component::FanSubsystem::CLI;
our @ISA = qw(HP::Proliant::Component::FanSubsystem);

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

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

# partner not available = cpqHeFltTolFanRedundantPartner=0
# cpqHeFltTolFanTypeValue = other
sub init {
  my $self = shift;
  my %params = @_;
  my %tmpfan = ();
  foreach (grep(/^fans/, split(/\n/, $self->{rawdata}))) {
    s/^fans //g;
    if (/^#(\d+)\s+([\w#_\/\-]+)\s+(\w+)\s+(\w+)\s+(FAILED|[N\/A\d]+)%*\s+([\w\/]+)\s+(FAILED|[N\/A\d]+)\s+(\w+)/) {
      %tmpfan = (
          cpqHeFltTolFanIndex => $1, 
          cpqHeFltTolFanLocale => lc $2,
          cpqHeFltTolFanPresent => lc $3,
          cpqHeFltTolFanSpeed => lc $4,
          cpqHeFltTolFanPctMax => lc $5,                 # (FAILED|[N\/A\d]+)
          cpqHeFltTolFanRedundant => lc $6,
          cpqHeFltTolFanRedundantPartner => lc $7,       # (FAILED|[N\/A\d]+)
          cpqHeFltTolFanHotPlug => lc $8,
      ); 
    } elsif (/^#(\d+)\s+([\w#_\/\-]+?)(Yes|No|N\/A)\s+(\w+)\s+(FAILED|[N\/A\d]+)%*\s+([\w\/]+)\s+(FAILED|[N\/A\d]+)\s+(\w+)/) { 
      # #5   SCSI_BACKPLANE_ZONEYes     NORMAL N/A  .... 
      %tmpfan = (
          cpqHeFltTolFanIndex => $1,
          cpqHeFltTolFanLocale => lc $2,
          cpqHeFltTolFanPresent => lc $3,
          cpqHeFltTolFanSpeed => lc $4, 
          cpqHeFltTolFanPctMax => lc $5,
          cpqHeFltTolFanRedundant => lc $6,
          cpqHeFltTolFanRedundantPartner => lc $7,
          cpqHeFltTolFanHotPlug => lc $8,
      );
    } elsif (/^#(\d+)\s+([\w#_\/\-]+)\s+[NOno]+\s/) {
      # Fan is not installed. #2   CPU#2   No   -   -    No      N/A      -
    } elsif (/^#(\d+)/) {
      main::contact_author("FAN", $_); 
    }
    if (%tmpfan) {
      $tmpfan{runtime} = $params{runtime};
      $tmpfan{cpqHeFltTolFanChassis} = 1; # geht aus hpasmcli nicht hervor
      $tmpfan{cpqHeFltTolFanType} = 'other';
      if ($tmpfan{cpqHeFltTolFanPctMax} !~ /^\d+$/) {
        if ($tmpfan{cpqHeFltTolFanSpeed} eq 'normal') {
          $tmpfan{cpqHeFltTolFanPctMax} = 50;
        } elsif ($tmpfan{cpqHeFltTolFanSpeed} eq 'high') {
          $tmpfan{cpqHeFltTolFanPctMax} = 100;
        } else {
          $tmpfan{cpqHeFltTolFanPctMax} = 0;
        }
      }
      if($tmpfan{cpqHeFltTolFanSpeed} eq 'failed') {
        $tmpfan{cpqHeFltTolFanCondition} = 'failed';
      } elsif($tmpfan{cpqHeFltTolFanSpeed} eq 'n/a') {
        $tmpfan{cpqHeFltTolFanCondition} = 'other';
      } else {
        $tmpfan{cpqHeFltTolFanCondition} = 'ok';
      }
      $tmpfan{cpqHeFltTolFanRedundant} = 
          $tmpfan{cpqHeFltTolFanRedundant} eq 'yes' ? 'redundant' :
          $tmpfan{cpqHeFltTolFanRedundant} eq 'no' ? 'notRedundant' : 'other';
      $tmpfan{cpqHeFltTolFanPresent} = 
          $tmpfan{cpqHeFltTolFanPresent} eq 'yes' ? 'present' :
          $tmpfan{cpqHeFltTolFanPresent} eq 'failed' ? 'present' :
          $tmpfan{cpqHeFltTolFanPresent} eq 'no' ? 'absent' : 'other';
      $tmpfan{cpqHeFltTolFanHotPlug} = 
          $tmpfan{cpqHeFltTolFanHotPlug} eq 'yes' ? 'hotPluggable' :
          $tmpfan{cpqHeFltTolFanHotPlug} eq 'no' ? 'nonHotPluggable' : 'other';
      push(@{$self->{fans}},
          HP::Proliant::Component::FanSubsystem::Fan->new(%tmpfan));
      %tmpfan = ();
    }
  }
}

sub overall_check {
  my $self = shift;
  # nix. nur wegen der gleichheit mit snmp
  return 0;
}

package HP::Proliant::Component::FanSubsystem::SNMP;
our @ISA = qw(HP::Proliant::Component::FanSubsystem
    HP::Proliant::Component::SNMP);

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

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    runtime => $params{runtime},
    rawdata => $params{rawdata},
    fans => [],
    he_fans => [],
    th_fans => [],
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  bless $self, $class;
  $self->overall_init(%params);
  $self->he_init(%params);
  $self->te_init(%params);
  $self->unite();
  return $self;
}

sub overall_init {
  my $self = shift;
  my %params = @_;
  my $snmpwalk = $params{rawdata};
  # overall
  my $cpqHeThermalSystemFanStatus = '1.3.6.1.4.1.232.6.2.6.4.0';
  my $cpqHeThermalSystemFanStatusValue = {
    1 => 'other',
    2 => 'ok',
    3 => 'degraded',
    4 => 'failed',
  };
  my $cpqHeThermalCpuFanStatus = '1.3.6.1.4.1.232.6.2.6.5.0';
  my $cpqHeThermalCpuFanStatusValue = {
    1 => 'other',
    2 => 'ok',
    4 => 'failed', # shutdown
  };
  $self->{sysstatus} = SNMP::Utils::get_object_value(
      $snmpwalk, $cpqHeThermalSystemFanStatus,
      $cpqHeThermalSystemFanStatusValue);
  $self->{cpustatus} = SNMP::Utils::get_object_value(
      $snmpwalk, $cpqHeThermalCpuFanStatus,
      $cpqHeThermalCpuFanStatusValue);
  $self->{sysstatus} |= lc $self->{sysstatus};
  $self->{cpustatus} |= lc $self->{cpustatus};
}

sub te_init {
  my $self = shift;
  my %params = @_;
  my $snmpwalk = $params{rawdata};
  my $ignore_redundancy = $params{ignore_redundancy};
  # cpqHeThermalFanTable
  my $oids = {
      cpqHeThermalFanEntry => '1.3.6.1.4.1.232.6.2.6.6.1',
      cpqHeThermalFanIndex => '1.3.6.1.4.1.232.6.2.6.6.1.1',
      cpqHeThermalFanRequired => '1.3.6.1.4.1.232.6.2.6.6.1.2',
      cpqHeThermalFanPresent => '1.3.6.1.4.1.232.6.2.6.6.1.3',
      cpqHeThermalFanCpuFan => '1.3.6.1.4.1.232.6.2.6.6.1.4',
      cpqHeThermalFanStatus => '1.3.6.1.4.1.232.6.2.6.6.1.5',
      cpqHeThermalFanHwLocation => '1.3.6.1.4.1.232.6.2.6.6.1.6',
      cpqHeThermalFanRequiredValue => {
        1 => 'other',
        2 => 'nonRequired',
        3 => 'required',
      },
      cpqHeThermalFanPresentValue => {
        1 => 'other',
        2 => 'absent',
        3 => 'present',
      },
      cpqHeThermalFanCpuFanValue => {
        1 => 'other',
        2 => 'systemFan',
        3 => 'cpuFan',
      },
      cpqHeThermalFanStatusValue => {
        1 => 'other',
        2 => 'ok',
        4 => 'failed',
      },
  };
  # INDEX { cpqHeThermalFanIndex }
  foreach ($self->get_entries($oids, 'cpqHeThermalFanEntry')) {
    next if ! $_->{cpqHeThermalFanPresent};
    push(@{$self->{th_fans}},
        HP::Proliant::Component::FanSubsystem::Fan->new(%{$_}));
  }
}

sub he_init {
  my $self = shift;
  my %params = @_;
  my $snmpwalk = $params{rawdata};
  my $ignore_redundancy = $params{ignore_redundancy};
  # cpqHeFltTolFanTable
  my $oids = {
      cpqHeFltTolFanEntry => '1.3.6.1.4.1.232.6.2.6.7.1',
      cpqHeFltTolFanChassis => '1.3.6.1.4.1.232.6.2.6.7.1.1',
      cpqHeFltTolFanIndex => '1.3.6.1.4.1.232.6.2.6.7.1.2',
      cpqHeFltTolFanLocale => '1.3.6.1.4.1.232.6.2.6.7.1.3',
      cpqHeFltTolFanPresent => '1.3.6.1.4.1.232.6.2.6.7.1.4',
      cpqHeFltTolFanType => '1.3.6.1.4.1.232.6.2.6.7.1.5',
      cpqHeFltTolFanSpeed => '1.3.6.1.4.1.232.6.2.6.7.1.6',
      cpqHeFltTolFanRedundant => '1.3.6.1.4.1.232.6.2.6.7.1.7',
      cpqHeFltTolFanRedundantPartner => '1.3.6.1.4.1.232.6.2.6.7.1.8',
      cpqHeFltTolFanCondition => '1.3.6.1.4.1.232.6.2.6.7.1.9',
      cpqHeFltTolFanHotPlug => '1.3.6.1.4.1.232.6.2.6.7.1.10',
      cpqHeFltTolFanHwLocation => '1.3.6.1.4.1.232.6.2.6.7.1.11',
      cpqHeFltTolFanCurrentSpeed => '1.3.6.1.4.1.232.6.2.6.7.1.12',
      cpqHeFltTolFanLocaleValue => {
          1 => "other",
          2 => "unknown",
          3 => "system",
          4 => "systemBoard",
          5 => "ioBoard",
          6 => "cpu",
          7 => "memory",
          8 => "storage",
          9 => "removableMedia",
          10 => "powerSupply", 
          11 => "ambient",
          12 => "chassis",
          13 => "bridgeCard",
      },
      cpqHeFltTolFanPresentValue => {
          1 => "other",
          2 => "absent",
          3 => "present",
      },
      cpqHeFltTolFanSpeedValue => {
          1 => "other",
          2 => "normal",
          3 => "high",
      },
      cpqHeFltTolFanRedundantValue => {
          1 => "other",
          2 => "notRedundant",
          3 => "redundant",
      },
      cpqHeFltTolFanTypeValue => {
          1 => "other",
          2 => "tachInput",
          3 => "spinDetect",
      },
      cpqHeFltTolFanConditionValue => {
          1 => "other",
          2 => "ok",
          3 => "degraded",
          4 => "failed",
      },
      cpqHeFltTolFanHotPlugValue => {
          1 => "other",
          2 => "nonHotPluggable",
          3 => "hotPluggable",
      },
  };
  # INDEX { cpqHeFltTolFanChassis, cpqHeFltTolFanIndex }
  foreach ($self->get_entries($oids, 'cpqHeFltTolFanEntry')) {
    next if ! defined $_->{cpqHeFltTolFanIndex};
    # z.b. USM65201WS hat nur solche fragmente. die werden erst gar nicht
    # als fans akzeptiert. dafuer gibts dann die overall condition
    # SNMPv2-SMI::enterprises.232.6.2.6.7.1.1.0.1 = INTEGER: 0
    # SNMPv2-SMI::enterprises.232.6.2.6.7.1.1.0.2 = INTEGER: 0
    $_->{cpqHeFltTolFanPctMax} = ($_->{cpqHeFltTolFanPresent} eq 'present') ?
        50 : 0;
    push(@{$self->{he_fans}},
        HP::Proliant::Component::FanSubsystem::Fan->new(%{$_}));
  }

}

sub unite {
  my $self = shift;
  my $tmpfans = {};
  foreach (@{$self->{he_fans}}) {
    $tmpfans->{$_->{cpqHeFltTolFanIndex}} = $_;
  }
  foreach (@{$self->{he_fans}}) {
    if (exists $tmpfans->{$_->{cpqHeFltTolFanRedundantPartner}}) {
      $_->{partner} = $tmpfans->{$_->{cpqHeFltTolFanRedundantPartner}};
    } else {
      $_->{partner} = undef;
    }
  }
  @{$self->{fans}} = @{$self->{he_fans}};
}

sub overall_check {
  my $self = shift;
  my $result = 0;
  $self->blacklist('ofs', '');
  if ($self->{sysstatus} && $self->{cpustatus}) {
    if ($self->{sysstatus} eq 'degraded') {
      $result = 1;
      $self->add_message(WARNING,
          sprintf 'system fan overall status is %s', $self->{sysstatus});
    } elsif ($self->{sysstatus} eq 'failed') {
      $result = 2;
      $self->add_message(CRITICAL,
          sprintf 'system fan overall status is %s', $self->{sysstatus});
    } 
    if ($self->{cpustatus} eq 'degraded') {
      $result = 1;
      $self->add_message(WARNING,
          sprintf 'cpu fan overall status is %s', $self->{cpustatus});
    } elsif ($self->{cpustatus} eq 'failed') {
      $result = 2;
      $self->add_message(CRITICAL,
          sprintf 'cpu fan overall status is %s', $self->{cpustatus});
    } 
    $self->add_info(sprintf 'overall fan status: system=%s, cpu=%s',
        $self->{sysstatus}, $self->{cpustatus});
  } else {
    $result = 0;
    $self->add_info('this system seems to be water-cooled. no fans found');
  }
  return $result;
}



package HP::Proliant::Component::MemorySubsystem::CLI;
our @ISA = qw(HP::Proliant::Component::MemorySubsystem);

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

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

sub init {
  my $self = shift;
  my %params = @_;
  $self->{dimms} = [];
  my %tmpdimm = (
    runtime => $params{runtime},
  );
  my $inblock = 0;
  foreach (grep(/^dimm/, split(/\n/, $self->{rawdata}))) {
    s/^dimm\s*$//g;
    if (/Cartridge #:\s+(\d+)/ || /Processor #:\s+(\d+)/) {
      # neuerdings (g6) tauchen hier prozessor- statt cartridge-angaben auf
      $tmpdimm{cartridge} = $1;
      $tmpdimm{board} = $1;
      $inblock = 1;
    } elsif (/Module #:\s+(\d+)/) {
      $tmpdimm{module} = $1;
    } elsif (/Present:\s+(\w+)/) {
      $tmpdimm{status} = lc $1 eq 'yes' ? 'present' :
          lc $1 eq 'no' ? 'notPresent' : 'other';
    } elsif (/Status:\s+(.+?)\s*$/) {
      $tmpdimm{condition} = lc $1 =~ /degraded/ ? 'degraded' :
          lc $1 eq 'ok' ? 'ok' : lc $1 =~ /n\/a/ ? 'n/a' : 'other';
    } elsif (/Size:\s+(\d+)\s*(.+?)\s*$/) {
      $tmpdimm{size} = $1 * (lc $2 eq 'mb' ? 1024*1024 :
          lc $2 eq 'gb' ? 1024*1024*1024 : 1);
    } elsif (/^\s*$/) {
      if ($inblock) {
        $inblock = 0;
        push(@{$self->{dimms}},
            HP::Proliant::Component::MemorySubsystem::Dimm->new(%tmpdimm));
        %tmpdimm = (
          runtime => $params{runtime},
        );
      }
    } elsif (/(\d+)\s+(\d+)\s+(\w+)\s+(0x\w+)\s+(0x\w+)\s+(\d+[MGT]B)\s+(\d+MHz)\s+(\w+)/) {
      $tmpdimm{cartridge} = $1;
      $tmpdimm{module} = $2;
      $tmpdimm{status} = lc $3 eq 'yes' ? 'present' :
          lc $3 eq 'no' ? 'notPresent' : 'other';
      my $formfactor = $4;
      my $memorytype = $5;
      my $memorysize = $6;
      my $memoryspeed = $7;
      $tmpdimm{condition} = lc $8 =~ /degraded/ ? 'degraded' :
          lc $8 eq 'ok' ? 'ok' : lc $8 =~ /n\/a/ ? 'n/a' : 'other';
      $memorysize =~ /(\d+)([MGT]B)/;
      $tmpdimm{size} = $1 * (lc $2 eq 'mb' ? 1024*1024 :
          lc $2 eq 'gb' ? 1024*1024*1024 : 1);
      push(@{$self->{dimms}},
          HP::Proliant::Component::MemorySubsystem::Dimm->new(%tmpdimm));
    }
  }
  if ($inblock) {
    push(@{$self->{dimms}},
        HP::Proliant::Component::MemorySubsystem::Dimm->new(%tmpdimm));
  }
}

sub is_faulty {
  my $self = shift;
  return 0; # cli hat so einen globalen status nicht
}


package HP::Proliant::Component::MemorySubsystem::SNMP;
our @ISA = qw(HP::Proliant::Component::MemorySubsystem
    HP::Proliant::Component::SNMP);

use strict;

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    runtime => $params{runtime},
    rawdata => $params{rawdata},
    dimms => [],
    si_dimms => [],
    he_dimms => [],
    h2_dimms => [],
    he_cartridges => [],
    h2_cartridges => [],
    si_overall_condition => undef,
    he_overall_condition => undef,
    h2_overall_condition => undef,
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  bless $self, $class;
  $self->si_init();
  $self->he_init();
  $self->he_cartridge_init();
  $self->h2_init();
  #$self->h2_cartridge_init();
  $self->condense();
  return $self;
}

sub si_init {
  my $self = shift;
  my $snmpwalk = $self->{rawdata};
  my $oids = {
      cpqSiMemModuleEntry => '1.3.6.1.4.1.232.2.2.4.5.1',
      cpqSiMemBoardIndex => '1.3.6.1.4.1.232.2.2.4.5.1.1',
      cpqSiMemModuleIndex => '1.3.6.1.4.1.232.2.2.4.5.1.2',
      cpqSiMemModuleSize => '1.3.6.1.4.1.232.2.2.4.5.1.3',
      cpqSiMemModuleType => '1.3.6.1.4.1.232.2.2.4.5.1.4',
      cpqSiMemECCStatus => '1.3.6.1.4.1.232.2.2.4.5.1.11',
      cpqSiMemModuleHwLocation => '1.3.6.1.4.1.232.2.2.4.5.1.12',
      cpqSiMemModuleTypeValue => {
          1 => 'other',
          2 => 'board',
          3 => 'cpqSingleWidthModule',
          4 => 'cpqDoubleWidthModule',
          5 => 'simm',
          6 => 'pcmcia',
          7 => 'compaq-specific',
          8 => 'dimm',
          9 => 'smallOutlineDimm',
          10 => 'rimm',
          11 => 'srimm',
      },
      cpqSiMemECCStatusValue => {
          0 => "n/a",
          1 => "other",
          2 => "ok",
          3 => "degraded",
          4 => "degradedModuleIndexUnknown",
          34 => 'n/a', # es ist zum kotzen...
          104 => 'n/a',
      },
  };
  # INDEX { cpqSiMemBoardIndex, cpqSiMemModuleIndex }
  foreach ($self->get_entries($oids, 'cpqSiMemModuleEntry')) {
    $_->{cartridge} = $_->{cpqSiMemBoardIndex};
    $_->{module} = $_->{cpqSiMemModuleIndex};
    next if (! defined $_->{cartridge} || ! defined $_->{module});
    $_->{size} = $_->{cpqSiMemModuleSize};
    $_->{type} = $_->{cpqSiMemModuleType};
    $_->{condition} = $_->{cpqSiMemECCStatus};
    $_->{status} = ($_->{cpqSiMemModuleSize} > 0) ? 'present' : 'notPresent';
    push(@{$self->{si_dimms}},
        HP::Proliant::Component::MemorySubsystem::Dimm->new(%{$_})
    );
  } 
  my $cpqSiMemECCCondition = '1.3.6.1.4.1.232.2.2.4.15.0';
  my $cpqSiMemECCConditionValue = {
    1 => 'other',
    2 => 'ok',
    3 => 'degraded',
  };
  $self->{si_overall_condition} = SNMP::Utils::get_object_value(
        $self->{rawdata}, $cpqSiMemECCCondition,
        $cpqSiMemECCConditionValue);
  $self->trace(2, sprintf 'overall si condition is %s', 
      $self->{si_overall_condition} || 'undefined');
}

sub he_init {
  my $self = shift;
  my $snmpwalk = $self->{rawdata};
  my $oids = {
      cpqHeResMemModuleEntry => '1.3.6.1.4.1.232.6.2.14.11.1',
      cpqHeResMemBoardIndex => '1.3.6.1.4.1.232.6.2.14.11.1.1',
      cpqHeResMemModuleIndex => '1.3.6.1.4.1.232.6.2.14.11.1.2',
      cpqHeResMemModuleStatus => '1.3.6.1.4.1.232.6.2.14.11.1.4',
      cpqHeResMemModuleCondition => '1.3.6.1.4.1.232.6.2.14.11.1.5',
      cpqHeResMemModuleStatusValue => {
          1 => "other",         # unknown or could not be determined
          2 => "notPresent",    # not present or un-initialized
          3 => "present",       # present but not in use
          4 => "good",          # present and in use. ecc threshold not exceeded
          5 => "add",           # added but not yet in use
          6 => "upgrade",       # upgraded but not yet in use
          7 => "missing",       # expected but missing
          8 => "doesNotMatch",  # does not match the other modules in the bank
          9 => "notSupported",  # module not supported
          10 => "badConfig",    # violates add/upgrade configuration
          11 => "degraded",     # ecc exceeds threshold
      },
      # condition = status of the correctable memory errors
      cpqHeResMemModuleConditionValue => {
          0 => "n/a", # this appears only with buggy firmwares.
          # (only 1 module shows up)
          1 => "other",
          2 => "ok",
          3 => "degraded",
      },
  };
  my $tablesize = SNMP::Utils::get_size($snmpwalk, 
      $oids->{cpqHeResMemModuleEntry});
  # INDEX { cpqHeResMemBoardIndex, cpqHeResMemModuleIndex }
  foreach ($self->get_entries($oids, 'cpqHeResMemModuleEntry')) {
    $_->{cartridge} = $_->{cpqHeResMemBoardIndex};
    $_->{module} = $_->{cpqHeResMemModuleIndex};
    $_->{present} = $_->{cpqHeResMemModuleStatus};
    $_->{status} = $_->{cpqHeResMemModuleStatus};
    $_->{condition} = $_->{cpqHeResMemModuleCondition};
    if ((! defined $_->{module}) && ($_->{cartridge} == 0)) {
      $_->{module} = $_->{index2}; # auf dem systemboard verbaut
    }

    push(@{$self->{he_dimms}}, 
        HP::Proliant::Component::MemorySubsystem::Dimm->new(%{$_})
    ) unless (! defined $_->{cartridge} || ! defined $_->{module} ||
        $tablesize == 1);
  }
  my $cpqHeResilientMemCondition = '1.3.6.1.4.1.232.6.2.14.4.0';
  my $cpqHeResilientMemConditionValue = {
    1 => 'other',
    2 => 'ok',
    3 => 'degraded',
  };
  $self->{he_overall_condition} = SNMP::Utils::get_object_value(
        $self->{rawdata}, $cpqHeResilientMemCondition,
        $cpqHeResilientMemConditionValue);
  $self->trace(2, sprintf 'overall he condition is %s', 
      $self->{hei_overall_condition} || 'undefined');
}

sub he_cartridge_init {
  my $self = shift;
  my $snmpwalk = $self->{rawdata};
  my $oids = {
      cpqHeResMemBoardEntry => '1.3.6.1.4.1.232.6.2.14.10.1',
      cpqHeResMemBoardSlotIndex => '1.3.6.1.4.1.232.6.2.14.10.1.1',
      cpqHeResMemBoardOnlineStatus => '1.3.6.1.4.1.232.6.2.14.10.1.2',
      cpqHeResMemBoardErrorStatus => '1.3.6.1.4.1.232.6.2.14.10.1.3',
      cpqHeResMemBoardNumSockets => '1.3.6.1.4.1.232.6.2.14.10.1.5',
      cpqHeResMemBoardOsMemSize => '1.3.6.1.4.1.232.6.2.14.10.1.6',
      cpqHeResMemBoardTotalMemSize => '1.3.6.1.4.1.232.6.2.14.10.1.7',
      cpqHeResMemBoardCondition => '1.3.6.1.4.1.232.6.2.14.10.1.8',
      # onlinestatus
      cpqHeResMemBoardOnlineStatusValue => {
          0 => "n/a", # this appears only with buggy firmwares.
          # (only 1 module shows up)
          1 => "other",
          2 => "present",
          3 => "absent",
      },
      cpqHeResMemBoardErrorStatusValue => {
          1 => "other",         #
          2 => "noError",       #
          3 => "dimmEccError",  #
          4 => "unlockError",   #
          5 => "configError",   #
          6 => "busError",      #
          7 => "powerError",    #
      },
      # condition = status of the correctable memory errors
      cpqHeResMemBoardConditionValue => {
          0 => "n/a", # this appears only with buggy firmwares.
          # (only 1 module shows up)
          1 => "other",
          2 => "ok",
          3 => "degraded",
      },
  };
  my $tablesize = SNMP::Utils::get_size($snmpwalk,
      $oids->{cpqHeResMemBoardEntry});
  # INDEX { cpqHeResMemBoardIndex, cpqHeResMemBoardIndex }
  foreach ($self->get_entries($oids, 'cpqHeResMemBoardEntry')) {
    push(@{$self->{he_cartridges}},
        HP::Proliant::Component::MemorySubsystem::Cartridge->new(%{$_})
    ) unless (! defined $_->{cpqHeResMemBoardSlotIndex} || $tablesize == 1);
  }
}

sub h2_init {
  my $self = shift;
  my $snmpwalk = $self->{rawdata};
  my $oids = {
      cpqHeResMem2ModuleEntry => '1.3.6.1.4.1.232.6.2.14.13.1',
      cpqHeResMem2BoardNum => '1.3.6.1.4.1.232.6.2.14.13.1.2',
      cpqHeResMem2ModuleNum => '1.3.6.1.4.1.232.6.2.14.13.1.5',
      cpqHeResMem2ModuleStatus => '1.3.6.1.4.1.232.6.2.14.13.1.19',
      cpqHeResMem2ModuleCondition => '1.3.6.1.4.1.232.6.2.14.13.1.20',
      cpqHeResMem2ModuleSize => '1.3.6.1.4.1.232.6.2.14.13.1.6',
    
      cpqHeResMem2ModuleStatusValue => {
          1 => "other",         # unknown or could not be determined
          2 => "notPresent",    # not present or un-initialized
          3 => "present",       # present but not in use
          4 => "good",          # present and in use. ecc threshold not exceeded
          5 => "add",           # added but not yet in use
          6 => "upgrade",       # upgraded but not yet in use
          7 => "missing",       # expected but missing
          8 => "doesNotMatch",  # does not match the other modules in the bank
          9 => "notSupported",  # module not supported
          10 => "badConfig",    # violates add/upgrade configuration
          11 => "degraded",     # ecc exceeds threshold
      },
      # condition = status of the correctable memory errors
      cpqHeResMem2ModuleConditionValue => {
          0 => "n/a", # this appears only with buggy firmwares.
          # (only 1 module shows up)
          1 => "other",
          2 => "ok",
          3 => "degraded",
      },
  };
  # INDEX { cpqHeResMem2ModuleNum }
  my $lastboard = 0;
  my $lastmodule = 0;
  my $myboard= 0;
  my $hpboard = 0;
  foreach (sort { $a->{index1} <=> $b->{index1} }
      $self->get_entries($oids, 'cpqHeResMem2ModuleEntry')) {
    $hpboard = $_->{cpqHeResMem2BoardNum};
      # dass hier faelschlicherweise 0 zurueckkommt, wundert mich schon
      # gar nicht mehr
    $_->{module} = $_->{cpqHeResMem2ModuleNum};
    if ($_->{module} < $lastmodule) {
      # sieht so aus, als haette man es mit einem neuen board zu tun
      # da hp zu bloed ist, selber hochzuzaehlen, muss ich das tun
      $myboard++;
    }
    $lastmodule = $_->{cpqHeResMem2ModuleNum};
    $_->{cartridge} = ($myboard != $hpboard) ? $myboard : $hpboard;
    $_->{present} = $_->{cpqHeResMem2ModuleStatus};
    $_->{status} = $_->{cpqHeResMem2ModuleStatus};
    $_->{condition} = $_->{cpqHeResMem2ModuleCondition};
    $_->{size} = $_->{cpqHeResMem2ModuleSize};
    push(@{$self->{h2_dimms}},
        HP::Proliant::Component::MemorySubsystem::Dimm->new(%{$_})
    ) unless (! defined $_->{cpqHeResMem2BoardNum} ||
        ! defined $_->{cpqHeResMem2ModuleNum});
  }
}

sub h2_cartridge_init {
  my $self = shift;
  my $snmpwalk = $self->{rawdata};
  my $oids = {
      cpqHeResMem2BoardEntry => '1.3.6.1.4.1.232.6.2.14.12.1',
      cpqHeResMem2BoardIndex => '1.3.6.1.4.1.232.6.2.14.12.1.1',
      cpqHeResMem2BoardOnlineStatus => '1.3.6.1.4.1.232.6.2.14.12.1.5',
      cpqHeResMem2BoardErrorStatus => '1.3.6.1.4.1.232.6.2.14.12.1.6',
      cpqHeResMem2BoardNumSockets => '1.3.6.1.4.1.232.6.2.14.12.1.8',
      cpqHeResMem2BoardOsMemSize => '1.3.6.1.4.1.232.6.2.14.12.1.9',
      cpqHeResMem2BoardTotalMemSize => '1.3.6.1.4.1.232.6.2.14.12.1.10',
      cpqHeResMem2BoardCondition => '1.3.6.1.4.1.232.6.2.14.12.1.11',
      # onlinestatus
      cpqHeResMem2BoardOnlineStatusValue => {
          0 => "n/a", # this appears only with buggy firmwares.
          # (only 1 module shows up)
          1 => "other",
          2 => "present",
          3 => "absent",
      },
      cpqHeResMem2BoardErrorStatusValue => {
          1 => "other",         #
          2 => "noError",       #
          3 => "dimmEccError",  #
          4 => "unlockError",   #
          5 => "configError",   #
          6 => "busError",      #
          7 => "powerError",    #
      },
      # condition = status of the correctable memory errors
      cpqHeResMem2BoardConditionValue => {
          0 => "n/a", # this appears only with buggy firmwares.
          # (only 1 module shows up)
          1 => "other",
          2 => "ok",
          3 => "degraded",
      },
  };
  my $tablesize = SNMP::Utils::get_size($snmpwalk,
      $oids->{cpqHeResMemBoardEntry});
  # INDEX { cpqHeResMem2BoardIndex, cpqHeResMem2BoardIndex }
  foreach ($self->get_entries($oids, 'cpqHeResMem2BoardEntry')) {
    push(@{$self->{h2_cartridges}},
        HP::Proliant::Component::MemorySubsystem::Cartridge->new(%{$_})
    ) unless (! defined $_->{cpqHeRes2MemBoardIndex} || $tablesize == 1);
  }
}

sub condense {
  my $self = shift;
  my $snmpwalk = $self->{rawdata};
  # wenn saemtliche dimms n/a sind
  #  wenn ignore dimms: ignoring %d dimms with status 'n/a'
  #  wenn buggyfirmware: ignoring %d dimms with status 'n/a' because of buggy firmware
  # if buggy firmware : condition n/a ist normal
  # ignore-dimms :
  # es gibt si_dimms und he_dimms
  my $si_dimms = scalar(@{$self->{si_dimms}});
  my $he_dimms = scalar(@{$self->{he_dimms}});
  my $h2_dimms = scalar(@{$self->{h2_dimms}});
  $self->trace(2, sprintf "SI: %02d   HE: %02d   H2: %02d",
      $si_dimms, $he_dimms, $h2_dimms)
      if ($self->{runtime}->{options}->{verbose} >= 2);
  foreach ($self->get_si_boards()) {
    printf "SI%02d-> ", $_ if ($self->{runtime}->{options}->{verbose} >= 2);
    foreach ($self->get_si_modules($_)) {
      printf "%02d ", $_ if ($self->{runtime}->{options}->{verbose} >= 2);
    }
    printf "\n" if ($self->{runtime}->{options}->{verbose} >= 2);
  }
  foreach ($self->get_he_boards()) {
    printf "HE%02d-> ", $_ if ($self->{runtime}->{options}->{verbose} >= 2);
    foreach ($self->get_he_modules($_)) {
      printf "%02d ", $_ if ($self->{runtime}->{options}->{verbose} >= 2);
    }
    printf "\n" if ($self->{runtime}->{options}->{verbose} >= 2);
  }
  foreach ($self->get_h2_boards()) {
    printf "H2%02d-> ", $_ if ($self->{runtime}->{options}->{verbose} >= 2);
    foreach ($self->get_h2_modules($_)) {
      printf "%02d ", $_ if ($self->{runtime}->{options}->{verbose} >= 2);
    }
    printf "\n" if ($self->{runtime}->{options}->{verbose} >= 2);
  }
  if (($h2_dimms == 0) && ($he_dimms == 0) && ($si_dimms > 0)) {
    printf "TYP1 %s\n", $self->{runtime}->{product}
        if ($self->{runtime}->{options}->{verbose} >= 2);
    @{$self->{dimms}} = $self->update_si_with_si();
  } elsif (($h2_dimms == 0) && ($he_dimms > 0) && ($si_dimms > 0)) {
    printf "TYP2 %s\n", $self->{runtime}->{product}
        if ($self->{runtime}->{options}->{verbose} >= 2);
    @{$self->{dimms}} = $self->update_si_with_he();
  } elsif (($h2_dimms == 0) && ($he_dimms > 0) && ($si_dimms == 0)) {
    printf "TYP3 %s\n", $self->{runtime}->{product}
        if ($self->{runtime}->{options}->{verbose} >= 2);
    @{$self->{dimms}} = $self->update_he_with_he();
  } elsif (($h2_dimms > 0) && ($he_dimms == 0) && ($si_dimms == 0)) {
    printf "TYP4 %s\n", $self->{runtime}->{product}
        if ($self->{runtime}->{options}->{verbose} >= 2);
    @{$self->{dimms}} = $self->update_h2_with_h2();
  } elsif (($h2_dimms > 0) && ($he_dimms > 0) && ($si_dimms == 0)) {
    printf "TYP5 %s\n", $self->{runtime}->{product}
        if ($self->{runtime}->{options}->{verbose} >= 2);
    @{$self->{dimms}} = $self->update_he_with_h2();
  } elsif (($h2_dimms > 0) && ($he_dimms == 0) && ($si_dimms > 0)) {
    printf "TYP6 %s\n", $self->{runtime}->{product}
        if ($self->{runtime}->{options}->{verbose} >= 2);
    @{$self->{dimms}} = $self->update_si_with_h2();
  } elsif (($h2_dimms > 0) && ($he_dimms > 0) && ($si_dimms > 0)) {
    if ($h2_dimms > $si_dimms) {
      printf "TYP7 %s\n", $self->{runtime}->{product}
          if ($self->{runtime}->{options}->{verbose} >= 2);
      @{$self->{dimms}} = $self->update_he_with_h2();
    } else {
      printf "TYP8 %s\n", $self->{runtime}->{product}
          if ($self->{runtime}->{options}->{verbose} >= 2);
      @{$self->{dimms}} = $self->update_si_with_he();
    }
  } else {
    printf "TYPX %s\n", $self->{runtime}->{product}
        if ($self->{runtime}->{options}->{verbose} >= 2);
  }
  my $all_dimms = scalar(@{$self->{dimms}});
  $self->trace(2, sprintf "ALL: %02d", $all_dimms);
}

sub dump {
  my $self = shift;
  if ($self->{runtime}->{options}->{verbose} > 2) {
    printf "[SI]\n";
    foreach (@{$self->{si_dimms}}) {
      $_->dump();
    }
    printf "[HE]\n";
    foreach (@{$self->{he_dimms}}) {
      $_->dump();
    }
    printf "[H2]\n";
    foreach (@{$self->{h2_dimms}}) {
      $_->dump();
    }
  }
  $self->SUPER::dump();
}

sub update_si_with_si {
  my $self = shift;
  my $snmpwalk = $self->{rawdata};
  my @dimms = ();
  my $repaircondition = undef;
  # wenn si keine statusinformationen liefert, dann besteht die chance
  # dass ein undokumentiertes he-fragment vorliegt
  # 1.3.6.1.4.1.232.6.2.14.11.1.1.0.<anzahl der dimms>
  my $cpqHeResMemModuleEntry = "1.3.6.1.4.1.232.6.2.14.11.1";
  if (SNMP::Utils::get_size($snmpwalk, $cpqHeResMemModuleEntry) == 1) {
    $repaircondition = lc SNMP::Utils::get_object(
        $snmpwalk, $cpqHeResMemModuleEntry.'.1.0.'.scalar(@{$self->{si_dimms}}));
    # repaircondition 0 (ok) biegt alles wieder gerade
  } else { 
    # anderer versuch
    if ($self->{si_overall_condition} &&
        $self->{si_overall_condition} eq 'ok') {
      $repaircondition = 0;
    }
  }
  foreach my $si_dimm (@{$self->{si_dimms}}) {
    if (($si_dimm->{condition} eq 'n/a') ||
        ($si_dimm->{condition} eq 'other')) {
      $si_dimm->{condition} = 'ok' if
          (defined $repaircondition && $repaircondition == 0);
    }
    push(@dimms,
        HP::Proliant::Component::MemorySubsystem::Dimm->new(
            runtime => $si_dimm->{runtime},
            cartridge => $si_dimm->{cartridge},
            module => $si_dimm->{module},
            size => $si_dimm->{size},
            status => $si_dimm->{status},
            condition => $si_dimm->{condition},
    ));
  }
  return @dimms;
}

sub update_si_with_he {
  my $self = shift;
  my @dimms = ();
  my $first_si_cartridge = ($self->get_si_boards())[0];
  my $first_he_cartridge = ($self->get_he_boards())[0];
  my $offset = 0;
  if (scalar(@{$self->{si_dimms}}) == scalar(@{$self->{he_dimms}})) {
    # aufpassen! sowas kann vorkommen: si cartridge 0...6, he cartridge 1...7
    if ($first_si_cartridge != $first_he_cartridge) {
      # README case 5
      $offset = $first_si_cartridge - $first_he_cartridge;
    }
  } elsif ((scalar(@{$self->{si_dimms}}) > 1) && 
      (scalar(@{$self->{he_dimms}}) == 1)) {
    # siehe update_si_with_si. he-fragment
    return $self->update_si_with_si();
  } else { 
    # z.b. 4 si notpresent, 4 si present, 4 he
  }
  foreach my $si_dimm (@{$self->{si_dimms}}) {
    if (($si_dimm->{condition} eq 'n/a') || 
        ($si_dimm->{condition} eq 'other')) {
      if (my $he_dimm = $self->get_he_module(
          $si_dimm->{cartridge} - $offset, $si_dimm->{module})) {
        # vielleicht hat he mehr ahnung
        $si_dimm->{condition} = $he_dimm->{condition};
        if (($si_dimm->{condition} eq 'n/a') || 
            ($si_dimm->{condition} eq 'other')) {
          # wenns immer noch kein brauchbares ergebnis gibt....
          if ($self->{he_overall_condition} &&
              $self->{he_overall_condition} eq 'ok') {
            # wird schon stimmen...
            $si_dimm->{condition} = 'ok';
          } else {
            # ansonsten stellen wir uns dumm
            $si_dimm->{status} = 'notPresent';
          }
        }
      } else {
        # in dem fall zeigt si unbestueckte cartridges an
      }
    }
    push(@dimms,
        HP::Proliant::Component::MemorySubsystem::Dimm->new(
            runtime => $si_dimm->{runtime},
            cartridge => $si_dimm->{cartridge},
            module => $si_dimm->{module},
            size => $si_dimm->{size},
            status => $si_dimm->{status},
            condition => $si_dimm->{condition},
    ));
  }
  return @dimms;
}

sub update_he_with_he {
  my $self = shift;
  my @dimms = ();
  foreach my $he_dimm (@{$self->{he_dimms}}) {
    push(@dimms,
        HP::Proliant::Component::MemorySubsystem::Dimm->new(
            runtime => $he_dimm->{runtime},
            cartridge => $he_dimm->{cartridge},
            module => $he_dimm->{module},
            size => $he_dimm->{size},
            status => $he_dimm->{status},
            condition => $he_dimm->{condition},
    ));
  }
  return @dimms;
}

sub update_si_with_h2 {
  my $self = shift;
  my @dimms = ();
  my $first_si_cartridge = ($self->get_si_boards())[0];
  my $first_h2_cartridge = ($self->get_h2_boards())[0];
  my $offset = 0;
  if (scalar(@{$self->{si_dimms}}) == scalar(@{$self->{h2_dimms}})) {
    # aufpassen! sowas kann vorkommen: si cartridge 0...6, he cartridge 1...7
    if ($first_si_cartridge != $first_h2_cartridge) {
      # README case 5
      $offset = $first_si_cartridge - $first_h2_cartridge;
    }
  } else { 
    # z.b. 4 si notpresent, 4 si present, 4 he
  }
  foreach my $si_dimm (@{$self->{si_dimms}}) {
    if (($si_dimm->{condition} eq 'n/a') || 
        ($si_dimm->{condition} eq 'other')) {
      if (my $h2_dimm = $self->get_h2_module(
          $si_dimm->{cartridge} - $offset, $si_dimm->{module})) {
        # vielleicht hat h2 mehr ahnung
        $si_dimm->{condition} = $h2_dimm->{condition};
        if (1) {
          # ist zwar da, aber irgendwie auskonfiguriert
          $si_dimm->{status} = 'notPresent' if $h2_dimm->{status} eq 'other';
        }
      } else {
        # in dem fall zeigt si unbestueckte cartridges an
      }
    }
    push(@dimms,
        HP::Proliant::Component::MemorySubsystem::Dimm->new(
            runtime => $si_dimm->{runtime},
            cartridge => $si_dimm->{cartridge},
            module => $si_dimm->{module},
            size => $si_dimm->{size},
            status => $si_dimm->{status},
            condition => $si_dimm->{condition},
    ));
  }
  return @dimms;
}

sub update_he_with_h2 {
  my $self = shift;
  my @dimms = ();
  my $first_he_cartridge = ($self->get_he_boards())[0];
  my $first_h2_cartridge = ($self->get_h2_boards())[0];
  my $offset = 0;
  # auch hier koennte sowas u.u.vorkommen: he cartridge 0..6, h2 cartridge 1..7
  # ich habs zwar nie gesehen, aber wer weiss...
  if ($first_h2_cartridge != $first_he_cartridge) {
    $offset = $first_h2_cartridge - $first_he_cartridge;
  }
  foreach my $he_dimm (@{$self->{he_dimms}}) {
    if (($he_dimm->{condition} eq 'n/a') || 
        ($he_dimm->{condition} eq 'other')) {
      if (my $h2_dimm = $self->get_h2_module(
          $he_dimm->{cartridge} + $offset, $he_dimm->{module})) {
        # vielleicht hat h2 mehr ahnung
        $he_dimm->{condition} = $h2_dimm->{condition};
        if (1) {
          # ist zwar da, aber irgendwie auskonfiguriert
          $he_dimm->{status} = 'notPresent' if $h2_dimm->{status} eq 'other';
        }
      } else {
        # in dem fall weiss he mehr als h2
      }
    }
    if ($he_dimm->{size} == 0) {
      if (my $h2_dimm = $self->get_h2_module(
          $he_dimm->{cartridge} + $offset, $he_dimm->{module})) {
        $he_dimm->{size} = $h2_dimm->{size};
        # h2 beinhaltet eine size-oid
      }
    }
    push(@dimms,
        HP::Proliant::Component::MemorySubsystem::Dimm->new(
            runtime => $he_dimm->{runtime},
            cartridge => $he_dimm->{cartridge},
            module => $he_dimm->{module},
            size => $he_dimm->{size},
            status => $he_dimm->{status},
            condition => $he_dimm->{condition},
    ));
  }
  return @dimms;
}

sub update_h2_with_h2 {
  my $self = shift;
  my @dimms = ();
  foreach my $h2_dimm (@{$self->{h2_dimms}}) {
    push(@dimms,
        HP::Proliant::Component::MemorySubsystem::Dimm->new(
            runtime => $h2_dimm->{runtime},
            cartridge => $h2_dimm->{cartridge},
            module => $h2_dimm->{module},
            size => $h2_dimm->{size},
            status => $h2_dimm->{status},
            condition => $h2_dimm->{condition},
    ));
  }
  return @dimms;
}

sub is_faulty {
  my $self = shift;
  if (scalar(@{$self->{si_dimms}}) > 0 && 
      scalar(@{$self->{he_dimms}}) > 0) {
    return $self->si_is_faulty() || $self->he_is_faulty();
  } elsif (scalar(@{$self->{si_dimms}}) > 0 &&
        scalar(@{$self->{he_dimms}}) == 0) {
    return $self->si_is_faulty();
  } elsif (scalar(@{$self->{si_dimms}}) == 0 &&
        scalar(@{$self->{he_dimms}}) > 0) {
    return $self->he_is_faulty();
  } else {
    return 0;
  }
}

sub si_is_faulty {
  my $self = shift;
  return ! defined $self->{si_overall_condition} ? 0 :
      $self->{si_overall_condition} eq 'degraded' ? 1 : 0;
}

sub si_is_ok {
  my $self = shift;
  return ! defined $self->{si_overall_condition} ? 1 :
      $self->{si_overall_condition} eq 'ok' ? 1 : 0;
}

sub he_is_faulty {
  my $self = shift;
  return ! defined $self->{he_overall_condition} ? 0 :
      $self->{he_overall_condition} eq 'degraded' ? 1 : 0;
}

sub he_is_ok {
  my $self = shift;
  return ! defined $self->{he_overall_condition} ? 1 :
      $self->{he_overall_condition} eq 'ok' ? 1 : 0;
}

sub get_si_boards {
  my $self = shift;
  my %found = ();
  foreach (@{$self->{si_dimms}}) {
    $found{$_->{cartridge}} = 1;
  }
  return sort { $a <=> $b } keys %found;
}

sub get_si_modules {
  my $self = shift;
  my $board = shift;
  my %found = ();
  foreach (grep { $_->{cartridge} == $board } @{$self->{si_dimms}}) {
    $found{$_->{module}} = 1;
  }
  return sort { $a <=> $b } keys %found;
}

sub get_he_boards {
  my $self = shift;
  my %found = ();
  foreach (@{$self->{he_dimms}}) {
    $found{$_->{cartridge}} = 1;
  }
  return sort { $a <=> $b } keys %found;
}

sub get_he_modules {
  my $self = shift;
  my $board = shift;
  my %found = ();
  foreach (grep { $_->{cartridge} == $board } @{$self->{he_dimms}}) {
    $found{$_->{module}} = 1;
  }
  return sort { $a <=> $b } keys %found;
}

sub get_he_module {
  my $self = shift;
  my $board = shift;
  my $module = shift;
  my $found = (grep { $_->{cartridge} == $board && $_->{module} == $module } 
      @{$self->{he_dimms}})[0];
  return $found;
}

sub get_h2_boards {
  my $self = shift;
  my %found = ();
  # 
  foreach (@{$self->{h2_dimms}}) {
    $found{$_->{cartridge}} = 1;
  }
  return sort { $a <=> $b } keys %found;
}

sub get_h2_modules {
  my $self = shift;
  my $board = shift;
  my %found = ();
  foreach (grep { $_->{cartridge} == $board } @{$self->{h2_dimms}}) {
    $found{$_->{module}} = 1;
  }
  return sort { $a <=> $b } keys %found;
}

sub get_h2_module {
  my $self = shift;
  my $board = shift;
  my $module = shift;
  my $found = (grep { $_->{cartridge} == $board && $_->{module} == $module } 
      @{$self->{h2_dimms}})[0];
  return $found;
}


package HP::Proliant::Component::MemorySubsystem;
our @ISA = qw(HP::Proliant::Component);

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

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    runtime => $params{runtime},
    rawdata => $params{rawdata},
    method => $params{method},
    condition => $params{condition},
    status => $params{status},
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
    dimms => [],
  };
  bless $self, $class;
  if ($self->{method} eq 'snmp') {
    return HP::Proliant::Component::MemorySubsystem::SNMP->new(%params);
  } elsif ($self->{method} eq 'cli') {
    return HP::Proliant::Component::MemorySubsystem::CLI->new(%params);
  } else {
    die "unknown method";
  }
}

sub check {
  my $self = shift;
  my $errorfound = 0;
  $self->add_info('checking memory');
  foreach (@{$self->{dimms}}) {
    $_->check(); # info ausfuellen
  }
  if ((scalar(grep {
      $_->is_present() && 
      ($_->{condition} ne 'n/a' && $_->{condition} ne 'other' ) 
  } @{$self->{dimms}})) != 0) {
    foreach (@{$self->{dimms}}) {
      if (($_->is_present()) && ($_->{condition} ne 'ok')) {
        $_->add_message(CRITICAL, $_->{info});
        $errorfound++;
      }
    }
  } else {
    if ($self->{runtime}->{options}->{ignore_dimms}) {
      $self->add_message(OK,
          sprintf "ignoring %d dimms with status 'n/a' ",
          scalar(grep { ($_->is_present()) } @{$self->{dimms}}));
    } elsif ($self->{runtime}->{options}->{buggy_firmware}) {
      $self->add_message(OK,
          sprintf "ignoring %d dimms with status 'n/a' because of buggy firmware",
          scalar(grep { ($_->is_present()) } @{$self->{dimms}}));
    } else {
      $self->add_message(WARNING,
        sprintf "status of all %d dimms is n/a (please upgrade firmware)",
        scalar(grep { $_->is_present() } @{$self->{dimms}}));
        $errorfound++;
    }
  }
  foreach (@{$self->{dimms}}) {
    printf "%s\n", $_->{info} if $self->{runtime}->{options}->{verbose} >= 2;
  }
  if (! $errorfound && $self->is_faulty()) {
  #if ($self->is_faulty()) {
    $self->add_message(WARNING,
        sprintf 'overall memory error found');
  }
}

sub dump {
  my $self = shift;
 printf "i dump the memory\n";
  foreach (@{$self->{dimms}}) {
    $_->dump();
  }
}

package HP::Proliant::Component::MemorySubsystem::Dimm;
our @ISA = qw(HP::Proliant::Component::MemorySubsystem);

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

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    runtime => $params{runtime},
    cartridge => $params{cartridge},
    module => $params{module},
    size => $params{size} || 0,
    status => $params{status},
    condition => $params{condition},
    type => $params{type},
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  bless $self, $class;
  $self->{name} = sprintf '%s:%s',
      $self->{cartridge}, $self->{module};
  $self->{location} = sprintf 'module %s @ cartridge %s',
      $self->{module}, $self->{cartridge};
  return $self;
}  

sub check {
  my $self = shift;
  # check dient nur dazu, info und extended_info zu fuellen
  # die eigentliche bewertung findet eins hoeher statt
  $self->blacklist('d', $self->{name});
  if (($self->{status} eq 'present') || ($self->{status} eq 'good')) {
    if ($self->{condition} eq 'other') {
      $self->add_info(sprintf 'dimm %s (%s) is n/a',
          $self->{name}, $self->{location});
    } elsif ($self->{condition} ne 'ok') {
      $self->add_info(
        sprintf "dimm module %s (%s) needs attention (%s)",
        $self->{name}, $self->{location}, $self->{condition});
    } else {
      $self->add_info(sprintf 'dimm module %s (%s) is %s',
          $self->{name}, $self->{location}, $self->{condition});
    }
  } elsif ($self->{status} eq 'notPresent') {
    $self->add_info(sprintf 'dimm module %s (%s) is not present',
        $self->{name}, $self->{location});
  } else {
    $self->add_info(
      sprintf "dimm module %s (%s) needs attention (%s)",
      $self->{name}, $self->{location}, $self->{condition});
  }
}

sub is_present {
  my $self = shift;
  my @signs_of_presence = (qw(present good add upgraded doesnotmatch 
      notsupported badconfig degraded));
  return scalar(grep { $self->{status} eq $_ } @signs_of_presence);
}


sub dump {
  my $self = shift;
  #printf "[DIMM_%s_%s]\n", $self->{cartridge}, $self->{module};
  #foreach (qw(cartridge module size status condition info)) {
  #  printf "%s: %s\n", $_, $self->{$_};
  #}
  #printf "status: %s\n", $self->{status} if exists $self->{status};
  #printf "\n";
  printf "car %02d  mod %02d  siz %.0f  sta %-12s  con %-10s  typ %s\n",
    $self->{cartridge}, $self->{module}, $self->{size}, 
    $self->{status}, $self->{condition}, defined $self->{type} ? $self->{type} : "";
}


package HP::Proliant::Component::MemorySubsystem::Cartridge;
our @ISA = qw(HP::Proliant::Component::MemorySubsystem);

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

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    cpqHeResMemBoardSlotIndex => $params{cpqHeResMemBoardSlotIndex},
    cpqHeResMemBoardOnlineStatus => $params{cpqHeResMemBoardOnlineStatus},
    cpqHeResMemBoardErrorStatus => $params{cpqHeResMemBoardErrorStatus},
    cpqHeResMemBoardNumSockets => $params{cpqHeResMemBoardNumSockets},
    cpqHeResMemBoardOsMemSize => $params{cpqHeResMemBoardOsMemSize},
    cpqHeResMemBoardTotalMemSize => $params{cpqHeResMemBoardTotalMemSize},
    cpqHeResMemBoardCondition => $params{cpqHeResMemBoardCondition},
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  bless $self, $class;
  return $self;
}  

sub dump {
  my $self = shift;
  #printf "[CARTRIDGE_%s_%s]\n", $self->{cpqHeResMemBoardSlotIndex};
  #foreach (qw(cpqHeResMemBoardSlotIndex cpqHeResMemBoardOnlineStatus
  #    cpqHeResMemBoardErrorStatus cpqHeResMemBoardNumSockets
  #    cpqHeResMemBoardOsMemSize cpqHeResMemBoardTotalMemSize
  #    cpqHeResMemBoardCondition)) {
  #  printf "%s: %s\n", $_, $self->{$_};
  #}
  #printf "\n";
}


package HP::Proliant::Component::NicSubsystem::SNMP;
our @ISA = qw(HP::Proliant::Component::NicSubsystem
    HP::Proliant::Component::SNMP);

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

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

sub overall_init {
  my $self = shift;
  my %params = @_;
  my $snmpwalk = $self->{rawdata};
  # overall
  my $cpqNicIfLogMapOverallCondition  = '1.3.6.1.4.1.232.18.2.2.2.0';
  my $cpqNicIfLogMapOverallConditionValue = {
    1 => 'other',
    2 => 'ok',
    3 => 'degraded',
    4 => 'failed',
  };
  $self->{lognicstatus} = SNMP::Utils::get_object_value(
      $snmpwalk, $cpqNicIfLogMapOverallCondition,
      $cpqNicIfLogMapOverallConditionValue);
}

sub init {
  my $self = shift;
  my $snmpwalk = $self->{rawdata};
  my $ifconnect = {};
  # CPQNIC-MIB
  my $oids = {
      cpqNicIfLogMapEntry => '1.3.6.1.4.1.232.18.2.2.1.1',
      cpqNicIfLogMapIndex => '1.3.6.1.4.1.232.18.2.2.1.1.1',
      cpqNicIfLogMapIfNumber => '1.3.6.1.4.1.232.18.2.2.1.1.2',
      cpqNicIfLogMapDescription => '1.3.6.1.4.1.232.18.2.2.1.1.3',
      cpqNicIfLogMapGroupType => '1.3.6.1.4.1.232.18.2.2.1.1.4',
      cpqNicIfLogMapAdapterCount => '1.3.6.1.4.1.232.18.2.2.1.1.5',
      cpqNicIfLogMapAdapterOKCount => '1.3.6.1.4.1.232.18.2.2.1.1.6',
      cpqNicIfLogMapPhysicalAdapters => '1.3.6.1.4.1.232.18.2.2.1.1.7',
      cpqNicIfLogMapMACAddress => '1.3.6.1.4.1.232.18.2.2.1.1.8',
      cpqNicIfLogMapSwitchoverMode => '1.3.6.1.4.1.232.18.2.2.1.1.9',
      cpqNicIfLogMapCondition => '1.3.6.1.4.1.232.18.2.2.1.1.10',
      cpqNicIfLogMapStatus => '1.3.6.1.4.1.232.18.2.2.1.1.11',
      cpqNicIfLogMapNumSwitchovers => '1.3.6.1.4.1.232.18.2.2.1.1.12',
      cpqNicIfLogMapHwLocation => '1.3.6.1.4.1.232.18.2.2.1.1.13',
      cpqNicIfLogMapSpeed => '1.3.6.1.4.1.232.18.2.2.1.1.14',
      cpqNicIfLogMapVlanCount => '1.3.6.1.4.1.232.18.2.2.1.1.15',
      cpqNicIfLogMapVlans => '1.3.6.1.4.1.232.18.2.2.1.1.16',

      cpqNicIfLogMapGroupTypeValue => {
          1 => "unknown",
          2 => "none",
          3 => "redundantPair",
          4 => "nft",
          5 => "alb",
          6 => "fec",
          7 => "gec",
          8 => "ad",
          9 => "slb",
          10 => "tlb",
          11 => "redundancySet",
      },
      cpqNicIfLogMapConditionValue => {
          1 => "other",
          2 => "ok",
          3 => "degraded",
          4 => "failed",
      },
      cpqNicIfLogMapStatusValue => {
          1 => "unknown",
          2 => "ok",
          3 => "primaryFailed",
          4 => "standbyFailed",
          5 => "groupFailed",
          6 => "redundancyReduced",
          7 => "redundancyLost",
      },
      cpqNicIfLogMapSwitchoverModeValue => {
          1 => "unknown",
          2 => "none",
          3 => "manual",
          4 => "switchOnFail",
          5 => "preferredPrimary",
      },
  };

  # INDEX { cpqNicIfLogMapIndex }
  foreach ($self->get_entries($oids, 'cpqNicIfLogMapEntry')) {
    push(@{$self->{logical_nics}}, 
        HP::Proliant::Component::NicSubsystem::LogicalNic->new(%{$_})
    );
  }

  $oids = {
      cpqNicIfPhysAdapterEntry => '1.3.6.1.4.1.232.18.2.3.1.1',
      cpqNicIfPhysAdapterIndex => '1.3.6.1.4.1.232.18.2.3.1.1.1',
      cpqNicIfPhysAdapterIfNumber => '1.3.6.1.4.1.232.18.2.3.1.1.2',
      cpqNicIfPhysAdapterRole => '1.3.6.1.4.1.232.18.2.3.1.1.3',
      cpqNicIfPhysAdapterMACAddress => '1.3.6.1.4.1.232.18.2.3.1.1.4',
      cpqNicIfPhysAdapterSlot => '1.3.6.1.4.1.232.18.2.3.1.1.5',
      cpqNicIfPhysAdapterIoAddr => '1.3.6.1.4.1.232.18.2.3.1.1.6',
      cpqNicIfPhysAdapterIrq => '1.3.6.1.4.1.232.18.2.3.1.1.7',
      cpqNicIfPhysAdapterDma => '1.3.6.1.4.1.232.18.2.3.1.1.8',
      cpqNicIfPhysAdapterMemAddr => '1.3.6.1.4.1.232.18.2.3.1.1.9',
      cpqNicIfPhysAdapterPort => '1.3.6.1.4.1.232.18.2.3.1.1.10',
      cpqNicIfPhysAdapterDuplexState => '1.3.6.1.4.1.232.18.2.3.1.1.11',
      cpqNicIfPhysAdapterCondition => '1.3.6.1.4.1.232.18.2.3.1.1.12',
      cpqNicIfPhysAdapterState => '1.3.6.1.4.1.232.18.2.3.1.1.13',
      cpqNicIfPhysAdapterStatus => '1.3.6.1.4.1.232.18.2.3.1.1.14',
      cpqNicIfPhysAdapterStatsValid => '1.3.6.1.4.1.232.18.2.3.1.1.15',
      cpqNicIfPhysAdapterGoodTransmits => '1.3.6.1.4.1.232.18.2.3.1.1.16',
      cpqNicIfPhysAdapterGoodReceives => '1.3.6.1.4.1.232.18.2.3.1.1.17',
      cpqNicIfPhysAdapterBadTransmits => '1.3.6.1.4.1.232.18.2.3.1.1.18',
      cpqNicIfPhysAdapterBadReceives => '1.3.6.1.4.1.232.18.2.3.1.1.19',
      cpqNicIfPhysAdapterAlignmentErrors => '1.3.6.1.4.1.232.18.2.3.1.1.20',
      cpqNicIfPhysAdapterFCSErrors => '1.3.6.1.4.1.232.18.2.3.1.1.21',
      cpqNicIfPhysAdapterSingleCollisionFrames => '1.3.6.1.4.1.232.18.2.3.1.1.22',
      cpqNicIfPhysAdapterMultipleCollisionFrames => '1.3.6.1.4.1.232.18.2.3.1.1.23',
      cpqNicIfPhysAdapterDeferredTransmissions => '1.3.6.1.4.1.232.18.2.3.1.1.24',
      cpqNicIfPhysAdapterLateCollisions => '1.3.6.1.4.1.232.18.2.3.1.1.25',
      cpqNicIfPhysAdapterExcessiveCollisions => '1.3.6.1.4.1.232.18.2.3.1.1.26',
      cpqNicIfPhysAdapterInternalMacTransmitErrors => '1.3.6.1.4.1.232.18.2.3.1.1.27',
      cpqNicIfPhysAdapterCarrierSenseErrors => '1.3.6.1.4.1.232.18.2.3.1.1.28',
      cpqNicIfPhysAdapterFrameTooLongs => '1.3.6.1.4.1.232.18.2.3.1.1.29',
      cpqNicIfPhysAdapterInternalMacReceiveErrors => '1.3.6.1.4.1.232.18.2.3.1.1.30',
      cpqNicIfPhysAdapterHwLocation => '1.3.6.1.4.1.232.18.2.3.1.1.31',
      cpqNicIfPhysAdapterPartNumber => '1.3.6.1.4.1.232.18.2.3.1.1.32',
      cpqNicIfPhysAdapterRoleValue => {
          1 => "unknown",
          2 => "primary",
          3 => "secondary",
          4 => "member",
          5 => "txRx",
          6 => "tx",
          7 => "standby",
          8 => "none",
          255 => "notApplicable",
      },
      cpqNicIfPhysAdapterDuplexStateValue => {
          1 => "unknown",
          2 => "half",
          3 => "full",
      },
      cpqNicIfPhysAdapterConditionValue => {
          1 => "other",
          2 => "ok",
          3 => "degraded",
          4 => "failed",
      },
      cpqNicIfPhysAdapterStateValue => {
          1 => "unknown",
          2 => "ok",
          3 => "standby",
          4 => "failed",
      },
      cpqNicIfPhysAdapterStatusValue => {
          1 => "unknown",
          2 => "ok",
          3 => "generalFailure",
          4 => "linkFailure",
      },

  };
  # INDEX { cpqNicIfPhysAdapterIndex }
  foreach ($self->get_entries($oids, 'cpqNicIfPhysAdapterEntry')) {
    push(@{$self->{physical_nics}},
        HP::Proliant::Component::NicSubsystem::PhysicalNic->new(%{$_}));
  }

}


package HP::Proliant::Component::NicSubsystem;
our @ISA = qw(HP::Proliant::Component);

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

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    runtime => $params{runtime},
    rawdata => $params{rawdata},
    method => $params{method},
    condition => $params{condition},
    status => $params{status},
    logical_nics => [],
    physical_nics => [],
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  bless $self, $class;
  if ($self->{method} eq 'snmp') {
    return HP::Proliant::Component::NicSubsystem::SNMP->new(%params);
  } elsif ($self->{method} eq 'cli') {
    return HP::Proliant::Component::NicSubsystem::CLI->new(%params);
  } else {
    die "unknown method";
  }
  return $self;
}

sub check {
  my $self = shift;
  my $errorfound = 0;
  $self->add_info('checking nic teams');
  if (scalar (@{$self->{logical_nics}}) == 0) {
    $self->add_info('no logical nics found');
    $self->overall_check();
  } else {
    foreach (@{$self->{logical_nics}}) {
      $_->check();
    }
  }
  if (scalar (@{$self->{physical_nics}}) == 0) {
    $self->add_info('no physical nics found. do you connect with slip?');
  } else {
    foreach (@{$self->{physical_nics}}) {
      $_->check();
    }
  }
}

sub num_logical_nics {
  my $self = shift;
  return scalar @{$self->{logical_nics}};
}

sub num_physical_nics {
  my $self = shift;
  return scalar @{$self->{physical_nics}};
}

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

sub overall_check {
  my $self = shift;
  if ($self->{lognicstatus} ne "ok") {
    $self->add_info(sprintf 'overall logical nic status is %s',
        $self->{lognicstatus});
  }
}


package HP::Proliant::Component::NicSubsystem::LogicalNic;
our @ISA = qw(HP::Proliant::Component::NicSubsystem);

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

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    runtime => $params{runtime},
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  foreach (qw(cpqNicIfLogMapIndex cpqNicIfLogMapIfNumber cpqNicIfLogMapDescription cpqNicIfLogMapGroupType cpqNicIfLogMapAdapterCount cpqNicIfLogMapAdapterOKCount cpqNicIfLogMapPhysicalAdapters cpqNicIfLogMapSwitchoverMode cpqNicIfLogMapCondition cpqNicIfLogMapStatus cpqNicIfLogMapNumSwitchovers cpqNicIfLogMapHwLocation cpqNicIfLogMapSpeed cpqNicIfLogMapVlanCount cpqNicIfLogMapVlans)) {
    $self->{$_} = $params{$_};
  }
  bless $self, $class;
  return $self;
}

sub check {
  my $self = shift;
  $self->blacklist('lni', $self->{cpqNicIfLogMapIndex});
  if ($self->{cpqNicIfLogMapAdapterCount} > 0) {
    if ($self->{cpqNicIfLogMapCondition} eq "other") {
      # simply ignore this. if there is a physical nic
      # it is usually unknown/other/scheissegal
      $self->add_info(sprintf "logical nic %d (%s) is %s",
          $self->{cpqNicIfLogMapIndex}, $self->{cpqNicIfLogMapDescription},
          $self->{cpqNicIfLogMapCondition});
    } elsif ($self->{cpqNicIfLogMapCondition} ne "ok") {
      $self->add_info(sprintf "logical nic %d (%s) is %s (%s)",
          $self->{cpqNicIfLogMapIndex}, $self->{cpqNicIfLogMapDescription},
          $self->{cpqNicIfLogMapCondition}, $self->{cpqNicIfLogMapStatus});
      $self->add_message(CRITICAL, $self->{info});
    } else {
      $self->add_info(sprintf "logical nic %d (%s) is %s",
          $self->{cpqNicIfLogMapIndex}, $self->{cpqNicIfLogMapDescription},
          $self->{cpqNicIfLogMapCondition});
    }
  } else {
    $self->add_info(sprintf "logical nic %d (%s) has 0 physical nics",
        $self->{cpqNicIfLogMapIndex}, $self->{cpqNicIfLogMapDescription});
  }
}

sub dump {
  my $self = shift;
  printf "[LNIC_%s]\n", $self->{cpqNicIfLogMapIndex};
  foreach (qw(cpqNicIfLogMapIndex cpqNicIfLogMapIfNumber cpqNicIfLogMapDescription cpqNicIfLogMapAdapterCount cpqNicIfLogMapGroupType cpqNicIfLogMapSwitchoverMode cpqNicIfLogMapCondition cpqNicIfLogMapStatus cpqNicIfLogMapNumSwitchovers cpqNicIfLogMapHwLocation cpqNicIfLogMapSpeed)) {
    printf "%s: %s\n", $_, $self->{$_};
  }
  printf "info: %s\n", $self->{info};
  printf "\n";
}


package HP::Proliant::Component::NicSubsystem::PhysicalNic;
our @ISA = qw(HP::Proliant::Component::NicSubsystem);

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

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    runtime => $params{runtime},
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  foreach (qw(cpqNicIfPhysAdapterIndex cpqNicIfPhysAdapterIfNumber cpqNicIfPhysAdapterRole cpqNicIfPhysAdapterDuplexState cpqNicIfPhysAdapterCondition cpqNicIfPhysAdapterState cpqNicIfPhysAdapterStatus cpqNicIfPhysAdapterBadTransmits cpqNicIfPhysAdapterBadReceives)) {
    $self->{$_} = $params{$_};
  }
  bless $self, $class;
  return $self;
}

sub check {
  my $self = shift;
  $self->blacklist('pni', $self->{cpqNicIfPhysAdapterIndex});
  if ($self->{cpqNicIfPhysAdapterCondition} eq "other") {
    # hp doesnt output a clear status. i am optimistic, unknown/other
    # means "dont care"
    $self->add_info(sprintf "physical nic %d (%s) is %s",
        $self->{cpqNicIfPhysAdapterIndex}, $self->{cpqNicIfPhysAdapterRole},
        $self->{cpqNicIfPhysAdapterCondition});
  } elsif ($self->{cpqNicIfPhysAdapterCondition} ne "ok") {
    $self->add_info(sprintf "physical nic %d (%s) is %s (%s,%s)",
        $self->{cpqNicIfPhysAdapterIndex}, $self->{cpqNicIfPhysAdapterRole},
        $self->{cpqNicIfPhysAdapterCondition},
        $self->{cpqNicIfPhysAdapterState}, $self->{cpqNicIfPhysAdapterStatus});
    $self->add_message(CRITICAL, $self->{info});
  } else {
    if ($self->{cpqNicIfPhysAdapterDuplexState} ne "full") {
      $self->add_info(sprintf "physical nic %d (%s) is %s duplex",
          $self->{cpqNicIfPhysAdapterIndex}, $self->{cpqNicIfPhysAdapterRole},
          $self->{cpqNicIfPhysAdapterDuplexState});
    } else {
      $self->add_info(sprintf "physical nic %d (%s) is %s",
          $self->{cpqNicIfPhysAdapterIndex}, $self->{cpqNicIfPhysAdapterRole},
          $self->{cpqNicIfPhysAdapterCondition});
    }
  }
}

sub dump {
  my $self = shift;
  printf "[PNIC_%s]\n", $self->{cpqNicIfPhysAdapterIndex};
  foreach (qw(cpqNicIfPhysAdapterIndex cpqNicIfPhysAdapterIfNumber cpqNicIfPhysAdapterRole cpqNicIfPhysAdapterDuplexState cpqNicIfPhysAdapterCondition cpqNicIfPhysAdapterState cpqNicIfPhysAdapterStatus cpqNicIfPhysAdapterBadTransmits cpqNicIfPhysAdapterBadReceives)) {
    printf "%s: %s\n", $_, $self->{$_};
  }
  printf "info: %s\n", $self->{info};
  printf "\n";
}


package HP::Proliant::Component::AsrSubsystem::CLI;
our @ISA = qw(HP::Proliant::Component::AsrSubsystem);

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

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

sub init {
  my $self = shift;
  my %params = @_;
}

sub overall_check {
  my $self = shift;
  my %params = @_;
}


package HP::Proliant::Component::AsrSubsystem::SNMP;
our @ISA = qw(HP::Proliant::Component::AsrSubsystem
    HP::Proliant::Component::SNMP);

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

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

sub overall_init {
  my $self = shift;
  my %params = @_;
  my $snmpwalk = $params{rawdata};
  my $cpqHeAsrStatus = "1.3.6.1.4.1.232.6.2.5.1.0";
  my $cpqHeAsrStatusValue = {
    1 => "other",
    2 => "notAvailable",
    3 => "disabled",
    4 => "enabled",
  };
  my $cpqHeAsrCondition = "1.3.6.1.4.1.232.6.2.5.17.0";
  my $cpqHeAsrConditionValue = {
    1 => "other",
    2 => "ok",
    3 => "degraded",
    4 => "failed",
  };
  $self->{asrcondition} = SNMP::Utils::get_object_value(
      $snmpwalk, $cpqHeAsrCondition,
      $cpqHeAsrConditionValue);
  $self->{asrstatus} = SNMP::Utils::get_object_value(
      $snmpwalk, $cpqHeAsrStatus,
      $cpqHeAsrStatusValue);
  $self->{asrcondition} |= lc $self->{asrcondition};
  $self->{asrstatus} |= lc $self->{asrstatus};
}

sub overall_check {
  my $self = shift;
  my $result = 0;
  $self->blacklist('asr', '');
  if ($self->{asrstatus} and $self->{asrstatus} eq "enabled") {
    my $info = sprintf 'ASR overall condition is %s', $self->{asrcondition};
    if ($self->{asrcondition} eq "degraded") {
      $self->add_message(WARNING, $info);
    } elsif ($self->{asrcondition} eq "failed") {
      $self->add_message(CRITICAL, $info);
    }
    $self->add_info($info);
  } else {
    $self->add_info('This system does not have ASR.');
  }
}


package HP::Proliant::Component::AsrSubsystem;
our @ISA = qw(HP::Proliant::Component);

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

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    runtime => $params{runtime},
    rawdata => $params{rawdata},
    method => $params{method},
    condition => $params{condition},
    status => $params{status},
    temperatures => [],
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  bless $self, $class;
  if ($self->{method} eq 'snmp') {
    return HP::Proliant::Component::AsrSubsystem::SNMP->new(%params);
  } elsif ($self->{method} eq 'cli') {
    return HP::Proliant::Component::AsrSubsystem::CLI->new(%params);
  } else {
    die "unknown method";
  }
  return $self;
}

sub check {
  my $self = shift;
  my $errorfound = 0;
  $self->add_info('checking ASR');
  $self->overall_check(); 
}

sub dump {
  my $self = shift;
}




package HP::Proliant::Component::SNMP;

sub get_entries {
  my $self = shift;
  my $oids = shift;
  my $entry = shift;
  my $snmpwalk = $self->{rawdata};
  my @params = ();
  my @indices = SNMP::Utils::get_indices($snmpwalk, $oids->{$entry});
  foreach (@indices) {
    my @idx = @{$_};
    my %params = ( 
      runtime => $self->{runtime},
    );
    my $maxdimension = scalar(@idx) - 1;
    foreach my $idxnr (1..scalar(@idx)) {
      $params{'index'.$idxnr} = $_->[$idxnr - 1];
    }
    foreach my $oid (keys %{$oids}) {
      next if $oid =~ /Entry$/;
      next if $oid =~ /Value$/;
      if (exists $oids->{$oid.'Value'}) {
        $params{$oid} = SNMP::Utils::get_object_value(
            $snmpwalk, $oids->{$oid}, $oids->{$oid.'Value'}, @idx);
        if (! defined  $params{$oid}) {
          my $numerical_value = SNMP::Utils::get_object(
              $snmpwalk, $oids->{$oid}, @idx);
          if (! defined $numerical_value) {
            # maschine liefert schrott
            $params{$oid} = 'value_unknown';
          } else {
            $params{$oid} = 'value_'.SNMP::Utils::get_object(
                $snmpwalk, $oids->{$oid}, @idx);
          }
        }
      } else {  
        $params{$oid} = SNMP::Utils::get_object(
            $snmpwalk, $oids->{$oid}, @idx);
      }         
    }     
    push(@params, \%params);
  }
  return @params;
}

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


package HP::Proliant::Component::DiskSubsystem::Da::CLI;
our @ISA = qw(HP::Proliant::Component::DiskSubsystem::Da);

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

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    controllers => [],
    accelerators => [],
    enclosures => [],
    physical_drives => [],
    logical_drives => [],
    spare_drives => [],
    blacklisted => 0,
  };
  bless $self, $class;
  return $self;
}

sub init {
  my $self = shift;
  my $hpacucli = $self->{rawdata};
  my $slot = 0;
  my $type = "unkn";
  my @lines = ();
  my $thistype = 0;
  my $tmpcntl = {};
  my $tmpaccel = {};
  my $tmpld = {};
  my $tmppd = {};
  my $tmpencl = {};
  my $cntlindex = 0;
  my $enclosureindex = 0;
  my $ldriveindex = 0;
  my $pdriveindex = 0;
  my $incontroller = 0;
  foreach (split(/\n/, $hpacucli)) {
    next unless /^status/;
    next if /^status\s*$/;
    s/^status\s*//;
    if (/(MSA[\s\w]+)\s+in\s+(\w+)/) { 
      $incontroller = 1;
      $slot = $2;
      $cntlindex++;
      $tmpcntl->{$slot}->{cpqDaCntlrIndex} = $cntlindex;
      $tmpcntl->{$slot}->{cpqDaCntlrModel} = $1;
      $tmpcntl->{$slot}->{cpqDaCntlrSlot} = $slot;
    } elsif (/([\s\w]+) in Slot\s+(\d+)/) {
      $incontroller = 1;
      $slot = $2;
      $cntlindex++;
      $tmpcntl->{$slot}->{cpqDaCntlrIndex} = $cntlindex;
      $tmpcntl->{$slot}->{cpqDaCntlrModel} = $1;
      $tmpcntl->{$slot}->{cpqDaCntlrSlot} = $slot;
    } elsif (/Controller Status: (\w+)/) {
      $tmpcntl->{$slot}->{cpqDaCntlrBoardCondition} = lc $1;
      $tmpcntl->{$slot}->{cpqDaCntlrCondition} = lc $1;
    } elsif (/Cache Status: ([\w\s]+?)\s*$/) {
      # Cache Status: OK
      # Cache Status: Not Configured
      # Cache Status: Temporarily Disabled
      $tmpaccel->{$slot}->{cpqDaAccelCntlrIndex} = $cntlindex;
      $tmpaccel->{$slot}->{cpqDaAccelSlot} = $slot;
      #condition: other,ok,degraded,failed
      #status: other,invalid,enabled,tmpDisabled,permDisabled
      $tmpaccel->{$slot}->{cpqDaAccelCondition} = lc $1; 
      if ($tmpaccel->{$slot}->{cpqDaAccelCondition} eq 'ok') {
        $tmpaccel->{$slot}->{cpqDaAccelStatus} = 'enabled';
      } elsif ($tmpaccel->{$slot}->{cpqDaAccelCondition} eq 'not configured') {
        $tmpaccel->{$slot}->{cpqDaAccelCondition} = 'ok';
        $tmpaccel->{$slot}->{cpqDaAccelStatus} = 'enabled';
      } elsif ($tmpaccel->{$slot}->{cpqDaAccelCondition} eq 'temporarily disabled') {
        $tmpaccel->{$slot}->{cpqDaAccelCondition} = 'ok';
        $tmpaccel->{$slot}->{cpqDaAccelStatus} = 'tmpDisabled';
      } elsif ($tmpaccel->{$slot}->{cpqDaAccelCondition} eq 'permanently disabled') {
        $tmpaccel->{$slot}->{cpqDaAccelCondition} = 'ok';
        $tmpaccel->{$slot}->{cpqDaAccelStatus} = 'permDisabled';
      } else {
        $tmpaccel->{$slot}->{cpqDaAccelStatus} = 'enabled';
      }
    } elsif (/Battery.* Status: (\w+)/) {
      # sowas gibts auch Battery/Capacitor Status: OK
      $tmpaccel->{$slot}->{cpqDaAccelBattery} = lc $1;
    } elsif (/^\s*$/) {
    }
  }
  $slot = 0;
  $cntlindex = 0;
  $enclosureindex = 0;
  $ldriveindex = 0;
  $pdriveindex = 0;
  foreach (split(/\n/, $hpacucli)) {
    next unless /^config/;
    next if /^config\s*$/;
    s/^config\s*//;
    if (/(MSA[\s\w]+)\s+in\s+(\w+)/) {
      $slot = $2;
      $cntlindex++;
      $pdriveindex = 1;
    } elsif (/([\s\w]+) in Slot\s+(\d+)/) {
      #if ($slot ne $2 || ! $slot) {
        $cntlindex++;
        # 2012-12-15 das passt nicht zur oberen schleife
        # ich habe keine ahnung, was der hintergrund fuer dieses if ist
      #}
      $slot = $2;
      $pdriveindex = 1;
    } elsif (/([\s\w]+) at Port ([\w]+), Box (\d+), (.*)/) {
      $enclosureindex++;
      $tmpencl->{$slot}->{$enclosureindex}->{cpqDaEnclCntlrIndex} = $cntlindex;
      $tmpencl->{$slot}->{$enclosureindex}->{cpqDaEnclIndex} = $enclosureindex;
      $tmpencl->{$slot}->{$enclosureindex}->{cpqDaEnclPort} = $2;
      $tmpencl->{$slot}->{$enclosureindex}->{cpqDaEnclBox} = $3;
      $tmpencl->{$slot}->{$enclosureindex}->{cpqDaEnclCondition} = $4;
      $tmpencl->{$slot}->{$enclosureindex}->{cpqDaEnclStatus} =
          $tmpencl->{$slot}->{$enclosureindex}->{cpqDaEnclCondition};
      $tmpencl->{$slot}->{$enclosureindex}->{cpqDaLogDrvPhyDrvIDs} = 'unknown';
    } elsif (/logicaldrive\s+(.+?)\s+\((.*)\)/) {
      # logicaldrive 1 (683.5 GB, RAID 5, OK)
      # logicaldrive 1 (683.5 GB, RAID 5, OK)
      # logicaldrive 2 (442 MB, RAID 1+0, OK)
      $ldriveindex = $1;
      $tmpld->{$slot}->{$ldriveindex}->{cpqDaLogDrvCntlrIndex} = $cntlindex;
      $tmpld->{$slot}->{$ldriveindex}->{cpqDaLogDrvIndex} = $ldriveindex;
      ($tmpld->{$slot}->{$ldriveindex}->{cpqDaLogDrvSize},
          $tmpld->{$slot}->{$ldriveindex}->{cpqDaLogDrvFaultTol},
          $tmpld->{$slot}->{$ldriveindex}->{cpqDaLogDrvCondition}) =
          map { lc $_ } split(/,\s*/, $2);
      $tmpld->{$slot}->{$ldriveindex}->{cpqDaLogDrvStatus} =
          $tmpld->{$slot}->{$ldriveindex}->{cpqDaLogDrvCondition};
      $tmpld->{$slot}->{$ldriveindex}->{cpqDaLogDrvPhyDrvIDs} = 'unknown';
    } elsif (/physicaldrive\s+(.+?)\s+\((.*)\)/) {
      # physicaldrive 2:0   (port 2:id 0 , Parallel SCSI, 36.4 GB, OK)
      # physicaldrive 2I:1:6 (port 2I:box 1:bay 6, SAS, 146 GB, OK)
      # physicaldrive 1:1 (box 1:bay 1, Parallel SCSI, 146 GB, OK)
      my $name = $1;
      my($location, $type, $size, $status) = split(/,/, $2);
      $status =~ s/^\s+//g;
      $status =~ s/\s+$//g;
      $status = lc $status;
      my %location = ();
      foreach (split(/:/, $location)) {
        $location{$1} = $2 if /(\w+)\s+(\w+)/;
      }
      $location{box} ||= 0;
      $location{id} ||= $pdriveindex;
      $location{bay} ||= $location{id};
      $location{port} ||= $location{bay};
      $tmppd->{$slot}->{$name}->{name} = lc $name;
      $tmppd->{$slot}->{$name}->{cpqDaPhyDrvCntlrIndex} = $cntlindex;
      $tmppd->{$slot}->{$name}->{cpqDaPhyDrvIndex} = $location{id};
      $tmppd->{$slot}->{$name}->{cpqDaPhyDrvBay} = $location{bay};
      $tmppd->{$slot}->{$name}->{cpqDaPhyDrvBusNumber} = $location{port};
      $tmppd->{$slot}->{$name}->{cpqDaPhyDrvSize} = $size;
      $tmppd->{$slot}->{$name}->{cpqDaPhyDrvStatus} = $status;
      $tmppd->{$slot}->{$name}->{cpqDaPhyDrvCondition} = $status;
      $tmppd->{$slot}->{$name}->{ldriveindex} = $ldriveindex || -1;
      foreach (keys %{$tmppd->{$slot}->{$name}}) {
        $tmppd->{$slot}->{$name}->{$_} =~ s/^\s+//g;
        $tmppd->{$slot}->{$name}->{$_} =~ s/\s+$//g;
        $tmppd->{$slot}->{$name}->{$_} = lc $tmppd->{$slot}->{$name}->{$_};
      }
      $pdriveindex++;
    }
  }

  foreach my $slot (keys %{$tmpcntl}) {
    if (exists $tmpcntl->{$slot}->{cpqDaCntlrModel} &&
        ! $self->identified($tmpcntl->{$slot}->{cpqDaCntlrModel})) {
      delete $tmpcntl->{$slot};
      delete $tmpaccel->{$slot};
      delete $tmpencl->{$slot};
      delete $tmpld->{$slot};
      delete $tmppd->{$slot};
    }
  }

#printf "%s\n", Data::Dumper::Dumper($tmpcntl);
#printf "%s\n", Data::Dumper::Dumper($tmpaccel);
#printf "%s\n", Data::Dumper::Dumper($tmpld);
#printf "%s\n", Data::Dumper::Dumper($tmppd);
  foreach my $slot (sort {
      $tmpcntl->{$a}->{cpqDaCntlrIndex} <=> $tmpcntl->{$b}->{cpqDaCntlrIndex}
      }keys %{$tmpcntl}) {
    $tmpcntl->{$slot}->{runtime} = $self->{runtime};
    push(@{$self->{controllers}},
        HP::Proliant::Component::DiskSubsystem::Da::Controller->new(
            %{$tmpcntl->{$slot}}));
  }
  foreach my $slot (sort {
      $tmpaccel->{$a}->{cpqDaAccelCntlrIndex} <=> $tmpaccel->{$b}->{cpqDaAccelCntlrIndex}
      } keys %{$tmpaccel}) {
    $tmpaccel->{$slot}->{runtime} = $self->{runtime};
    push(@{$self->{accelerators}},
        HP::Proliant::Component::DiskSubsystem::Da::Accelerator->new(
            %{$tmpaccel->{$slot}}));
  }
  foreach my $slot (keys %{$tmpencl}) {
    foreach my $enclosureindex (keys %{$tmpencl->{$slot}}) {
      $tmpencl->{$slot}->{$enclosureindex}->{runtime} = $self->{runtime};
      push(@{$self->{enclosures}},
          HP::Proliant::Component::DiskSubsystem::Da::Enclosure->new(
              %{$tmpencl->{$slot}->{$enclosureindex}}));
    }
  }
  foreach my $slot (keys %{$tmpld}) {
    foreach my $ldriveindex (keys %{$tmpld->{$slot}}) {
      $tmpld->{$slot}->{$ldriveindex}->{runtime} = $self->{runtime};
      push(@{$self->{logical_drives}},
          HP::Proliant::Component::DiskSubsystem::Da::LogicalDrive->new(
              %{$tmpld->{$slot}->{$ldriveindex}}));
    }
    foreach my $pdriveindex (sort {
        (split ':', $a, 2)[0] cmp (split ':', $b, 2)[0] ||
        (split ':', $a, 2)[1] cmp (split ':', $b, 2)[1] ||
        (split ':', $a, 2)[2] <=> (split ':', $b, 2)[2]
        } keys %{$tmppd->{$slot}}) {
      $tmppd->{$slot}->{$pdriveindex}->{runtime} = $self->{runtime};
      push(@{$self->{physical_drives}},
          HP::Proliant::Component::DiskSubsystem::Da::PhysicalDrive->new(
              %{$tmppd->{$slot}->{$pdriveindex}}));
    }
  }
}

sub identified {
  my $self = shift;
  my $info = shift;
  return 1 if $info =~ /Parallel SCSI/;
  return 1 if $info =~ /Smart Array/; # Trond: works fine on E200i, P400, E400
  return 1 if $info =~ /MSA500/;
  #return 1 if $info =~ /Smart Array (5|6)/;
  #return 1 if $info =~ /Smart Array P400i/; # snmp sagt Da, trotz SAS in cli
  #return 1 if $info =~ /Smart Array P410i/; # dto
  return 0;
}
package HP::Proliant::Component::DiskSubsystem::Da::SNMP;
our @ISA = qw(HP::Proliant::Component::DiskSubsystem::Da
    HP::Proliant::Component::SNMP);

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

sub new {
  my $class = shift;
  my %params = @_;
  my $self = { 
    controllers => [],
    accelerators => [],
    enclosures => [],
    physical_drives => [],
    logical_drives => [],
    spare_drives => [],
    blacklisted => 0,
  };
  bless $self, $class;
  return $self;
}

sub init {
  my $self = shift;
  my $snmpwalk = $self->{rawdata};

  # CPQIDA-MIB
  my $oids = {
    cpqDaCntlrEntry => "1.3.6.1.4.1.232.3.2.2.1.1",
    cpqDaCntlrIndex => "1.3.6.1.4.1.232.3.2.2.1.1.1",
    cpqDaCntlrModel => "1.3.6.1.4.1.232.3.2.2.1.1.2",
    cpqDaCntlrSlot => "1.3.6.1.4.1.232.3.2.2.1.1.5",
    cpqDaCntlrCondition => "1.3.6.1.4.1.232.3.2.2.1.1.6",
    cpqDaCntlrBoardCondition => "1.3.6.1.4.1.232.3.2.2.1.1.12",
    cpqDaCntlrModelValue => {
        1 => 'other',
        2 => 'ida',
        3 => 'idaExpansion',
        4 => 'ida-2',
        5 => 'smart',
        6 => 'smart-2e',
        7 => 'smart-2p',
        8 => 'smart-2sl',
        9 => 'smart-3100es',
        10 => 'smart-3200',
        11 => 'smart-2dh',
        12 => 'smart-221',
        13 => 'sa-4250es',
        14 => 'sa-4200',
        15 => 'sa-integrated',
        16 => 'sa-431',
        17 => 'sa-5300',
        18 => 'raidLc2',
        19 => 'sa-5i',
        20 => 'sa-532',
        21 => 'sa-5312',
        22 => 'sa-641',
        23 => 'sa-642',
        24 => 'sa-6400',
        25 => 'sa-6400em',
        26 => 'sa-6i',
    },
    cpqDaCntlrConditionValue => {
        1 => "other",
        2 => "ok",
        3 => "degraded",
        4 => "failed",
    },
    cpqDaCntlrBoardConditionValue => {
        1 => "other",
        2 => "ok",
        3 => "degraded",
        4 => "failed",
    },
  };

  # INDEX { cpqDaCntlrIndex }
  foreach ($self->get_entries($oids, 'cpqDaCntlrEntry')) {
    push(@{$self->{controllers}},
        HP::Proliant::Component::DiskSubsystem::Da::Controller->new(%{$_}));
  }

  $oids = {
      cpqDaAccelEntry => "1.3.6.1.4.1.232.3.2.2.2.1",
      cpqDaAccelCntlrIndex => "1.3.6.1.4.1.232.3.2.2.2.1.1",
      cpqDaAccelStatus => "1.3.6.1.4.1.232.3.2.2.2.1.2",
      cpqDaAccelErrCode  => "1.3.6.1.4.1.232.3.2.2.2.1.5",
      cpqDaAccelBattery  => "1.3.6.1.4.1.232.3.2.2.2.1.6",
      cpqDaAccelCondition  => "1.3.6.1.4.1.232.3.2.2.2.1.9",
      cpqDaAccelBatteryValue => {
          1 => 'other',
          2 => 'ok',
          3 => 'recharging',
          4 => 'failed',
          5 => 'degraded',
          6 => 'notPresent',
      },
      cpqDaAccelConditionValue => {
          1 => "other",
          2 => "ok",
          3 => "degraded",
          4 => "failed",
      },
      cpqDaAccelStatusValue => {
          1 => "other",
          2 => "invalid",
          3 => "enabled",
          4 => "tmpDisabled",
          5 => "permDisabled",
      },
      cpqDaAccelErrCodeValue => {
          1 => 'other',
          2 => 'invalid',
          3 => 'badConfig',
          4 => 'lowBattery',
          5 => 'disableCmd',
          6 => 'noResources',
          7 => 'notConnected',
          8 => 'badMirrorData',
          9 => 'readErr',
          10 => 'writeErr',
          11 => 'configCmd',
          12 => 'expandInProgress',
          13 => 'snapshotInProgress',
          14 => 'redundantLowBattery',
          15 => 'redundantSizeMismatch',
          16 => 'redundantCacheFailure',
          17 => 'excessiveEccErrors',
          18 => 'adgEnablerMissing',
          19 => 'postEccErrors',
          20 => 'batteryHotRemoved',
          21 => 'capacitorChargeLow',
          22 => 'notEnoughBatteries',
          23 => 'cacheModuleNotSupported',
          24 => 'batteryNotSupported',
          25 => 'noCapacitorAttached',
          26 => 'capBasedBackupFailed',
          27 => 'capBasedRestoreFailed',
          28 => 'capBasedModuleHWFailure',
          29 => 'capacitorFailedToCharge',
          30 => 'capacitorBasedHWMemBeingErased',
          31 => 'incompatibleCacheModule',
          32 => 'fbcmChargerCircuitFailure',
      },
  };
    
  # INDEX { cpqDaAccelCntlrIndex }
  foreach ($self->get_entries($oids, 'cpqDaAccelEntry')) {
    push(@{$self->{accelerators}},
        HP::Proliant::Component::DiskSubsystem::Da::Accelerator->new(%{$_}));
  }

  $oids = {
      cpqDaLogDrvEntry => "1.3.6.1.4.1.232.3.2.3.1.1",
      cpqDaLogDrvCntlrIndex => "1.3.6.1.4.1.232.3.2.3.1.1.1",
      cpqDaLogDrvIndex => "1.3.6.1.4.1.232.3.2.3.1.1.2",
      cpqDaLogDrvFaultTol => "1.3.6.1.4.1.232.3.2.3.1.1.3",
      cpqDaLogDrvStatus => "1.3.6.1.4.1.232.3.2.3.1.1.4",
      cpqDaLogDrvSize => "1.3.6.1.4.1.232.3.2.3.1.1.9",
      cpqDaLogDrvPhyDrvIDs => "1.3.6.1.4.1.232.3.2.3.1.1.10",
      cpqDaLogDrvCondition => "1.3.6.1.4.1.232.3.2.3.1.1.11",
      cpqDaLogDrvPercentRebuild => "1.3.6.1.4.1.232.3.2.3.1.1.12",
      cpqDaLogDrvFaultTolValue => {
          1 => "other",
          2 => "none",
          3 => "mirroring",
          4 => "dataGuard",
          5 => "distribDataGuard",
          7 => "advancedDataGuard",
      },
      cpqDaLogDrvConditionValue => {
          1 => "other",
          2 => "ok",
          3 => "degraded",
          4 => "failed",
      },
      cpqDaLogDrvStatusValue => {
          1 => "other",
          2 => "ok",
          3 => "failed",
          4 => "unconfigured",
          5 => "recovering",
          6 => "readyForRebuild",
          7 => "rebuilding",
          8 => "wrongDrive",
          9 => "badConnect",
          10 => "overheating",
          11 => "shutdown",
          12 => "expanding",
          13 => "notAvailable",
          14 => "queuedForExpansion",
      },
  };

  # INDEX { cpqDaLogDrvCntlrIndex, cpqDaLogDrvIndex }
  foreach ($self->get_entries($oids, 'cpqDaLogDrvEntry')) {
    $_->{cpqDaLogDrvPhyDrvIDs} ||= 'empty';
    push(@{$self->{logical_drives}},
        HP::Proliant::Component::DiskSubsystem::Da::LogicalDrive->new(%{$_}));
  }

  $oids = {
      cpqDaPhyDrvEntry => "1.3.6.1.4.1.232.3.2.5.1.1",
      cpqDaPhyDrvCntlrIndex => "1.3.6.1.4.1.232.3.2.5.1.1.1",
      cpqDaPhyDrvIndex => "1.3.6.1.4.1.232.3.2.5.1.1.2",
      cpqDaPhyDrvBay => "1.3.6.1.4.1.232.3.2.5.1.1.5",
      cpqDaPhyDrvStatus => "1.3.6.1.4.1.232.3.2.5.1.1.6",
      cpqDaPhyDrvSize => "1.3.6.1.4.1.232.3.2.5.1.1.9",
      cpqDaPhyDrvCondition => "1.3.6.1.4.1.232.3.2.5.1.1.37",
      cpqDaPhyDrvBusNumber => "1.3.6.1.4.1.232.3.2.5.1.1.50",
      cpqDaPhyDrvConditionValue => {
          1 => "other",
          2 => "ok",
          3 => "degraded",
          4 => "failed",
      },
      cpqDaPhyDrvStatusValue => {
          1 => "other",
          2 => "ok",
          3 => "failed",
          4 => "predictiveFailure",
      },
  };
    
  # INDEX { cpqDaPhyDrvCntlrIndex, cpqDaPhyDrvIndex }
  foreach ($self->get_entries($oids, 'cpqDaPhyDrvEntry')) {
    push(@{$self->{physical_drives}},
        HP::Proliant::Component::DiskSubsystem::Da::PhysicalDrive->new(%{$_}));
  }

}
package HP::Proliant::Component::DiskSubsystem::Da;
our @ISA = qw(HP::Proliant::Component::DiskSubsystem);

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

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    runtime => $params{runtime},
    rawdata => $params{rawdata},
    method => $params{method},
    controllers => [],
    accelerators => [],
    enclosures => [],
    physical_drives => [],
    logical_drives => [],
    spare_drives => [],
    condition => undef,
    blacklisted => 0,
  };
  bless $self, $class;
  if ($self->{method} eq 'snmp') {
    bless $self, 'HP::Proliant::Component::DiskSubsystem::Da::SNMP';
  } else {
    bless $self, 'HP::Proliant::Component::DiskSubsystem::Da::CLI';
  }
  $self->init();
  $self->assemble();
  return $self;
}

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

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

package HP::Proliant::Component::DiskSubsystem::Da::Controller;
our @ISA = qw(HP::Proliant::Component::DiskSubsystem::Da);

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

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    runtime => $params{runtime},
    cpqDaCntlrIndex => $params{cpqDaCntlrIndex},
    cpqDaCntlrSlot => $params{cpqDaCntlrSlot},
    cpqDaCntlrModel => $params{cpqDaCntlrModel},
    cpqDaCntlrCondition => $params{cpqDaCntlrCondition},
    cpqDaCntlrBoardCondition => $params{cpqDaCntlrBoardCondition},
    blacklisted => 0,
  };
  $self->{name} = $params{name} || $self->{cpqDaCntlrSlot};
  $self->{controllerindex} = $self->{cpqDaCntlrIndex};
  bless $self, $class;
  return $self;
}

sub check {
  my $self = shift;
#$self->dumper($self);
  $self->blacklist('daco', $self->{cpqDaCntlrIndex});
  foreach (@{$self->{accelerators}}) {
    $_->check();
  }
  foreach (@{$self->{enclosures}}) {
    $_->check();
  } 
  foreach (@{$self->{logical_drives}}) {
    $_->check();
  } 
  foreach (@{$self->{physical_drives}}) {
    $_->check();
  } 
  foreach (@{$self->{spare_drives}}) {
    $_->check();
  } 
  if ($self->{cpqDaCntlrCondition} eq 'other') {
    if (scalar(@{$self->{physical_drives}})) {
      $self->add_message(CRITICAL,
          sprintf 'da controller %s in slot %s needs attention', 
              $self->{cpqDaCntlrIndex}, $self->{cpqDaCntlrSlot});
      $self->add_info(sprintf 'da controller %s in slot %s needs attention',
          $self->{cpqDaCntlrIndex}, $self->{cpqDaCntlrSlot});
    } else {
      $self->add_info(sprintf 'da controller %s in slot %s is ok and unused',
          $self->{cpqDaCntlrIndex}, $self->{cpqDaCntlrSlot});
      $self->{blacklisted} = 1;
    }
  } elsif ($self->{cpqDaCntlrCondition} eq 'degraded') {
    # maybe only the battery has failed and is disabled, no problem
    if (scalar(grep {
        $_->has_failed() && $_->is_disabled()
    } @{$self->{accelerators}})) {
      # message was already written in the accel code
    } else {
      $self->add_message(CRITICAL,
          sprintf 'da controller %s in slot %s needs attention', 
              $self->{cpqDaCntlrIndex}, $self->{cpqDaCntlrSlot});
      $self->add_info(sprintf 'da controller %s in slot %s needs attention',
          $self->{cpqDaCntlrIndex}, $self->{cpqDaCntlrSlot});
    }
  } elsif ($self->{cpqDaCntlrCondition} ne 'ok') {
    $self->add_message(CRITICAL,
        sprintf 'da controller %s in slot %s needs attention', 
            $self->{cpqDaCntlrIndex}, $self->{cpqDaCntlrSlot});
    $self->add_info(sprintf 'da controller %s in slot %s needs attention',
        $self->{cpqDaCntlrIndex}, $self->{cpqDaCntlrSlot});
  } else {
    $self->add_info(sprintf 'da controller %s in slot %s is ok', 
        $self->{cpqDaCntlrIndex}, $self->{cpqDaCntlrSlot});
  }
} 

sub dump {
  my $self = shift;
  printf "[DA_CONTROLLER_%s]\n", $self->{name};
  foreach (qw(cpqDaCntlrSlot cpqDaCntlrIndex cpqDaCntlrCondition 
      cpqDaCntlrModel)) {
    printf "%s: %s\n", $_, $self->{$_};
  }
  printf "\n";
  foreach (@{$self->{accelerators}}) {
    $_->dump();
  }
  foreach (@{$self->{enclosures}}) {
    $_->dump();
  }
  foreach (@{$self->{logical_drives}}) {
    $_->dump();
  }
  foreach (@{$self->{physical_drives}}) {
    $_->dump();
  }
  foreach (@{$self->{spare_drives}}) {
    $_->dump();
  }
}


package HP::Proliant::Component::DiskSubsystem::Da::Accelerator;
our @ISA = qw(HP::Proliant::Component::DiskSubsystem::Da);

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

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    runtime => $params{runtime},
    cpqDaAccelCntlrIndex => $params{cpqDaAccelCntlrIndex},
    cpqDaAccelBattery => $params{cpqDaAccelBattery} || 'notPresent',
    cpqDaAccelCondition => $params{cpqDaAccelCondition},
    cpqDaAccelStatus => $params{cpqDaAccelStatus},
    cpqDaAccelErrCode => $params{cpqDaAccelErrCode},
    blacklisted => 0,
    failed => 0,
  };
  $self->{controllerindex} = $self->{cpqDaAccelCntlrIndex};
  bless $self, $class;
  return $self;
}

sub check {
  my $self = shift;
  $self->blacklist('daac', $self->{cpqDaAccelCntlrIndex});
  $self->add_info(sprintf 'controller accelerator is %s',
      $self->{cpqDaAccelCondition});
  if ($self->{cpqDaAccelCondition} eq "failed" || $self->{cpqDaAccelCondition} eq "degraded") {
    $self->add_message(CRITICAL, sprintf
        "controller accelerator is %s (reason: %s) and needs attention",
        $self->{cpqDaAccelCondition}, $self->{cpqDaAccelErrCode});
  }
  $self->blacklist('daacb', $self->{cpqDaAccelCntlrIndex});
  $self->add_info(sprintf 'controller accelerator battery is %s',
      $self->{cpqDaAccelBattery});
  if ($self->{cpqDaAccelBattery} eq "notPresent") {
  } elsif ($self->{cpqDaAccelBattery} eq "recharging") {
    $self->add_message(WARNING, "controller accelerator battery recharging");
  } elsif ($self->{cpqDaAccelBattery} eq "failed" &&
      $self->{cpqDaAccelStatus} eq "tmpDisabled") {
    $self->add_message(WARNING, "controller accelerator battery needs attention");
  } elsif ($self->{cpqDaAccelBattery} ne "ok") {
    # (other) failed degraded
    $self->add_message(CRITICAL, "controller accelerator battery needs attention");
  } 
}

sub has_failed {
  my $self = shift;
  return $self->{cpqDaAccelStatus} =~ /Disabled/ ? 1 : 0;
}

sub is_disabled {
  my $self = shift;
  return $self->{cpqDaAccelStatus} =~ /Disabled/ ? 1 : 0;
}

sub dump {
  my $self = shift;
  printf "[ACCELERATOR]\n";
  foreach (qw(cpqDaAccelCntlrIndex cpqDaAccelBattery
      cpqDaAccelStatus cpqDaAccelCondition cpqDaAccelErrCode)) {
    printf "%s: %s\n", $_, $self->{$_};
  }
  printf "\n";
}

package HP::Proliant::Component::DiskSubsystem::Da::Enclosure;
our @ISA = qw(HP::Proliant::Component::DiskSubsystem::Da);

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

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    runtime => $params{runtime},
    name => $params{name},
    cpqDaEnclCntlrIndex => $params{cpqDaEnclCntlrIndex},
    cpqDaEnclIndex => $params{cpqDaEnclIndex},
    cpqDaEnclPort => $params{cpqDaEnclPort},
    cpqDaEnclBox => $params{cpqDaEnclBox},
    cpqDaEnclCondition => $params{cpqDaEnclCondition},
    blacklisted => 0,
  };
  bless $self, $class;
  $self->{name} = $params{name} ||
      $self->{cpqDaEnclPort}.':'.$self->{cpqDaEnclBox}; ##vorerst
  $self->{controllerindex} = $self->{cpqDaEnclCntlrIndex};
  return $self;
}

sub check {
  my $self = shift;
  $self->blacklist('dae', $self->{name});
  $self->add_info(
      sprintf "disk enclosure %s is %s",
          $self->{name}, $self->{cpqDaEnclCondition});
  if ($self->{cpqDaEnclCondition} !~ /^OK/) {
    $self->add_message(CRITICAL,
        sprintf "disk enclosure %s is %s",
            $self->{name}, $self->{cpqDaEnclCondition});
  }
}

sub dump {
  my $self = shift;
  printf "[Disk Enclosure]\n";
  foreach (qw(cpqDaEnclCntlrIndex cpqDaEnclIndex cpqDaEnclPort
		   cpqDaEnclBox cpqDaEnclCondition)) {
    printf "%s: %s\n", $_, $self->{$_};
  }
  printf "\n";
}

package HP::Proliant::Component::DiskSubsystem::Da::LogicalDrive;
our @ISA = qw(HP::Proliant::Component::DiskSubsystem::Da);

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

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    runtime => $params{runtime},
    cpqDaLogDrvIndex => $params{cpqDaLogDrvIndex},
    cpqDaLogDrvCntlrIndex => $params{cpqDaLogDrvCntlrIndex},
    cpqDaLogDrvSize => $params{cpqDaLogDrvSize},
    cpqDaLogDrvFaultTol => $params{cpqDaLogDrvFaultTol},
    cpqDaLogDrvPercentRebuild => $params{cpqDaLogDrvPercentRebuild},
    cpqDaLogDrvStatus => $params{cpqDaLogDrvStatus},
    cpqDaLogDrvCondition => $params{cpqDaLogDrvCondition},
    cpqDaLogDrvPhyDrvIDs => $params{cpqDaLogDrvPhyDrvIDs},
    blacklisted => 0,
  };
  bless $self, $class;
  $self->{name} = $params{name} || 
      $self->{cpqDaLogDrvCntlrIndex}.':'.$self->{cpqDaLogDrvIndex}; ##vorerst
  $self->{controllerindex} = $self->{cpqDaLogDrvCntlrIndex};
  if (! $self->{cpqDaLogDrvPercentRebuild} ||
      $self->{cpqDaLogDrvPercentRebuild} == 4294967295) {
    $self->{cpqDaLogDrvPercentRebuild} = 100;
  }
  return $self;
}

sub check {
  my $self = shift;
  $self->blacklist('dald', $self->{name});
  $self->add_info(sprintf "logical drive %s is %s (%s)",
          $self->{name}, $self->{cpqDaLogDrvStatus},
          $self->{cpqDaLogDrvFaultTol});
  if ($self->{cpqDaLogDrvCondition} ne "ok") {
    if ($self->{cpqDaLogDrvStatus} =~ 
        /rebuild|recovering|recovery|expanding|queued/) {
      $self->add_message(WARNING,
          sprintf "logical drive %s is %s", 
              $self->{name}, $self->{cpqDaLogDrvStatus});
    } else {
      $self->add_message(CRITICAL,
          sprintf "logical drive %s is %s",
              $self->{name}, $self->{cpqDaLogDrvStatus});
    }
  } 
}

sub dump {
  my $self = shift;
  printf "[LOGICAL_DRIVE]\n";
  foreach (qw(cpqDaLogDrvCntlrIndex cpqDaLogDrvIndex cpqDaLogDrvSize
      cpqDaLogDrvFaultTol cpqDaLogDrvStatus cpqDaLogDrvCondition
      cpqDaLogDrvPercentRebuild cpqDaLogDrvPhyDrvIDs)) {
    printf "%s: %s\n", $_, $self->{$_};
  }
  printf "\n";
}


package HP::Proliant::Component::DiskSubsystem::Da::PhysicalDrive;
our @ISA = qw(HP::Proliant::Component::DiskSubsystem::Da);

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

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    runtime => $params{runtime},
    name => $params{name},
    cpqDaPhyDrvCntlrIndex => $params{cpqDaPhyDrvCntlrIndex},
    cpqDaPhyDrvIndex => $params{cpqDaPhyDrvIndex},
    cpqDaPhyDrvBay => $params{cpqDaPhyDrvBay},
    cpqDaPhyDrvBusNumber => $params{cpqDaPhyDrvBusNumber},
    cpqDaPhyDrvSize => $params{cpqDaPhyDrvSize},
    cpqDaPhyDrvStatus => $params{cpqDaPhyDrvStatus},
    cpqDaPhyDrvCondition => $params{cpqDaPhyDrvCondition},
    blacklisted => 0,
  };
  bless $self, $class;
  $self->{name} = $params{name} ||
      $self->{cpqDaPhyDrvCntlrIndex}.':'.$self->{cpqDaPhyDrvIndex}; ##vorerst
  $self->{controllerindex} = $self->{cpqDaPhyDrvCntlrIndex};
  return $self;
}

sub check {
  my $self = shift;
  $self->blacklist('dapd', $self->{name});
  $self->add_info(
      sprintf "physical drive %s is %s",
          $self->{name}, $self->{cpqDaPhyDrvCondition});
  if ($self->{cpqDaPhyDrvCondition} ne 'ok') {
    $self->add_message(CRITICAL,
        sprintf "physical drive %s is %s", 
            $self->{name}, $self->{cpqDaPhyDrvCondition});
  }
}

sub dump {
  my $self = shift;
  printf "[PHYSICAL_DRIVE]\n";
  foreach (qw(cpqDaPhyDrvCntlrIndex cpqDaPhyDrvIndex cpqDaPhyDrvBay
      cpqDaPhyDrvBusNumber cpqDaPhyDrvSize cpqDaPhyDrvStatus
      cpqDaPhyDrvCondition)) {
    printf "%s: %s\n", $_, $self->{$_};
  }
  printf "\n";
}


package HP::Proliant::Component::DiskSubsystem::Da::SpareDrive;
our @ISA = qw(HP::Proliant::Component::DiskSubsystem::Da);

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

sub dump {
  my $self = shift;
  printf "[SPARE_DRIVE]\n";
  foreach (qw(cpqDaPhyDrvCntlrIndex cpqDaPhyDrvIndex cpqDaPhyDrvBay
      cpqDaPhyDrvBusNumber cpqDaPhyDrvSize cpqDaPhyDrvStatus
      cpqDaPhyDrvCondition)) {
    printf "%s: %s\n", $_, $self->{$_};
  }
  printf "\n";
}



package HP::Proliant::Component::DiskSubsystem::Sas::CLI;
our @ISA = qw(HP::Proliant::Component::DiskSubsystem::Sas);

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

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    controllers => [],
    accelerators => [],
    enclosures => [],
    physical_drives => [],
    logical_drives => [],
    spare_drives => [],
    blacklisted => 0,
  };
  bless $self, $class;
  return $self;
}

sub init {
  my $self = shift;
}


package HP::Proliant::Component::DiskSubsystem::Sas::SNMP;
our @ISA = qw(HP::Proliant::Component::DiskSubsystem::Sas
    HP::Proliant::Component::SNMP);

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

sub new {
  my $class = shift;
  my %params = @_;
  my $self = { 
    controllers => [],
    accelerators => [],
    enclosures => [],
    physical_drives => [],
    logical_drives => [],
    spare_drives => [],
    blacklisted => 0,
  };
  bless $self, $class;
  return $self;
}

sub init {
  my $self = shift;
  my $snmpwalk = $self->{rawdata};

  # CPQSCSI-MIB
  my $oids = {
      cpqSasHbaEntry => "1.3.6.1.4.1.232.5.5.1.1.1",
      cpqSasHbaIndex => "1.3.6.1.4.1.232.5.5.1.1.1.1",
      cpqSasHbaLocation => "1.3.6.1.4.1.232.5.5.1.1.1.2",
      cpqSasHbaSlot  => "1.3.6.1.4.1.232.5.5.1.1.1.6",
      cpqSasHbaStatus  => "1.3.6.1.4.1.232.5.5.1.1.1.4",
      cpqSasHbaStatusValue => {
          1 => "other",
          2 => "ok",
          3 => "failed",
      },
      cpqSasHbaCondition  => "1.3.6.1.4.1.232.5.5.1.1.1.5",
      cpqSasHbaConditionValue => {
          1 => "other",
          2 => "ok", 
          3 => "degraded", 
          4 => "failed",
      },
  };

  # INDEX { cpqSasHbaIndex } 
  foreach ($self->get_entries($oids, 'cpqSasHbaEntry')) {
    push(@{$self->{controllers}},
        HP::Proliant::Component::DiskSubsystem::Sas::Controller->new(%{$_}));
  }

  $oids = {
      cpqSasLogDrvEntry => "1.3.6.1.4.1.232.5.5.3.1.1",
      cpqSasLogDrvHbaIndex => "1.3.6.1.4.1.232.5.5.3.1.1.1",
      cpqSasLogDrvIndex => "1.3.6.1.4.1.232.5.5.3.1.1.2",
      cpqSasLogDrvStatus => "1.3.6.1.4.1.232.5.5.3.1.1.4",
      cpqSasLogDrvCondition => "1.3.6.1.4.1.232.5.5.3.1.1.5",
      cpqSasLogDrvRebuildingPercent => "1.3.6.1.4.1.232.5.5.3.1.1.12",
      cpqSasLogDrvRaidLevel => "1.3.6.1.4.1.232.5.5.3.1.1.3",
      cpqSasLogDrvRaidLevelValue => {
          1 => "other",
          2 => "raid0",
          3 => "raid1",
          4 => "raid0plus1",
          5 => "raid5",
          6 => "raid15",
          7 => "volume",
      },
      cpqSasLogDrvConditionValue => {
          1 => "other",
          2 => "ok",
          3 => "degraded",
          4 => "failed",
      },
      cpqSasLogDrvStatusValue => {
          1 => "other",
          2 => "ok",
          3 => "degraded",
          4 => "rebuilding",
          5 => "failed",
          6 => "offline",
      }
  };
  # INDEX { cpqSasLogDrvCntlrIndex, cpqSasLogDrvIndex }
  foreach ($self->get_entries($oids, 'cpqSasLogDrvEntry')) {
    push(@{$self->{logical_drives}},
        HP::Proliant::Component::DiskSubsystem::Sas::LogicalDrive->new(%{$_}));
  }

  $oids = {
      cpqSasPhyDrvEntry => "1.3.6.1.4.1.232.5.5.2.1.1",
      cpqSasPhyDrvHbaIndex => "1.3.6.1.4.1.232.5.5.2.1.1.1",
      cpqSasPhyDrvIndex => "1.3.6.1.4.1.232.5.5.2.1.1.2",
      cpqSasPhyDrvLocationString => "1.3.6.1.4.1.232.5.5.2.1.1.3",
      cpqSasPhyDrvStatus => "1.3.6.1.4.1.232.5.5.2.1.1.5",
      cpqSasPhyDrvSize => "1.3.6.1.4.1.232.5.5.2.1.1.8",
      cpqSasPhyDrvCondition => "1.3.6.1.4.1.232.5.5.2.1.1.6",
      cpqSasPhyDrvConditionValue => {
          1 => "other",
          2 => "ok",
          3 => "degraded",
          4 => "failed",
      },
      cpqSasPhyDrvStatusValue => {
          1 => "other",
          2 => "ok",
          3 => "predictiveFailure",
          4 => "offline",
          5 => "failed",
          6 => "missingWasOk",
          7 => "missingWasPredictiveFailure",
          8 => "missingWasOffline",
          9 => "missingWasFailed",
      },
  };
    
  # INDEX { cpqPhyLogDrvCntlrIndex, cpqSasPhyDrvIndex }
  foreach ($self->get_entries($oids, 'cpqSasPhyDrvEntry')) {
    push(@{$self->{physical_drives}},
        HP::Proliant::Component::DiskSubsystem::Sas::PhysicalDrive->new(%{$_}));
  }

}
package HP::Proliant::Component::DiskSubsystem::Sas;
our @ISA = qw(HP::Proliant::Component::DiskSubsystem);

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

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    runtime => $params{runtime},
    rawdata => $params{rawdata},
    method => $params{method},
    controllers => [],
    enclosures => [],
    physical_drives => [],
    logical_drives => [],
    spare_drives => [],
    condition => undef,
    blacklisted => 0,
  };
  bless $self, $class;
  if ($self->{method} eq 'snmp') {
    bless $self, 'HP::Proliant::Component::DiskSubsystem::Sas::SNMP';
  } else {
    bless $self, 'HP::Proliant::Component::DiskSubsystem::Sas::CLI';
  }
  $self->init();
  $self->assemble();
  return $self;
}

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

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

package HP::Proliant::Component::DiskSubsystem::Sas::Controller;
our @ISA = qw(HP::Proliant::Component::DiskSubsystem::Sas);

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

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    runtime => $params{runtime},
    cpqSasHbaIndex => $params{cpqSasHbaIndex},
    cpqSasHbaLocation => $params{cpqSasHbaLocation},
    cpqSasHbaSlot => $params{cpqSasHbaSlot},
    cpqSasHbaStatus => $params{cpqSasHbaStatus},
    cpqSasHbaCondition => $params{cpqSasHbaCondition},
    blacklisted => 0,
  };
  $self->{name} = $params{name} || $self->{cpqSasHbaSlot};
  $self->{controllerindex} = $self->{cpqSasHbaIndex};
  bless $self, $class;
  return $self;
}

sub check {
  my $self = shift;
  $self->blacklist('saco', $self->{cpqSasHbaSlot});
  if ($self->{cpqSasHbaCondition} eq 'other') {
    if (scalar(@{$self->{physical_drives}})) {
      $self->add_message(CRITICAL,
          sprintf 'sas controller in slot %s needs attention',
              $self->{cpqSasHbaSlot});
      $self->add_info(sprintf 'sas controller in slot %s needs attention',
          $self->{cpqSasHbaSlot});
    } else {
      $self->add_info(sprintf 'sas controller in slot %s is ok and unused',
          $self->{cpqSasHbaSlot});
      $self->{blacklisted} = 1;
    }
  } elsif ($self->{cpqSasHbaCondition} ne 'ok') {
    $self->add_message(CRITICAL,
        sprintf 'sas controller in slot %s needs attention',
            $self->{cpqSasHbaSlot});
    $self->add_info(sprintf 'sas controller in slot %s needs attention',
        $self->{cpqSasHbaSlot});
  } else {
    $self->add_info(sprintf 'sas controller in slot %s is ok',
        $self->{cpqSasHbaSlot});
  }
  foreach (@{$self->{logical_drives}}) {
    $_->check();
  } 
  foreach (@{$self->{physical_drives}}) {
    $_->check();
  } 
  foreach (@{$self->{spare_drives}}) {
    $_->check();
  } 
} 

sub dump {
  my $self = shift;
  printf "[SAS_HBA%s]\n", $self->{name};
  foreach (qw(cpqSasHbaSlot cpqSasHbaIndex cpqSasHbaCondition
      cpqSasHbaStatus cpqSasHbaLocation)) {
    printf "%s: %s\n", $_, $self->{$_};
  }
  printf "\n";
  foreach (@{$self->{logical_drives}}) {
    $_->dump();
  }
  foreach (@{$self->{physical_drives}}) {
    $_->dump();
  }
  foreach (@{$self->{spare_drives}}) {
    $_->dump();
  }
}


package HP::Proliant::Component::DiskSubsystem::Sas::LogicalDrive;
our @ISA = qw(HP::Proliant::Component::DiskSubsystem::Sas);

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

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    runtime => $params{runtime},
    cpqSasLogDrvHbaIndex => $params{cpqSasLogDrvHbaIndex},
    cpqSasLogDrvIndex => $params{cpqSasLogDrvIndex},
    cpqSasLogDrvStatus => $params{cpqSasLogDrvStatus},
    cpqSasLogDrvCondition => $params{cpqSasLogDrvCondition},
    cpqSasLogDrvRebuildingPercent => $params{cpqSasLogDrvRebuildingPercent},
    cpqSasLogDrvRaidLevel => $params{cpqSasLogDrvRaidLevel},
    blacklisted => 0,
  };
  bless $self, $class;
  $self->{name} = $params{name} || 
      $self->{cpqSasLogDrvHbaIndex}.':'.$self->{cpqSasLogDrvIndex}; ####vorerst
  $self->{controllerindex} = $self->{cpqSasLogDrvHbaIndex};
  if (! $self->{cpqSasLogDrvRebuildingPercent} ||
      $self->{cpqSasLogDrvRebuildingPercent} == 4294967295) {
    $self->{cpqSasLogDrvRebuildingPercent} = 100;
  }
  return $self;
}

sub check {
  my $self = shift;
  $self->blacklist('sald', $self->{name});
  if ($self->{cpqSasLogDrvCondition} ne "ok") {
    if ($self->{cpqSasLogDrvStatus} =~ 
        /rebuild|recovering|expanding|queued/) {
      $self->add_message(WARNING,
          sprintf "logical drive %s is %s", 
              $self->{name}, $self->{cpqSasLogDrvStatus});
    } else {
      $self->add_message(CRITICAL,
          sprintf "logical drive %s is %s",
              $self->{name}, $self->{cpqSasLogDrvStatus});
    }
  } 
  $self->add_info(
      sprintf "logical drive %s is %s (%s)", $self->{name},
          $self->{cpqSasLogDrvStatus}, $self->{cpqSasLogDrvRaidLevel});
}

sub dump {
  my $self = shift;
  printf "[LOGICAL_DRIVE]\n";
  foreach (qw(cpqSasLogDrvHbaIndex cpqSasLogDrvIndex cpqSasLogDrvRaidLevel
      cpqSasLogDrvStatus cpqSasLogDrvCondition
      cpqSasLogDrvRebuildingPercent)) {
    printf "%s: %s\n", $_, $self->{$_};
  }
  printf "\n";
}


package HP::Proliant::Component::DiskSubsystem::Sas::PhysicalDrive;
our @ISA = qw(HP::Proliant::Component::DiskSubsystem::Sas);

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

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    runtime => $params{runtime},
    cpqSasPhyDrvHbaIndex => $params{cpqSasPhyDrvHbaIndex},
    cpqSasPhyDrvIndex => $params{cpqSasPhyDrvIndex},
    cpqSasPhyDrvLocationString => $params{cpqSasPhyDrvLocationString},
    cpqSasPhyDrvStatus => $params{cpqSasPhyDrvStatus},
    cpqSasPhyDrvSize => $params{cpqSasPhyDrvSize},
    cpqSasPhyDrvCondition => $params{cpqSasPhyDrvCondition},
    blacklisted => 0,
  };
  $self->{name} = $params{name} || 
      $self->{cpqSasPhyDrvHbaIndex}.':'.$self->{cpqSasPhyDrvIndex}; ####vorerst
  $self->{controllerindex} = $self->{cpqSasPhyDrvHbaIndex};
  bless $self, $class;
  return $self;
}

sub check {
  my $self = shift;
  $self->blacklist('sapd', $self->{name});
  if ($self->{cpqSasPhyDrvCondition} ne 'ok') {
    $self->add_message(CRITICAL,
        sprintf "physical drive %s is %s", 
            $self->{name}, $self->{cpqSasPhyDrvCondition});
  }
  $self->add_info(
      sprintf "physical drive %s is %s", 
          $self->{name}, $self->{cpqSasPhyDrvCondition});
}

sub dump {
  my $self = shift;
  printf "[PHYSICAL_DRIVE]\n";
  foreach (qw(cpqSasPhyDrvHbaIndex cpqSasPhyDrvIndex cpqSasPhyDrvLocationString
      cpqSasPhyDrvSize cpqSasPhyDrvStatus cpqSasPhyDrvCondition)) {
    printf "%s: %s\n", $_, $self->{$_};
  }
  printf "\n";
}


package HP::Proliant::Component::DiskSubsystem::Sas::SpareDrive;
our @ISA = qw(HP::Proliant::Component::DiskSubsystem::Sas);

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

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



package HP::Proliant::Component::DiskSubsystem::Scsi::CLI;
our @ISA = qw(HP::Proliant::Component::DiskSubsystem::Scsi);

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

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    controllers => [],
    accelerators => [],
    enclosures => [],
    physical_drives => [],
    logical_drives => [],
    spare_drives => [],
    blacklisted => 0,
  };
  bless $self, $class;
  return $self;
}

sub init {
  my $self = shift;
}


package HP::Proliant::Component::DiskSubsystem::Scsi::SNMP;
our @ISA = qw(HP::Proliant::Component::DiskSubsystem::Scsi
    HP::Proliant::Component::SNMP);

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

sub new {
  my $class = shift;
  my %params = @_;
  my $self = { 
    controllers => [],
    accelerators => [],
    enclosures => [],
    physical_drives => [],
    logical_drives => [],
    spare_drives => [],
    blacklisted => 0,
  };
  bless $self, $class;
  return $self;
}

sub init {
  my $self = shift;
  my $snmpwalk = $self->{rawdata};

  # CPQSCSI-MIB
  my $oids = {
      cpqScsiCntlrEntry => '1.3.6.1.4.1.232.5.2.2.1.1',
      cpqScsiCntlrIndex => '1.3.6.1.4.1.232.5.2.2.1.1.1',
      cpqScsiCntlrBusIndex => '1.3.6.1.4.1.232.5.2.2.1.1.2',
      cpqScsiCntlrSlot => '1.3.6.1.4.1.232.5.2.2.1.1.6',
      cpqScsiCntlrStatus => '1.3.6.1.4.1.232.5.2.2.1.1.7',
      cpqScsiCntlrCondition => '1.3.6.1.4.1.232.5.2.2.1.1.12',
      cpqScsiCntlrHwLocation => '1.3.6.1.4.1.232.5.2.2.1.1.16',
      cpqScsiCntlrStatusValue => {
          1 => "other",
          2 => "ok",
          3 => "failed",
      },
      cpqScsiCntlrConditionValue => {
          1 => "other",
          2 => "ok",
          3 => "degraded",
          4 => "failed",
      }
  };

  # INDEX { cpqScsiCntlrIndex, cpqScsiCntlrBusIndex }
  foreach ($self->get_entries($oids, 'cpqScsiCntlrEntry')) {
    push(@{$self->{controllers}},
        HP::Proliant::Component::DiskSubsystem::Scsi::Controller->new(%{$_}));
  }

  $oids = {
      cpqScsiLogDrvEntry => '1.3.6.1.4.1.232.5.2.3.1.1',
      cpqScsiLogDrvCntlrIndex => '1.3.6.1.4.1.232.5.2.3.1.1.1',
      cpqScsiLogDrvBusIndex => '1.3.6.1.4.1.232.5.2.3.1.1.2',
      cpqScsiLogDrvIndex => '1.3.6.1.4.1.232.5.2.3.1.1.3',
      cpqScsiLogDrvFaultTol => '1.3.6.1.4.1.232.5.2.3.1.1.4',
      cpqScsiLogDrvStatus => '1.3.6.1.4.1.232.5.2.3.1.1.5',
      cpqScsiLogDrvSize => '1.3.6.1.4.1.232.5.2.3.1.1.6',
      cpqScsiLogDrvPhyDrvIDs => '1.3.6.1.4.1.232.5.2.3.1.1.7',
      cpqScsiLogDrvCondition => '1.3.6.1.4.1.232.5.2.3.1.1.8',
      cpqScsiLogDrvStatusValue => {
          1 => "other",
          2 => "ok",
          3 => "failed",
          4 => "unconfigured",
          5 => "recovering",
          6 => "readyForRebuild",
          7 => "rebuilding",
          8 => "wrongDrive",
          9 => "badConnect",
      },
      cpqScsiLogDrvConditionValue => {
          1 => "other",
          2 => "ok",
          3 => "degraded",
          4 => "failed",
      },
      cpqScsiLogDrvFaultTolValue => {
          1 => "other",
          2 => "none",
          3 => "mirroring",
          4 => "dataGuard",
          5 => "distribDataGuard",
      },

  };
  # INDEX { cpqScsiLogDrvCntlrIndex, cpqScsiLogDrvBusIndex, cpqScsiLogDrvIndex }
  foreach ($self->get_entries($oids, 'cpqScsiLogDrvEntry')) {
    push(@{$self->{logical_drives}},
        HP::Proliant::Component::DiskSubsystem::Scsi::LogicalDrive->new(%{$_}));
  }

  $oids = {
      cpqScsiPhyDrvEntry => '1.3.6.1.4.1.232.5.2.4.1.1',
      cpqScsiPhyDrvCntlrIndex => '1.3.6.1.4.1.232.5.2.4.1.1.1',
      cpqScsiPhyDrvBusIndex => '1.3.6.1.4.1.232.5.2.4.1.1.2',
      cpqScsiPhyDrvIndex => '1.3.6.1.4.1.232.5.2.4.1.1.3',
      cpqScsiPhyDrvStatus => '1.3.6.1.4.1.232.5.2.4.1.1.9',
      cpqScsiPhyDrvSize => '1.3.6.1.4.1.232.5.2.4.1.1.7',
      cpqScsiPhyDrvCondition => '1.3.6.1.4.1.232.5.2.4.1.1.26',
      cpqScsiPhyDrvConditionValue => {
          1 => "other",
          2 => "ok",
          3 => "degraded",
          4 => "failed",
      },
      cpqScsiPhyDrvStatusValue => {
          1 => "other",
          2 => "ok",
          3 => "failed",
          4 => "notConfigured",
          5 => "badCable",
          6 => "missingWasOk",
          7 => "missingWasFailed",
          8 => "predictiveFailure",
          9 => "missingWasPredictiveFailure",
          10 => "offline",
          11 => "missingWasOffline",
          12 => "hardError",
      },
  };
    
  # INDEX { cpqScsiPhyDrvCntlrIndex, cpqScsiPhyDrvBusIndex, cpqScsiPhyDrvIndex }
  foreach ($self->get_entries($oids, 'cpqScsiPhyDrvEntry')) {
    push(@{$self->{physical_drives}},
        HP::Proliant::Component::DiskSubsystem::Scsi::PhysicalDrive->new(%{$_}));
  }

}
package HP::Proliant::Component::DiskSubsystem::Scsi;
our @ISA = qw(HP::Proliant::Component::DiskSubsystem);

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

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    runtime => $params{runtime},
    rawdata => $params{rawdata},
    method => $params{method},
    controllers => [],
    enclosures => [],
    physical_drives => [],
    logical_drives => [],
    spare_drives => [],
    condition => undef,
    blacklisted => 0,
  };
  bless $self, $class;
  if ($self->{method} eq 'snmp') {
    bless $self, 'HP::Proliant::Component::DiskSubsystem::Scsi::SNMP';
  } else {
    bless $self, 'HP::Proliant::Component::DiskSubsystem::Scsi::CLI';
  }
  $self->init();
  $self->assemble();
  return $self;
}

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

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

package HP::Proliant::Component::DiskSubsystem::Scsi::Controller;
our @ISA = qw(HP::Proliant::Component::DiskSubsystem::Scsi);

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

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    runtime => $params{runtime},
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  map { $self->{$_} = $params{$_} } grep /cpqScsiCntlr/, keys %params;
  $self->{name} = $params{name} || $params{cpqScsiCntlrIndex}.':'.$params{cpqScsiCntlrBusIndex};
  $self->{controllerindex} = $self->{cpqScsiCntlrIndex};
  bless $self, $class;
  return $self;
}

sub check {
  my $self = shift;
  $self->blacklist('scco', $self->{name});
  my $info = sprintf 'scsi controller %s in slot %s is %s',
      $self->{name}, $self->{cpqScsiCntlrSlot}, $self->{cpqScsiCntlrCondition};
  if ($self->{cpqScsiCntlrCondition} eq 'other') {
    if (scalar(@{$self->{physical_drives}})) {
      $info .= ' and needs attention';
      $self->add_message(CRITICAL, $info);
      $self->add_info($info);
    } else {
      $info .= ' and unused';
      $self->add_info($info);
      $self->{blacklisted} = 1;
    }
  } elsif ($self->{cpqScsiCntlrCondition} ne 'ok') {
    $info .= ' and needs attention';
    $self->add_message(CRITICAL, $info);
    $self->add_info($info);
  } else {
    $self->add_info($info);
  }
  foreach (@{$self->{logical_drives}}) {
    $_->check();
  } 
  foreach (@{$self->{physical_drives}}) {
    $_->check();
  } 
  foreach (@{$self->{spare_drives}}) {
    $_->check();
  } 
} 

sub dump {
  my $self = shift;
  printf "[SCSI_CONTROLLER_%s]\n", $self->{name};
  foreach (qw(cpqScsiCntlrIndex cpqScsiCntlrBusIndex cpqScsiCntlrSlot
      cpqScsiCntlrStatus cpqScsiCntlrCondition cpqScsiCntlrHwLocation)) {
    printf "%s: %s\n", $_, $self->{$_};
  }
  printf "\n";
  foreach (@{$self->{logical_drives}}) {
    $_->dump();
  }
  foreach (@{$self->{physical_drives}}) {
    $_->dump();
  }
  foreach (@{$self->{spare_drives}}) {
    $_->dump();
  }
}


package HP::Proliant::Component::DiskSubsystem::Scsi::LogicalDrive;
our @ISA = qw(HP::Proliant::Component::DiskSubsystem::Scsi);

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

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    runtime => $params{runtime},
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  map { $self->{$_} = $params{$_} } grep /cpqScsiLogDrv/, keys %params;
  $self->{name} = $params{name} || $params{cpqScsiLogDrvCntlrIndex}.':'.$params{cpqScsiLogDrvBusIndex}.':'.$params{cpqScsiLogDrvIndex};
  bless $self, $class;
  $self->{controllerindex} = $self->{cpqScsiLogDrvCntlrIndex};
  return $self;
}

sub check {
  my $self = shift;
  $self->blacklist('scld', $self->{name});
  my $info = sprintf 'logical drive %s is %s', $self->{name}, $self->{cpqScsiLogDrvStatus};
  if ($self->{cpqScsiLogDrvCondition} ne "ok") {
    if ($self->{cpqScsiLogDrvStatus} =~ 
        /rebuild|recovering/) {
      $self->add_message(WARNING, $info);
    } else {
      $self->add_message(CRITICAL, $info);
    }
  } 
  $self->add_info($info);
}

sub dump {
  my $self = shift;
  printf "[LOGICAL_DRIVE_%s]\n", $self->{name};
  foreach (qw(cpqScsiLogDrvCntlrIndex cpqScsiLogDrvBusIndex cpqScsiLogDrvIndex
      cpqScsiLogDrvFaultTol cpqScsiLogDrvStatus cpqScsiLogDrvSize 
      cpqScsiLogDrvPhyDrvIDs cpqScsiLogDrvCondition)) {
    printf "%s: %s\n", $_, $self->{$_};
  }
  printf "\n";
}


package HP::Proliant::Component::DiskSubsystem::Scsi::PhysicalDrive;
our @ISA = qw(HP::Proliant::Component::DiskSubsystem::Scsi);

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

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    runtime => $params{runtime},
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  map { $self->{$_} = $params{$_} } grep /cpqScsiPhyDrv/, keys %params;
  $self->{name} = $params{name} || 
      $self->{cpqScsiPhyDrvCntlrIndex}.':'.$self->{cpqScsiPhyDrvBusIndex}.':'.$self->{cpqScsiPhyDrvIndex}; 
  $self->{controllerindex} = $self->{cpqScsiPhyDrvCntlrIndex};
  bless $self, $class;
  return $self;
}

sub check {
  my $self = shift;
  $self->blacklist('scpd', $self->{name});
  my $info = sprintf 'physical drive %s is %s', $self->{name}, $self->{cpqScsiPhyDrvCondition};
  if ($self->{cpqScsiPhyDrvCondition} ne 'ok') {
    $self->add_message(CRITICAL, $info);
  }
  $self->add_info($info);
}

sub dump {
  my $self = shift;
  printf "[PHYSICAL_DRIVE_%s]\n", $self->{name};
  foreach (qw(cpqScsiPhyDrvCntlrIndex cpqScsiPhyDrvBusIndex cpqScsiPhyDrvIndex
      cpqScsiPhyDrvStatus cpqScsiPhyDrvSize cpqScsiPhyDrvCondition)) {
    printf "%s: %s\n", $_, $self->{$_};
  }
  printf "\n";
}


package HP::Proliant::Component::DiskSubsystem::Scsi::SpareDrive;
our @ISA = qw(HP::Proliant::Component::DiskSubsystem::Scsi);

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

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



package HP::Proliant::Component::DiskSubsystem::Ide::CLI;
our @ISA = qw(HP::Proliant::Component::DiskSubsystem::Ide);

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

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    controllers => [],
    accelerators => [],
    enclosures => [],
    physical_drives => [],
    logical_drives => [],
    spare_drives => [],
    blacklisted => 0,
  };
  bless $self, $class;
  return $self;
}

sub init {
  my $self = shift;
}


package HP::Proliant::Component::DiskSubsystem::Ide::SNMP;
our @ISA = qw(HP::Proliant::Component::DiskSubsystem::Ide
    HP::Proliant::Component::SNMP);

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

sub new {
  my $class = shift;
  my %params = @_;
  my $self = { 
    controllers => [],
    accelerators => [],
    enclosures => [],
    physical_drives => [],
    logical_drives => [],
    spare_drives => [],
    blacklisted => 0,
  };
  bless $self, $class;
  return $self;
}

sub init {
  my $self = shift;
  my $snmpwalk = $self->{rawdata};

  # CPQIDE-MIB
  my $oids = {
      cpqIdeControllerEntry => '1.3.6.1.4.1.232.14.2.3.1.1',
      cpqIdeControllerIndex => '1.3.6.1.4.1.232.14.2.3.1.1.1',
      cpqIdeControllerOverallCondition => '1.3.6.1.4.1.232.14.2.3.1.1.2',
      cpqIdeControllerModel => '1.3.6.1.4.1.232.14.2.3.1.1.3',
      cpqIdeControllerSlot => '1.3.6.1.4.1.232.14.2.3.1.1.5',
      cpqIdeControllerOverallConditionValue => {
          1 => "other",
          2 => "ok",
          3 => "degraded",
          4 => "failed",
      },
  };

  # INDEX { cpqIdeControllerIndex }
  foreach ($self->get_entries($oids, 'cpqIdeControllerEntry')) {
    push(@{$self->{controllers}},
        HP::Proliant::Component::DiskSubsystem::Ide::Controller->new(%{$_}));
  }

  $oids = {
      cpqIdeLogicalDriveEntry => '1.3.6.1.4.1.232.14.2.6.1.1',
      cpqIdeLogicalDriveControllerIndex => '1.3.6.1.4.1.232.14.2.6.1.1.1',
      cpqIdeLogicalDriveIndex => '1.3.6.1.4.1.232.14.2.6.1.1.2',
      cpqIdeLogicalDriveRaidLevel => '1.3.6.1.4.1.232.14.2.6.1.1.3',
      cpqIdeLogicalDriveCapacity => '1.3.6.1.4.1.232.14.2.6.1.1.4',
      cpqIdeLogicalDriveStatus => '1.3.6.1.4.1.232.14.2.6.1.1.5',
      cpqIdeLogicalDriveCondition => '1.3.6.1.4.1.232.14.2.6.1.1.6',
      cpqIdeLogicalDriveDiskIds => '1.3.6.1.4.1.232.14.2.6.1.1.7',
      cpqIdeLogicalDriveSpareIds => '1.3.6.1.4.1.232.14.2.6.1.1.9',
      cpqIdeLogicalDriveRebuildingDisk => '1.3.6.1.4.1.232.14.2.6.1.1.10',
      cpqIdeLogicalDriveRaidLevelValue => {
          1 => "other",
          2 => "raid0",
          3 => "raid1",
          4 => "raid0plus1",
      },
      cpqIdeLogicalDriveStatusValue => {
          1 => "other",
          2 => "ok",
          3 => "degraded",
          4 => "rebuilding",
          5 => "failed",
      },
      cpqIdeLogicalDriveConditionValue => {
          1 => "other",
          2 => "ok",
          3 => "degraded",
          4 => "failed",
      },
  };
  # INDEX { cpqIdeLogicalDriveControllerIndex, cpqIdeLogicalDriveIndex }
  foreach ($self->get_entries($oids, 'cpqIdeLogicalDriveEntry')) {
    push(@{$self->{logical_drives}},
        HP::Proliant::Component::DiskSubsystem::Ide::LogicalDrive->new(%{$_}));
  }

  $oids = {
      cpqIdeAtaDiskEntry => '1.3.6.1.4.1.232.14.2.4.1.1',
      cpqIdeAtaDiskControllerIndex => '1.3.6.1.4.1.232.14.2.4.1.1.1',
      cpqIdeAtaDiskIndex => '1.3.6.1.4.1.232.14.2.4.1.1.2',
      cpqIdeAtaDiskModel => '1.3.6.1.4.1.232.14.2.4.1.1.3',
      cpqIdeAtaDiskStatus => '1.3.6.1.4.1.232.14.2.4.1.1.6',
      cpqIdeAtaDiskCondition => '1.3.6.1.4.1.232.14.2.4.1.1.7',
      cpqIdeAtaDiskCapacity => '1.3.6.1.4.1.232.14.2.4.1.1.8',
      cpqIdeAtaDiskLogicalDriveMember => '1.3.6.1.4.1.232.14.2.4.1.1.13',
      cpqIdeAtaDiskIsSpare => '1.3.6.1.4.1.232.14.2.4.1.1.14',
      cpqIdeAtaDiskStatusValue => {
          1 => "other",
          2 => "ok",
          3 => "smartError",
          4 => "failed",
      },
      cpqIdeAtaDiskConditionValue => {
          1 => "other",
          2 => "ok",
          3 => "degraded",
          4 => "failed",
      },
  };
  # INDEX { cpqIdeAtaDiskControllerIndex, cpqIdeAtaDiskIndex }
  foreach ($self->get_entries($oids, 'cpqIdeAtaDiskEntry')) {
    push(@{$self->{physical_drives}},
        HP::Proliant::Component::DiskSubsystem::Ide::PhysicalDrive->new(%{$_}));
  }

}
package HP::Proliant::Component::DiskSubsystem::Ide;
our @ISA = qw(HP::Proliant::Component::DiskSubsystem);

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

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    runtime => $params{runtime},
    rawdata => $params{rawdata},
    method => $params{method},
    controllers => [],
    enclosures => [],
    physical_drives => [],
    logical_drives => [],
    spare_drives => [],
    condition => undef,
    blacklisted => 0,
  };
  bless $self, $class;
  if ($self->{method} eq 'snmp') {
    bless $self, 'HP::Proliant::Component::DiskSubsystem::Ide::SNMP';
  } else {
    bless $self, 'HP::Proliant::Component::DiskSubsystem::Ide::CLI';
  }
  $self->init();
  $self->assemble();
  return $self;
}

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

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

package HP::Proliant::Component::DiskSubsystem::Ide::Controller;
our @ISA = qw(HP::Proliant::Component::DiskSubsystem::Ide);

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

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    runtime => $params{runtime},
    cpqIdeControllerIndex => $params{cpqIdeControllerIndex},
    cpqIdeControllerOverallCondition => $params{cpqIdeControllerOverallCondition},
    cpqIdeControllerModel => $params{cpqIdeControllerModel},
    cpqIdeControllerSlot => $params{cpqIdeControllerSlot}, # -1 ist sysboard?
    blacklisted => 0,
  };
  $self->{name} = $params{name} || $self->{cpqIdeControllerIndex};
  $self->{controllerindex} = $self->{cpqIdeControllerIndex};
  bless $self, $class;
  return $self;
}

sub check {
  my $self = shift;
  $self->blacklist('ideco', $self->{name});
  if ($self->{cpqIdeControllerOverallCondition} eq 'other') {
    if (scalar(@{$self->{physical_drives}})) {
      $self->add_message(CRITICAL,
          sprintf 'ide controller %s in slot %s needs attention',
              $self->{cpqIdeControllerIndex}, $self->{cpqIdeControllerSlot});
      $self->add_info(sprintf 'ide controller %s in slot %s needs attention',
          $self->{cpqIdeControllerIndex}, $self->{cpqIdeControllerSlot});
    } else {
      $self->add_info(sprintf 'ide controller %s in slot %s is ok and unused',
          $self->{cpqIdeControllerIndex}, $self->{cpqIdeControllerSlot});
      $self->{blacklisted} = 1;
    }
  } elsif ($self->{cpqIdeControllerOverallCondition} ne 'ok') {
    $self->add_message(CRITICAL,
        sprintf 'ide controller %s in slot %s needs attention',
            $self->{cpqIdeControllerIndex}, $self->{cpqIdeControllerSlot});
    $self->add_info(sprintf 'ide controller %s in slot %s needs attention',
        $self->{cpqIdeControllerIndex}, $self->{cpqIdeControllerSlot});
  } else {
    $self->add_info(sprintf 'ide controller %s in slot %s is ok',
        $self->{cpqIdeControllerIndex}, $self->{cpqIdeControllerSlot});
  }
  foreach (@{$self->{logical_drives}}) {
    $_->check();
  } 
  foreach (@{$self->{physical_drives}}) {
    $_->check();
  } 
  foreach (@{$self->{spare_drives}}) {
    $_->check();
  } 
} 

sub dump {
  my $self = shift;
  printf "[IDE_CONTROLLER_%s]\n", $self->{name};
  foreach (qw(cpqIdeControllerIndex cpqIdeControllerSlot
      cpqIdeControllerModel cpqIdeControllerOverallCondition)) {
    printf "%s: %s\n", $_, $self->{$_};
  }
  printf "\n";
  foreach (@{$self->{logical_drives}}) {
    $_->dump();
  }
  foreach (@{$self->{physical_drives}}) {
    $_->dump();
  }
  foreach (@{$self->{spare_drives}}) {
    $_->dump();
  }
}


package HP::Proliant::Component::DiskSubsystem::Ide::LogicalDrive;
our @ISA = qw(HP::Proliant::Component::DiskSubsystem::Ide);

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

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    runtime => $params{runtime},
    cpqIdeLogicalDriveControllerIndex => $params{cpqIdeLogicalDriveControllerIndex},
    cpqIdeLogicalDriveIndex => $params{cpqIdeLogicalDriveIndex},
    cpqIdeLogicalDriveRaidLevel => $params{cpqIdeLogicalDriveRaidLevel},
    cpqIdeLogicalDriveCapacity => $params{cpqIdeLogicalDriveCapacity},
    cpqIdeLogicalDriveStatus => $params{cpqIdeLogicalDriveStatus},
    cpqIdeLogicalDriveCondition => $params{cpqIdeLogicalDriveCondition},
    cpqIdeLogicalDriveDiskIds => $params{cpqIdeLogicalDriveDiskIds},
    cpqIdeLogicalDriveSpareIds => $params{cpqIdeLogicalDriveSpareIds},
    cpqIdeLogicalDriveRebuildingDisk => $params{cpqIdeLogicalDriveRebuildingDisk},
    blacklisted => 0,
  };
  bless $self, $class;
  $self->{name} = $params{name} || 
      $self->{cpqIdeLogicalDriveControllerIndex}.':'.
      $self->{cpqIdeLogicalDriveIndex};
  $self->{controllerindex} = $self->{cpqIdeLogicalDriveControllerIndex};
  return $self;
}

sub check {
  my $self = shift;
  $self->blacklist('ideld', $self->{name});
  if ($self->{cpqIdeLogicalDriveCondition} ne "ok") {
    if ($self->{cpqIdeLogicalDriveStatus} =~ 
        /rebuild/) {
      $self->add_message(WARNING,
          sprintf "logical drive %s is %s", 
              $self->{name}, $self->{cpqIdeLogicalDriveStatus});
    } else {
      $self->add_message(CRITICAL,
          sprintf "logical drive %s is %s",
              $self->{name}, $self->{cpqIdeLogicalDriveStatus});
    }
  } 
  $self->add_info(
      sprintf "logical drive %s is %s", $self->{name},
          $self->{cpqIdeLogicalDriveStatus});
}

sub dump {
  my $self = shift;
  printf "[LOGICAL_DRIVE]\n";
  foreach (qw(cpqIdeLogicalDriveControllerIndex cpqIdeLogicalDriveIndex
      cpqIdeLogicalDriveRaidLevel cpqIdeLogicalDriveCapacity 
      cpqIdeLogicalDriveDiskIds cpqIdeLogicalDriveSpareIds 
      cpqIdeLogicalDriveRebuildingDisk cpqIdeLogicalDriveStatus 
      cpqIdeLogicalDriveCondition)) {
    printf "%s: %s\n", $_, $self->{$_};
  }
  printf "\n";
}


package HP::Proliant::Component::DiskSubsystem::Ide::PhysicalDrive;
our @ISA = qw(HP::Proliant::Component::DiskSubsystem::Ide);

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

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    runtime => $params{runtime},
    cpqIdeAtaDiskControllerIndex => $params{cpqIdeAtaDiskControllerIndex},
    cpqIdeAtaDiskIndex => $params{cpqIdeAtaDiskIndex},
    cpqIdeAtaDiskModel => $params{cpqIdeAtaDiskModel},
    cpqIdeAtaDiskStatus => $params{cpqIdeAtaDiskStatus},
    cpqIdeAtaDiskCondition => $params{cpqIdeAtaDiskCondition},
    cpqIdeAtaDiskCapacity => $params{cpqIdeAtaDiskCapacity},
    cpqIdeAtaDiskLogicalDriveMember => $params{cpqIdeAtaDiskLogicalDriveMember},
    cpqIdeAtaDiskIsSpare => $params{cpqIdeAtaDiskIsSpare},
    blacklisted => 0,
  };
  $self->{name} = $params{name} || 
      $self->{cpqIdeAtaDiskControllerIndex}.':'.
      $self->{cpqIdeAtaDiskIndex}; ####vorerst
  $self->{controllerindex} = $self->{cpqIdeAtaDiskControllerIndex};
  bless $self, $class;
  return $self;
}

sub check {
  my $self = shift;
  $self->blacklist('idepd', $self->{name});
  if ($self->{cpqIdeAtaDiskCondition} ne 'ok') {
    $self->add_message(CRITICAL,
        sprintf "physical drive %s is %s", 
            $self->{name}, $self->{cpqIdeAtaDiskCondition});
  }
  $self->add_info(
      sprintf "physical drive %s is %s", 
          $self->{name}, $self->{cpqIdeAtaDiskCondition});
}

sub dump {
  my $self = shift;
  printf "[PHYSICAL_DRIVE]\n";
  foreach (qw(cpqIdeAtaDiskControllerIndex cpqIdeAtaDiskIndex
      cpqIdeAtaDiskModel cpqIdeAtaDiskCapacity cpqIdeAtaDiskLogicalDriveMember 
      cpqIdeAtaDiskStatus cpqIdeAtaDiskCondition)) {
    printf "%s: %s\n", $_, $self->{$_};
  }
  printf "\n";
}


package HP::Proliant::Component::DiskSubsystem::Ide::SpareDrive;
our @ISA = qw(HP::Proliant::Component::DiskSubsystem::Ide);

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

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



package HP::Proliant::Component::DiskSubsystem::Fca::CLI;
our @ISA = qw(HP::Proliant::Component::DiskSubsystem::Fca);

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

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    controllers => [],
    accelerators => [],
    enclosures => [],
    physical_drives => [],
    logical_drives => [],
    spare_drives => [],
    blacklisted => 0,
  };
  bless $self, $class;
  return $self;
}

sub init {
  my $self = shift;
  # .....
  $self->{global_status} =
      HP::Proliant::Component::DiskSubsystem::Fca::GlobalStatus->new(
          runtime => $self->{runtime},
          cpqFcaMibCondition => 'n/a',
      );

}


package HP::Proliant::Component::DiskSubsystem::Fca::SNMP;
our @ISA = qw(HP::Proliant::Component::DiskSubsystem::Fca
    HP::Proliant::Component::SNMP);

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

sub new {
  my $class = shift;
  my %params = @_;
  my $self = { 
    controllers => [],
    accelerators => [],
    enclosures => [],
    physical_drives => [],
    logical_drives => [],
    spare_drives => [],
    blacklisted => 0,
  };
  bless $self, $class;
  return $self;
}

sub init {
  my $self = shift;
  my $snmpwalk = $self->{rawdata};

  # CPQFCA-MIB
  my $oids = {
      cpqFcaHostCntlrEntry => '1.3.6.1.4.1.232.16.2.7.1.1',
      cpqFcaHostCntlrIndex => '1.3.6.1.4.1.232.16.2.7.1.1.1',
      cpqFcaHostCntlrSlot => '1.3.6.1.4.1.232.16.2.7.1.1.2',
      cpqFcaHostCntlrModel => '1.3.6.1.4.1.232.16.2.7.1.1.3',
      cpqFcaHostCntlrStatus => '1.3.6.1.4.1.232.16.2.7.1.1.4',
      cpqFcaHostCntlrCondition => '1.3.6.1.4.1.232.16.2.7.1.1.5',
      cpqFcaHostCntlrOverallCondition => '1.3.6.1.4.1.232.16.2.7.1.1.8',
      cpqFcaHostCntlrModelValue => {
          1 => "other",
          # You may need to upgrade your driver software and\or instrument
          # agent(s).  You have a drive array controller in the system
          # that the instrument agent does not recognize.  (other according to CPQFCAL-MIB)
          2 => "fchc-p",
          3 => "fchc-e",
          4 => "fchc64",
          5 => "sa-sam",
          6 => "fca-2101",
          7 => "sw64-33",
          8 => "fca-221x",
          9 => "dpfcmc",
          10 => 'fca-2404',
          11 => 'fca-2214',
          12 => 'a7298a',
          13 => 'fca-2214dc',
          14 => 'a6826a',
          15 => 'fcmcG3',
          16 => 'fcmcG4',
          17 => 'ab46xa',
          18 => 'fc-generic',
          19 => 'fca-1143',
          20 => 'fca-1243',
          21 => 'fca-2143',
          22 => 'fca-2243',
          23 => 'fca-1050',
          24 => 'fca-lpe1105',
          25 => 'fca-qmh2462',
          26 => 'fca-1142sr',
          27 => 'fca-1242sr',
          28 => 'fca-2142sr',
          29 => 'fca-2242sr',
          30 => 'fcmc20pe',
          31 => 'fca-81q',
          32 => 'fca-82q',
          33 => 'fca-qmh2562',
          34 => 'fca-81e',
          35 => 'fca-82e',
          36 => 'fca-1205',
      },
      cpqFcaHostCntlrStatusValue => {
          1 => "other",
          2 => "ok",
          3 => "failed",
          4 => "shutdown",
          5 => "loopDegraded",
          6 => "loopFailed",
          7 => "notConnected",
      },
      cpqFcaHostCntlrConditionValue => {
          1 => "other",
          2 => "ok",
          3 => "degraded",
          4 => "failed",
      },
      cpqFcaHostCntlrOverallConditionValue => {
          1 => "other",
          2 => "ok",
          3 => "degraded",
          4 => "failed",
      }, # cntrl + alle associated storage boxes
  };

  # INDEX { cpqFcaHostCntlrIndex }
  foreach ($self->get_entries($oids, 'cpqFcaHostCntlrEntry')) {
    push(@{$self->{host_controllers}},
        HP::Proliant::Component::DiskSubsystem::Fca::HostController->new(%{$_}));
  }

  $oids = {
      cpqFcaCntlrEntry => '1.3.6.1.4.1.232.16.2.2.1.1',
      cpqFcaCntlrBoxIndex => '1.3.6.1.4.1.232.16.2.2.1.1.1',
      cpqFcaCntlrBoxIoSlot => '1.3.6.1.4.1.232.16.2.2.1.1.2',
      cpqFcaCntlrModel => '1.3.6.1.4.1.232.16.2.2.1.1.3',
      cpqFcaCntlrStatus => '1.3.6.1.4.1.232.16.2.2.1.1.5',
      cpqFcaCntlrCondition => '1.3.6.1.4.1.232.16.2.2.1.1.6',
      cpqFcaCntlrModelValue => {
        1 => "other",
        2 => "fibreArray",
        3 => "msa1000",
        4 => "smartArrayClusterStorage",
        5 => "hsg80",
        6 => "hsv110", 
        7 => "msa500g2", 
        8 => "msa20",
        8 => "msa1510i",
      },
      cpqFcaCntlrStatusValue => {
          1 => "other",
          2 => "ok",
          3 => "failed",
          4 => "offline",
          5 => "redundantPathOffline",
          6 => "notConnected",
      },
      cpqFcaCntlrConditionValue => {
          1 => "other",
          2 => "ok",
          3 => "degraded",
          4 => "failed",
      },
  };

  # INDEX { cpqFcaCntlrBoxIndex, cpqFcaCntlrBoxIoSlot }
  foreach ($self->get_entries($oids, 'cpqFcaCntlrEntry')) {
    push(@{$self->{controllers}},
        HP::Proliant::Component::DiskSubsystem::Fca::Controller->new(%{$_}));
  }

  $oids = {
      cpqFcaAccelEntry => '1.3.6.1.4.1.232.16.2.2.2.1',
      cpqFcaAccelBoxIndex => '1.3.6.1.4.1.232.16.2.2.2.1.1',
      cpqFcaAccelBoxIoSlot => '1.3.6.1.4.1.232.16.2.2.2.1.2',
      cpqFcaAccelStatus => '1.3.6.1.4.1.232.16.2.2.2.1.3',
      cpqFcaAccelErrCode => '1.3.6.1.4.1.232.16.2.2.2.1.5',
      cpqFcaAccelBatteryStatus => '1.3.6.1.4.1.232.16.2.2.2.1.6',
      cpqFcaAccelCondition => '1.3.6.1.4.1.232.16.2.2.2.1.9',
      cpqFcaAccelStatusValue => {
          1 => "other",
          2 => "invalid",
          3 => "enabled",
          4 => "tmpDisabled",
          5 => "permDisabled",
      },
      cpqFcaAccelErrCodeValue => {
          1 => 'other',
          2 => 'invalid',
          3 => 'badConfig',
          4 => 'lowBattery',
          5 => 'disableCmd',
          6 => 'noResources',
          7 => 'notConnected',
          8 => 'badMirrorData',
          9 => 'readErr',
          10 => 'writeErr',
          11 => 'configCmd',
          12 => 'expandInProgress',
          13 => 'snapshotInProgress',
          14 => 'redundantLowBattery',
          15 => 'redundantSizeMismatch',
          16 => 'redundantCacheFailure',
          17 => 'excessiveEccErrors',
          19 => 'postEccErrors',
      },
      cpqFcaAccelBatteryStatusValue => {
          1 => 'other',
          2 => 'ok',
          3 => 'recharging',
          4 => 'failed',
          5 => 'degraded',
          6 => 'notPresent',
      },
      cpqFcaAccelConditionValue => {
          1 => "other",
          2 => "ok",
          3 => "degraded",
          4 => "failed",
      },
  };

  # INDEX { cpqFcaAccelBoxIndex, cpqFcaAccelBoxIoSlot }
  foreach ($self->get_entries($oids, 'cpqFcaAccelEntry')) {
    push(@{$self->{accelerators}},
        HP::Proliant::Component::DiskSubsystem::Fca::Accelerator->new(%{$_}));
  }

  $oids = {
      cpqFcaLogDrvEntry => '1.3.6.1.4.1.232.16.2.3.1.1',
      cpqFcaLogDrvBoxIndex => '1.3.6.1.4.1.232.16.2.3.1.1.1',
      cpqFcaLogDrvIndex => '1.3.6.1.4.1.232.16.2.3.1.1.2',
      cpqFcaLogDrvFaultTol => '1.3.6.1.4.1.232.16.2.3.1.1.3',
      cpqFcaLogDrvStatus => '1.3.6.1.4.1.232.16.2.3.1.1.4',
      cpqFcaLogDrvPercentRebuild => '1.3.6.1.4.1.232.16.2.3.1.1.6',
      cpqFcaLogDrvSize => '1.3.6.1.4.1.232.16.2.3.1.1.9',
      cpqFcaLogDrvPhyDrvIDs => '1.3.6.1.4.1.232.16.2.3.1.1.10',
      cpqFcaLogDrvCondition => '1.3.6.1.4.1.232.16.2.3.1.1.11',
      cpqFcaLogDrvFaultTolValue => {
          1 => 'other',
          2 => 'none',
          3 => 'mirroring',
          4 => 'dataGuard',
          5 => 'distribDataGuard',
          7 => 'advancedDataGuard',
      },
      cpqFcaLogDrvStatusValue => {
          1 => 'other',
          2 => 'ok',
          3 => 'failed',
          4 => 'unconfigured',
          5 => 'recovering',
          6 => 'readyForRebuild',
          7 => 'rebuilding',
          8 => 'wrongDrive',
          9 => 'badConnect',
          10 => 'overheating',
          11 => 'shutdown',
          12 => 'expanding',
          13 => 'notAvailable',
          14 => 'queuedForExpansion',
          15 => 'hardError',
      },
      cpqFcaLogDrvConditionValue => {
          1 => 'other',
          2 => 'ok',
          3 => 'degraded',
          4 => 'failed',
      },
  };

  # INDEX { cpqFcaLogDrvBoxIndex, cpqFcaLogDrvIndex }
  foreach ($self->get_entries($oids, 'cpqFcaLogDrvEntry')) {
    push(@{$self->{logical_drives}},
        HP::Proliant::Component::DiskSubsystem::Fca::LogicalDrive->new(%{$_}));
  }

  $oids = {
      cpqFcaPhyDrvEntry => '1.3.6.1.4.1.232.16.2.5.1.1',
      cpqFcaPhyDrvBoxIndex => '1.3.6.1.4.1.232.16.2.5.1.1.1',
      cpqFcaPhyDrvIndex => '1.3.6.1.4.1.232.16.2.5.1.1.2',
      cpqFcaPhyDrvModel => '1.3.6.1.4.1.232.16.2.5.1.1.3',
      cpqFcaPhyDrvBay => '1.3.6.1.4.1.232.16.2.5.1.1.5',
      cpqFcaPhyDrvStatus => '1.3.6.1.4.1.232.16.2.5.1.1.6',
      cpqFcaPhyDrvCondition => '1.3.6.1.4.1.232.16.2.5.1.1.31',
      cpqFcaPhyDrvSize => '1.3.6.1.4.1.232.16.2.5.1.1.38',
      cpqFcaPhyDrvBusNumber => '1.3.6.1.4.1.232.16.2.5.1.1.42',
      cpqFcaPhyDrvStatusValue => {
          1 => 'other',
          2 => 'unconfigured',
          3 => 'ok',
          4 => 'threshExceeded',
          5 => 'predictiveFailure',
          6 => 'failed',
      },
      cpqFcaPhyDrvConditionValue => {
          1 => 'other',
          2 => 'ok',
          3 => 'degraded',
          4 => 'failed',
      },
  };

  # INDEX { cpqFcaPhyDrvBoxIndex, cpqFcaPhyDrvIndex }
  foreach ($self->get_entries($oids, 'cpqFcaPhyDrvEntry')) {
    push(@{$self->{physical_drives}},
        HP::Proliant::Component::DiskSubsystem::Fca::PhysicalDrive->new(%{$_}));
  }

  $oids = {
      cpqFcaMibRevMajor => '1.3.6.1.4.1.232.16.1.1.0',
      cpqFcaMibRevMinor => '1.3.6.1.4.1.232.16.1.2.0',
      cpqFcaMibCondition => '1.3.6.1.4.1.232.16.1.3.0',
      cpqFcaMibConditionValue => {
          1 => 'other',
          2 => 'ok',
          3 => 'degraded',
          4 => 'failed',
      },
  };
  $self->{global_status} =
      HP::Proliant::Component::DiskSubsystem::Fca::GlobalStatus->new(
          runtime => $self->{runtime},
          cpqFcaMibCondition => 
            SNMP::Utils::get_object_value($snmpwalk,
                $oids->{cpqFcaMibCondition}, $oids->{cpqFcaMibConditionValue})
      );
}
package HP::Proliant::Component::DiskSubsystem::Fca;
our @ISA = qw(HP::Proliant::Component::DiskSubsystem);

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

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    runtime => $params{runtime},
    rawdata => $params{rawdata},
    method => $params{method},
    host_controllers => [],
    controllers => [],
    accelerators => [],
    enclosures => [],
    physical_drives => [],
    logical_drives => [],
    spare_drives => [],
    global_status => undef,
    blacklisted => 0,
  };
  bless $self, $class;
  if ($self->{method} eq 'snmp') {
    bless $self, 'HP::Proliant::Component::DiskSubsystem::Fca::SNMP';
  } else {
    bless $self, 'HP::Proliant::Component::DiskSubsystem::Fca::CLI';
  }
  $self->init();
  $self->assemble();
  return $self;
}

sub assemble {
  my $self = shift;
  $self->trace(3, sprintf "%s controllers und platten zusammenfuehren",
      ref($self));
  $self->trace(3, sprintf "has %d host controllers", 
      scalar(@{$self->{host_controllers}}));
  $self->trace(3, sprintf "has %d controllers",
      scalar(@{$self->{controllers}}));
  $self->trace(3, sprintf "has %d physical_drives",
      scalar(@{$self->{physical_drives}}));
  $self->trace(3, sprintf "has %d logical_drives",
      scalar(@{$self->{logical_drives}}));
  $self->trace(3, sprintf "has %d spare_drives",
      scalar(@{$self->{spare_drives}}));
}

sub check {
  my $self = shift;
  foreach (@{$self->{host_controllers}}) {
    $_->check();
  }
  foreach (@{$self->{controllers}}) {
    $_->check();
  }
  foreach (@{$self->{accelerators}}) {
    $_->check();
  }
  foreach (@{$self->{logical_drives}}) {
    $_->check();
  }
  foreach (@{$self->{physical_drives}}) {
    $_->check();
  }
  # wozu eigentlich?
  #if (! $self->has_controllers()) {
    #$self->{global_status}->check();
  #}
}

sub dump {
  my $self = shift;
  foreach (@{$self->{host_controllers}}) {
    $_->dump();
  }
  foreach (@{$self->{controllers}}) {
    $_->dump();
  }
  foreach (@{$self->{accelerators}}) {
    $_->dump();
  }
  foreach (@{$self->{logical_drives}}) {
    $_->dump();
  }
  foreach (@{$self->{physical_drives}}) {
    $_->dump();
  }
  #$self->{global_status}->dump();
}


package HP::Proliant::Component::DiskSubsystem::Fca::GlobalStatus;
our @ISA = qw(HP::Proliant::Component::DiskSubsystem::Fca);

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

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    runtime => $params{runtime},
    cpqFcaMibCondition => $params{cpqFcaMibCondition},
  };
  bless $self, $class;
  return $self;
}

sub check {
  my $self = shift;
  if ($self->{cpqFcaMibCondition} eq 'other') {
    $self->add_message(OK,
        sprintf 'fcal overall condition is other, update your drivers, please');
  } elsif ($self->{cpqFcaMibCondition} ne 'ok') {
    $self->add_message(CRITICAL, 
        sprintf 'fcal overall condition is %s', $self->{cpqFcaMibCondition});
  }
  $self->{info} = 
      sprintf 'fcal overall condition is %s', $self->{cpqFcaMibCondition};
}

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


package HP::Proliant::Component::DiskSubsystem::Fca::HostController;
our @ISA = qw(HP::Proliant::Component::DiskSubsystem::Fca);

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

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    runtime => $params{runtime},
    blacklisted => 0,
    info => undef,
    extendedinfo => undef, 
  }; 
  map { $self->{$_} = $params{$_} } grep /cpqFcaHostCntlr/, keys %params;
  $self->{name} = $params{name} || $self->{cpqFcaHostCntlrIndex};
  $self->{controllerindex} = $self->{cpqFcaHostCntlrIndex};
  bless $self, $class;
  return $self;
}

sub check {
  my $self = shift;
  $self->blacklist('fcahc', $self->{name});
  my $info = sprintf 'fcal host controller %s in slot %s is %s',
      $self->{name}, $self->{cpqFcaHostCntlrSlot}, $self->{cpqFcaHostCntlrCondition};
  if ($self->{cpqFcaHostCntlrCondition} eq 'other') {
    #$info .= sprintf ' and needs attention (%s)', $self->{cpqFcaHostCntlrStatus};
    # let's assume other=ok
    $self->add_message(OK, $info);
    $self->add_info($info);
  } elsif ($self->{cpqFcaHostCntlrCondition} ne 'ok') {
    $self->add_message(CRITICAL, $info);
    $self->add_info($info);
  } else { 
    $self->add_info($info);
  }
  $self->blacklist('fcahco', $self->{name});
  $info = sprintf 'fcal host controller %s overall condition is %s',
      $self->{name}, $self->{cpqFcaHostCntlrOverallCondition};
  if ($self->{cpqFcaHostCntlrOverallCondition} eq 'other') {
    $self->add_message(OK, $info);
  } elsif ($self->{cpqFcaHostCntlrOverallCondition} ne 'ok') {
    $self->add_message(WARNING, $info);
  }
  $self->add_info($info);
}   

sub dump { 
  my $self = shift;
  printf "[FCAL_HOST_CONTROLLER_%s]\n", $self->{name};
  foreach (qw(cpqFcaHostCntlrIndex cpqFcaHostCntlrSlot
      cpqFcaHostCntlrModel cpqFcaHostCntlrStatus cpqFcaHostCntlrCondition
      cpqFcaHostCntlrOverallCondition)) {
    printf "%s: %s\n", $_, $self->{$_};
  }
  printf "\n";
}


package HP::Proliant::Component::DiskSubsystem::Fca::Controller;
our @ISA = qw(HP::Proliant::Component::DiskSubsystem::Fca);

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

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    runtime => $params{runtime},
    blacklisted => 0,
    info => undef,
    extendedinfo => undef, 
  }; 
  map { $self->{$_} = $params{$_} } grep /cpqFcaCntlr/, keys %params;
  $self->{name} = $params{name} || 
      $self->{cpqFcaCntlrBoxIndex}.':'.$self->{cpqFcaCntlrBoxIoSlot};
  $self->{controllerindex} = $self->{cpqFcaCntlrBoxIndex};
  bless $self, $class;
  return $self;
}

sub check {
  my $self = shift;
  $self->blacklist('fcaco', $self->{name});
  my $info = sprintf 'fcal controller %s in box %s/slot %s is %s',
      $self->{name}, $self->{cpqFcaCntlrBoxIndex}, $self->{cpqFcaCntlrBoxIoSlot},
      $self->{cpqFcaCntlrCondition};
  if ($self->{cpqFcaCntlrCondition} eq 'other') {
    if (1) { # was ist mit phys. drives?
      $info .= ' needs attention';
      $info .= sprintf(' (%s)', $self->{cpqFcaCntlrStatus}) unless $self->{cpqFcaCntlrStatus} eq 'ok';
      $self->add_message(CRITICAL, $info);
      $self->add_info($info);
    } else {
      $self->add_info($info);
      $self->{blacklisted} = 1;
    }
  } elsif ($self->{cpqFcaCntlrCondition} ne 'ok') {
    $info .= ' needs attention';
    $info .= sprintf(' (%s)', $self->{cpqFcaCntlrStatus}) unless $self->{cpqFcaCntlrStatus} eq 'ok';
    $self->add_message(CRITICAL, $info);
    $self->add_info($info);
  } else {
    $self->add_info($info);
  }
} 

sub dump {
  my $self = shift;
  printf "[FCAL_CONTROLLER_%s]\n", $self->{name};
  foreach (qw(cpqFcaCntlrBoxIndex cpqFcaCntlrBoxIoSlot cpqFcaCntlrModel
      cpqFcaCntlrStatus cpqFcaCntlrCondition)) {
    printf "%s: %s\n", $_, $self->{$_};
  }
  printf "\n";
}


package HP::Proliant::Component::DiskSubsystem::Fca::Accelerator;
our @ISA = qw(HP::Proliant::Component::DiskSubsystem::Fca);

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

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    runtime => $params{runtime},
    blacklisted => 0,
    info => undef,
    extendedinfo => undef, 
  }; 
  map { $self->{$_} = $params{$_} } grep /cpqFcaAccel/, keys %params;
  $self->{name} = $params{name} ||
      $self->{cpqFcaAccelBoxIndex}.':'.$self->{cpqFcaAccelBoxIoSlot};
  $self->{controllerindex} = $self->{cpqFcaAccelBoxIndex};
  bless $self, $class;
  return $self;
}

sub check {
  my $self = shift;
  # !!! cpqFcaAccelStatus
  $self->blacklist('fcaac', $self->{name});
  my $info = sprintf 'fcal accelerator %s in box %s/slot %s is ',
      $self->{name}, $self->{cpqFcaAccelBoxIndex}, $self->{cpqFcaAccelBoxIoSlot};
  if ($self->{cpqFcaAccelStatus} eq 'invalid') {
    $info .= 'not installed';
    $self->add_info($info);
  } elsif ($self->{cpqFcaAccelStatus} eq 'tmpDisabled') {
    $info .= 'temp disabled';
    $self->add_info($info);
  } elsif ($self->{cpqFcaAccelCondition} eq 'other') {
    $info .= sprintf '%s and needs attention (%s)',
        $self->{cpqFcaAccelCondition}, $self->{cpqFcaAccelErrCode};
    $self->add_message(CRITICAL, $info);
    $self->add_info($info);
  } elsif ($self->{cpqFcaAccelCondition} ne 'ok') {
    $info .= sprintf '%s and needs attention (%s)',
        $self->{cpqFcaAccelCondition}, $self->{cpqFcaAccelErrCode};
    $self->add_message(CRITICAL, $info);
    $self->add_info($info);
  } else {
    $info .= sprintf '%s', $self->{cpqFcaAccelCondition};
    $self->add_info($info);
  }
}

sub dump {
  my $self = shift;
  printf "[FCAL_ACCELERATOR_%s]\n", $self->{name};
  foreach (qw(cpqFcaAccelBoxIndex cpqFcaAccelBoxIoSlot cpqFcaAccelStatus
      cpqFcaAccelErrCode cpqFcaAccelBatteryStatus cpqFcaAccelCondition)) {
    printf "%s: %s\n", $_, $self->{$_};
  }
  printf "\n";
}


package HP::Proliant::Component::DiskSubsystem::Fca::LogicalDrive;
our @ISA = qw(HP::Proliant::Component::DiskSubsystem::Fca);

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

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    runtime => $params{runtime},
    blacklisted => 0,
    info => undef,
    extendedinfo => undef, 
  }; 
  map { $self->{$_} = $params{$_} } grep /cpqFcaLogDrv/, keys %params;
  bless $self, $class;
  $self->{name} = $params{name} || 
      $self->{cpqFcaLogDrvBoxIndex}.':'.
      $self->{cpqFcaLogDrvIndex};
  $self->{controllerindex} = $self->{cpqFcaLogDrvBoxIndex};
  return $self;
}

sub check {
  my $self = shift;
  $self->blacklist('fcald', $self->{name});
  my $info = sprintf 'logical drive %s (%s) is %s',
      $self->{name}, $self->{cpqFcaLogDrvFaultTol}, $self->{cpqFcaLogDrvCondition};
  if ($self->{cpqFcaLogDrvCondition} ne "ok") {
    $info .= sprintf ' (%s)', $self->{cpqFcaLogDrvStatus};
    if ($self->{cpqFcaLogDrvStatus} =~ 
        /rebuild|recovering|expand/) {
      $info .= sprintf ' (%s)', $self->{cpqFcaLogDrvStatus};
      $self->add_message(WARNING, $info);
    } else {
      $self->add_message(CRITICAL, $info);
    }
  } 
  $self->add_info($info);
}

sub dump {
  my $self = shift;
  printf "[LOGICAL_DRIVE_%s]\n", $self->{name};
  foreach (qw(cpqFcaLogDrvBoxIndex cpqFcaLogDrvIndex cpqFcaLogDrvFaultTol
      cpqFcaLogDrvStatus cpqFcaLogDrvPercentRebuild cpqFcaLogDrvSize 
      cpqFcaLogDrvPhyDrvIDs cpqFcaLogDrvCondition)) {
    printf "%s: %s\n", $_, $self->{$_};
  }
  printf "\n";
}


package HP::Proliant::Component::DiskSubsystem::Fca::PhysicalDrive;
our @ISA = qw(HP::Proliant::Component::DiskSubsystem::Fca);

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

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    runtime => $params{runtime},
    blacklisted => 0,
    info => undef,
    extendedinfo => undef, 
  }; 
  map { $self->{$_} = $params{$_} } grep /cpqFcaPhyDrv/, keys %params;
  $self->{name} = $params{name} || 
      $self->{cpqFcaPhyDrvBoxIndex}.':'.$self->{cpqFcaPhyDrvIndex}; ####vorerst
  $self->{controllerindex} = $self->{cpqScsiPhyDrvCntlrIndex};
  bless $self, $class;
  return $self;
}

sub check {
  my $self = shift;
  $self->blacklist('fcapd', $self->{name});
  my $info = sprintf "physical drive %s is %s", 
      $self->{name}, $self->{cpqFcaPhyDrvStatus};
  if ($self->{cpqFcaPhyDrvStatus} eq 'unconfigured') {
    # not part of a logical drive
    # condition will surely be other
  } elsif ($self->{cpqFcaPhyDrvCondition} ne 'ok') {
    $self->add_message(CRITICAL, $info);
  }
  $self->add_info($info);
}

sub dump {
  my $self = shift;
  printf "[PHYSICAL_DRIVE_%s]\n", $self->{name};
  foreach (qw(cpqFcaPhyDrvBoxIndex cpqFcaPhyDrvIndex cpqFcaPhyDrvModel
      cpqFcaPhyDrvBay cpqFcaPhyDrvStatus cpqFcaPhyDrvCondition
      cpqFcaPhyDrvSize cpqFcaPhyDrvBusNumber)) {
    printf "%s: %s\n", $_, $self->{$_};
  }
  printf "\n";
}


package HP::Proliant::Component::DiskSubsystem::Fca::SpareDrive;
our @ISA = qw(HP::Proliant::Component::DiskSubsystem::Fca);

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

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



package HP::Proliant::Component::DiskSubsystem;
our @ISA = qw(HP::Proliant::Component);

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

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    runtime => $params{runtime},
    rawdata => $params{rawdata},
    method => $params{method},
    da_subsystem => undef,
    sas_da_subsystem => undef,
    ide_da_subsystem => undef,
    fca_da_subsystem => undef,
    scsi_da_subsystem => undef,
    condition => $params{condition},
    blacklisted => 0,
  };
  bless $self, $class;
  $self->init();
  return $self;
}

sub init {
  my $self = shift;
  $self->{da_subsystem} = HP::Proliant::Component::DiskSubsystem::Da->new(
    runtime => $self->{runtime},
    rawdata => $self->{rawdata},
    method => $self->{method},
  );
  $self->{sas_subsystem} = HP::Proliant::Component::DiskSubsystem::Sas->new(
    runtime => $self->{runtime},
    rawdata => $self->{rawdata},
    method => $self->{method},
  );
  $self->{scsi_subsystem} = HP::Proliant::Component::DiskSubsystem::Scsi->new(
    runtime => $self->{runtime},
    rawdata => $self->{rawdata},
    method => $self->{method},
  );
  $self->{ide_subsystem} = HP::Proliant::Component::DiskSubsystem::Ide->new(
    runtime => $self->{runtime},
    rawdata => $self->{rawdata},
    method => $self->{method},
  );
  $self->{fca_subsystem} = HP::Proliant::Component::DiskSubsystem::Fca->new(
    runtime => $self->{runtime},
    rawdata => $self->{rawdata},
    method => $self->{method},
  );
}

sub check {
  my $self = shift;
  $self->add_info('checking disk subsystem');
  $self->{da_subsystem}->check();
  $self->{sas_subsystem}->check();
  $self->{scsi_subsystem}->check();
  $self->{ide_subsystem}->check();
  $self->{fca_subsystem}->check();
  $self->disk_summary();
}

sub dump {
  my $self = shift;
  $self->{da_subsystem}->dump();
  $self->{sas_subsystem}->dump();
  $self->{scsi_subsystem}->dump();
  $self->{ide_subsystem}->dump();
  $self->{fca_subsystem}->dump();
}

sub disk_summary {
  my $self = shift;
  foreach my $subsys (qw(da sas scsi ide fca)) {
    if (my $pd = $self->{$subsys.'_subsystem'}->has_physical_drives()) {
      my $ld = $self->{$subsys.'_subsystem'}->has_logical_drives();
      $self->add_summary(sprintf '%s: %d logical drives, %d physical drives',
          $subsys, $ld, $pd);
    }
  }
}

sub assemble {
  my $self = shift;
  $self->trace(3, sprintf "%s controllers und platten zusammenfuehren",
      ref($self));
  $self->trace(3, sprintf "has %d controllers",
      scalar(@{$self->{controllers}}));
  $self->trace(3, sprintf "has %d accelerators",
      scalar(@{$self->{accelerators}})) if exists $self->{accelerators};
  $self->trace(3, sprintf "has %d enclosures",
      scalar(@{$self->{enclosures}}));
  $self->trace(3, sprintf "has %d physical_drives",
      scalar(@{$self->{physical_drives}}));
  $self->trace(3, sprintf "has %d logical_drives",
      scalar(@{$self->{logical_drives}}));
  $self->trace(3, sprintf "has %d spare_drives",
      scalar(@{$self->{spare_drives}}));
  my $found = {
      accelerators => {},
      enclosures => {},
      logical_drives => {},
      physical_drives => {},
      spare_drives => {},
  };
  # found->{komponente}->{controllerindex} ist ein array
  # von teilen, die zu einem controller gehoeren
  foreach my $item (qw(accelerators enclosures logical_drives physical_drives spare_drives)) {
    next if ($item eq "enclosures" && ! exists $self->{$item});
    foreach (@{$self->{$item}}) {
      $found->{item}->{$_->{controllerindex}} = []
          unless exists $found->{$item}->{$_->{controllerindex}};
      push(@{$found->{$item}->{$_->{controllerindex}}}, $_);
    }
  }
  foreach my $item (qw(accelerators enclosures logical_drives physical_drives spare_drives)) {
    foreach (@{$self->{controllers}}) {
      if (exists $found->{$item}->{$_->{controllerindex}}) {
        $_->{$item} = $found->{$item}->{$_->{controllerindex}};
        delete $found->{$item}->{$_->{controllerindex}};
      } else {
        $_->{$item} = []; # z.b. ein leerer controller: physical_drives = []
      }
    }
  }
  # was jetzt noch in $found uebrig ist, gehoert zu keinem controller
  # d.h. komponenten mit ungueltigen cnrtlindex wurden gefunden
}

sub has_controllers {
  my $self = shift;
  return scalar(@{$self->{controllers}});
}

sub has_accelerators {
  my $self = shift;
  return exists $self->{accelerators} ? scalar(@{$self->{accelerators}}) : 0;
}

sub has_physical_drives {
  my $self = shift;
  return scalar(@{$self->{physical_drives}});
}

sub has_logical_drives {
  my $self = shift;
  return scalar(@{$self->{logical_drives}});
}

sub has_enclosures {
  my $self = shift;
  return scalar(@{$self->{enclosures}});
}


package HP::Proliant::Component;
our @ISA = qw(HP::Proliant);



package HP::Proliant;

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

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

sub init {
  my $self = shift;
  $self->{components} = {
      powersupply_subsystem => undef,
      fan_subsystem => undef,
      temperature_subsystem => undef,
      cpu_subsystem => undef,
      memory_subsystem => undef,
      nic_subsystem => undef,
      disk_subsystem => undef,
      asr_subsystem => undef,
      event_subsystem => undef,
  };
  $self->{serial} = 'unknown';
  $self->{product} = 'unknown';
  $self->{romversion} = 'unknown';
  $self->collect();
  if (! $self->{runtime}->{plugin}->check_messages() && 
      ! exists $self->{noinst_hint}) {
    $self->set_serial();
    $self->check_for_buggy_firmware();
    $self->analyze_cpus();
    $self->analyze_powersupplies();
    $self->analyze_fan_subsystem();
    $self->analyze_temperatures();
    $self->analyze_memory_subsystem();
    $self->analyze_nic_subsystem();
    $self->analyze_disk_subsystem();
    $self->analyze_asr_subsystem();
    $self->analyze_event_subsystem();
    $self->auto_blacklist();
    $self->check_cpus();
    $self->check_powersupplies();
    $self->check_fan_subsystem();
    $self->check_temperatures();
    $self->check_memory_subsystem();
    $self->check_nic_subsystem();
    $self->check_disk_subsystem();
    $self->check_asr_subsystem();
    $self->check_event_subsystem();
  }
}

sub identify {
  my $self = shift;
  foreach (qw(product serial romversion)) {
    $self->{$_} =~ s/^\s+//;
    $self->{$_} =~ s/\s+$//;
  }
  return sprintf "System: '%s', S/N: '%s', ROM: '%s'", 
      $self->{product}, $self->{serial}, $self->{romversion};
}

sub check_for_buggy_firmware {
  my $self = shift;
  my @buggyfirmwares = (
      "P24 12/11/2001",
      "P24 11/15/2002",
      "D13 06/03/2003",
      "D13 09/15/2004",
      "P20 12/17/2002"
  );
  if ($self->{romversion} =~ /^\w+ \d+\/\d+\/\d+$/) {
    $self->{runtime}->{options}->{buggy_firmware} =
        grep /^$self->{romversion}/, @buggyfirmwares;
  } else {
    # nicht parsbarer schrott in cpqSeSysRomVer, gesehen bei Gen9
    $self->{runtime}->{options}->{buggy_firmware} = undef;
  }
}

sub dump {
  my $self = shift;
  printf STDERR "serial %s\n", $self->{serial};
  printf STDERR "product %s\n", $self->{product};
  printf STDERR "romversion %s\n", $self->{romversion};
  printf STDERR "%s\n", Data::Dumper::Dumper($self->{components});
}

sub analyze_powersupplies {
  my $self = shift;
  $self->{components}->{powersupply_subsystem} =
      HP::Proliant::Component::PowersupplySubsystem->new(
    rawdata => $self->{rawdata},
    method => $self->{method},
    runtime => $self->{runtime},
  );
}

sub analyze_fan_subsystem {
  my $self = shift;
  $self->{components}->{fan_subsystem} = 
      HP::Proliant::Component::FanSubsystem->new(
    rawdata => $self->{rawdata},
    method => $self->{method},
    runtime => $self->{runtime},
  );
}

sub analyze_temperatures {
  my $self = shift;
  $self->{components}->{temperature_subsystem} = 
      HP::Proliant::Component::TemperatureSubsystem->new(
    rawdata => $self->{rawdata},
    method => $self->{method},
    runtime => $self->{runtime},
  );
}

sub analyze_cpus {
  my $self = shift;
  $self->{components}->{cpu_subsystem} =
      HP::Proliant::Component::CpuSubsystem->new(
    rawdata => $self->{rawdata},
    method => $self->{method},
    runtime => $self->{runtime},
  );
}

sub analyze_memory_subsystem {
  my $self = shift;
  $self->{components}->{memory_subsystem} = 
      HP::Proliant::Component::MemorySubsystem->new(
    rawdata => $self->{rawdata},
    method => $self->{method},
    runtime => $self->{runtime},
  );
}

sub analyze_nic_subsystem {
  my $self = shift;
  return if $self->{method} ne "snmp";
  $self->{components}->{nic_subsystem} = 
      HP::Proliant::Component::NicSubsystem->new(
    rawdata => $self->{rawdata},
    method => $self->{method},
    runtime => $self->{runtime},
  );
}

sub analyze_disk_subsystem {
  my $self = shift;
  $self->{components}->{disk_subsystem} =
      HP::Proliant::Component::DiskSubsystem->new(
    rawdata => $self->{rawdata},
    method => $self->{method},
    runtime => $self->{runtime},
  );
}

sub analyze_asr_subsystem {
  my $self = shift;
  $self->{components}->{asr_subsystem} =
      HP::Proliant::Component::AsrSubsystem->new(
    rawdata => $self->{rawdata},
    method => $self->{method},
    runtime => $self->{runtime},
  );
}

sub analyze_event_subsystem {
  my $self = shift;
  $self->{components}->{event_subsystem} =
      HP::Proliant::Component::EventSubsystem->new(
    rawdata => $self->{rawdata},
    method => $self->{method},
    runtime => $self->{runtime},
  );
}

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

sub check_powersupplies {
  my $self = shift;
  $self->{components}->{powersupply_subsystem}->check();
  $self->{components}->{powersupply_subsystem}->dump()
      if $self->{runtime}->{options}->{verbose} >= 2;
}

sub check_fan_subsystem {
  my $self = shift;
  $self->{components}->{fan_subsystem}->check();
  $self->{components}->{fan_subsystem}->dump()
      if $self->{runtime}->{options}->{verbose} >= 2;
}

sub check_temperatures {
  my $self = shift;
  $self->{components}->{temperature_subsystem}->check();
  $self->{components}->{temperature_subsystem}->dump()
      if $self->{runtime}->{options}->{verbose} >= 2;
}

sub check_memory_subsystem {
  my $self = shift;
  $self->{components}->{memory_subsystem}->check();
  $self->{components}->{memory_subsystem}->dump()
      if $self->{runtime}->{options}->{verbose} >= 2;
}

sub check_nic_subsystem {
  my $self = shift;
  return if $self->{method} ne "snmp";
  if ($self->{runtime}->{plugin}->{opts}->get('eval-nics')) {
    $self->{components}->{nic_subsystem}->check();
    $self->{components}->{nic_subsystem}->dump()
        if $self->{runtime}->{options}->{verbose} >= 2;
  }
}
sub check_disk_subsystem {
  my $self = shift;
  $self->{components}->{disk_subsystem}->check();
  $self->{components}->{disk_subsystem}->dump()
      if $self->{runtime}->{options}->{verbose} >= 2;
  # zum anhaengen an die normale ausgabe... da: 2 logical drives, 5 physical...
  $self->{runtime}->{plugin}->add_message(OK,
      $self->{components}->{disk_subsystem}->{summary})
      if $self->{components}->{disk_subsystem}->{summary};
}

sub check_asr_subsystem {
  my $self = shift;
  $self->{components}->{asr_subsystem}->check();
  $self->{components}->{asr_subsystem}->dump()
      if $self->{runtime}->{options}->{verbose} >= 2;
}

sub check_event_subsystem {
  my $self = shift;
  $self->{components}->{event_subsystem}->check();
  $self->{components}->{event_subsystem}->dump()
      if $self->{runtime}->{options}->{verbose} >= 2;
}

sub auto_blacklist() {
  my $self = shift;
  if ($self->{product} =~ /380 g6/) {
    # http://bizsupport1.austin.hp.com/bc/docs/support/SupportManual/c01723408/c01723408.pdf seite 19
    if ($self->{components}->{cpu_subsystem}->num_cpus() == 1) {
      $self->add_blacklist('ff/f:5,6');
    }
  } elsif ($self->{product} =~ /380 g6/) {
    # http://bizsupport1.austin.hp.com/bc/docs/support/SupportManual/c01704762/c01704762.pdf Fan 2 is only required when processor 2 is installed in the server.
  }
}


package HP::Proliant::CLI;

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

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

sub collect {
  my $self = shift;
  my $hpasmcli = undef;
  if (($self->{runtime}->{plugin}->opts->hpasmcli) &&
      (-f $self->{runtime}->{plugin}->opts->hpasmcli) &&
      (! -x $self->{runtime}->{plugin}->opts->hpasmcli)) {
    no strict 'refs';
    open(BIRK, $self->{runtime}->{plugin}->opts->hpasmcli);
    # all output in one file prefixed with server|powersupply|fans|temp|dimm
    while(<BIRK>) {
      chomp;
      $self->{rawdata} .= $_."\n";
    }
    close BIRK;
    # If you run this script and redirect it's output to a file
    # you can use it for testing purposes with
    # --hpasmcli <output>
    # It must not be executable. (chmod 644)
    my $diag = <<'EOEO';
    hpasmcli=$(which hpasmcli)
    hpacucli=$(which hpacucli)
    for i in server powersupply fans temp dimm
    do
      $hpasmcli -s "show $i" | while read line
      do
        printf "%s %s\n" $i "$line"
      done
    done 
    if [ -x "$hpacucli" ]; then
      for i in config status
      do
        $hpacucli ctrl all show $i | while read line
        do
          printf "%s %s\n" $i "$line"
        done
      done
    fi
EOEO
  } else {
    #die "exec hpasmcli";
    # alles einsammeln und in rawdata stecken
    my $hpasmcli = undef;
    $hpasmcli = $self->{runtime}->{plugin}->opts->hpasmcli ?
        $self->{runtime}->{plugin}->opts->hpasmcli : '/sbin/hpasmcli';
# check if this exists at all
# descend the directory
    if ($self->{runtime}->{plugin}->opts->hpasmcli &&
        -e $self->{runtime}->{plugin}->opts->hpasmcli) {
      $hpasmcli = $self->{runtime}->{plugin}->opts->hpasmcli;
    } elsif (-e '/sbin/hpasmcli') {
      $hpasmcli = '/sbin/hpasmcli';
    } else {
      $hpasmcli = undef;
    }
    if ($hpasmcli) {
      if ($< != 0) {
        close STDIN;
        $hpasmcli = "sudo -S ".$hpasmcli;
      }
      $self->trace(2                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   