#! /usr/bin/perl -w

package Nagios::MiniPlugin;

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

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

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

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

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

our %STATUS_TEXT = reverse %ERRORS;

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

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

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

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

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

sub add_perfdata {
  my ($self, %args) = @_;
  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 => 15,
  verbose => 0,
  license =>
"This nagios plugin is free software, and comes with ABSOLUTELY NO WARRANTY.
It may be used, redistributed and/or modified under the terms of the GNU
General Public Licence (see http://www.fsf.org/licensing/licenses/gpl.txt).",
);
# Standard arguments
my @ARGS = ({
    spec => 'usage|?',
    help => "-?, --usage\n   Print usage information",
  }, {
    spec => 'help|h',
    help => "-h, --help\n   Print detailed help screen",
  }, {
    spec => 'version|V',
    help => "-V, --version\n   Print version information",
  }, {
    #spec => 'extra-opts:s@',
    #help => "--extra-opts=[<section>[@<config_file>]]\n   Section and/or config_file from which to load extra options (may repeat)",
  }, {
    spec => 'timeout|t=i',
    help => "-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::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 => [],
    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},
    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});
  } 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)) {
    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 => [],
    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 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 => [],
    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",
      },
      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",
      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->{powersupplies}},
    #    HP::Proliant::Component::PowersupplySubsystem::Powersupply->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},
    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};
        
  }
  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});
  } 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
      cpqHeTemperatureCondition)) {
    printf "%s: %s\n", $_, $self->{$_};
  }
  printf "info: %s\n\n", $self->{info};
}



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->init(%params);
  return $self;
}

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",
      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',
      }
  };
  # INDEX { cpqHeTemperatureChassis, cpqHeTemperatureIndex }
  foreach ($self->get_entries($oids, 'cpqHeTemperatureEntry')) {
    push(@{$self->{temperatures}},
        HP::Proliant::Component::TemperatureSubsystem::Temperature->new(%{$_}));
  }
}


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->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} = lc SNMP::Utils::get_object_value(
      $snmpwalk, $cpqHeThermalSystemFanStatus,
      $cpqHeThermalSystemFanStatusValue);
  $self->{cpustatus} = lc SNMP::Utils::get_object_value(
      $snmpwalk, $cpqHeThermalCpuFanStatus,
      $cpqHeThermalCpuFanStatusValue);
}

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;
  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},
        );
      }
    }
  }
  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
    $self->update_si_with_si();
    return;
  } 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 füllen
  # die eigentliche bewertung findet eins höher 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::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 => [],
    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 $cntlindex = 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 (/([\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+)/) {
      $tmpaccel->{$slot}->{cpqDaAccelCntlrIndex} = $cntlindex;
      $tmpaccel->{$slot}->{cpqDaAccelSlot} = $slot;
      $tmpaccel->{$slot}->{cpqDaAccelCondition} = lc $1;
      $tmpaccel->{$slot}->{cpqDaAccelStatus} = 'enabled';
    } elsif (/Battery.* Status: (\w+)/) {
      # sowas gibts auch Battery/Capacitor Status: OK
      $tmpaccel->{$slot}->{cpqDaAccelBattery} = lc $1;
    } elsif (/^\s*$/) {
    }
  }
  $cntlindex = 0;
  $ldriveindex = 0;
  $pdriveindex = 0;
  foreach (split(/\n/, $hpacucli)) {
    next unless /^config/;
    next if /^config\s*$/;
    s/^config\s*//;
    if (/([\s\w]+) in Slot\s+(\d+)/) {
      $slot = $2;
      $cntlindex++;
      $pdriveindex = 1;
    } 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)
      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};
      $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}->{$_};
      }
    }
  }

  foreach my $slot (keys %{$tmpcntl}) {
    if (exists $tmpcntl->{$slot}->{cpqDaCntlrModel} &&
        ! $self->identified($tmpcntl->{$slot}->{cpqDaCntlrModel})) {
      delete $tmpcntl->{$slot};
      delete $tmpaccel->{$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 %{$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 =~ /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 => [],
    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",
      cpqDaAccelSlot => "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",
      }
  };
    
  # 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 => [],
    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});
  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} 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});
  }
  foreach (@{$self->{accelerators}}) {
    $_->check();
  } 
  foreach (@{$self->{logical_drives}}) {
    $_->check();
  } 
  foreach (@{$self->{physical_drives}}) {
    $_->check();
  } 
  foreach (@{$self->{spare_drives}}) {
    $_->check();
  } 
} 

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->{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},
    blacklisted => 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->{cpqDaAccelStatus} ne "enabled") {
  } elsif ($self->{cpqDaAccelCondition} ne "ok") {
    $self->add_message(CRITICAL, "controller accelerator needs attention");
  }
  $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} ne "ok") {
    # (other) failed degraded
    $self->add_message(CRITICAL, "controller accelerator battery needs attention");
  } 
}

sub dump {
  my $self = shift;
  printf "[ACCELERATOR]\n";
  foreach (qw(cpqDaAccelCntlrIndex cpqDaAccelBattery
      cpqDaAccelStatus cpqDaAccelCondition)) {
    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|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 "[LOGICAL_DRIVE]\n";
  foreach (qw(cntrlcpqDaCntlrIndex cpqDaCntlrIndex size level status condition)) {
    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 => [],
    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 => [],
    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 => [],
    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;
  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;
  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;
  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 => [],
    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 => [],
    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 => [],
    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},
    cpqScsiCntlrIndex => $params{cpqScsiCntlrIndex},
    cpqScsiCntlrBusIndex => $params{cpqScsiCntlrBusIndex},
    cpqScsiCntlrSlot => $params{cpqScsiCntlrSlot},
    cpqScsiCntlrStatus => $params{cpqScsiCntlrStatus},
    cpqScsiCntlrCondition => $params{cpqScsiCntlrCondition},
    cpqScsiCntlrHwLocation => $params{cpqScsiCntlrHwLocation},
    blacklisted => 0,
  };
  $self->{name} = $params{name} || $self->{cpqScsiCntlrIndex};
  $self->{controllerindex} = $self->{cpqScsiCntlrIndex};
  bless $self, $class;
  return $self;
}

sub check {
  my $self = shift;
  if ($self->{cpqScsiCntlrCondition} eq 'other') {
    if (scalar(@{$self->{physical_drives}})) {
      $self->add_message(CRITICAL,
          sprintf 'scsi controller in slot %s needs attention',
              $self->{cpqScsiCntlrSlot});
      $self->add_info(sprintf 'scsi controller in slot %s needs attention',
          $self->{cpqScsiCntlrSlot});
    } else {
      $self->add_info(sprintf 'scsi controller in slot %s is ok and unused',
          $self->{cpqScsiCntlrSlot});
      $self->{blacklisted} = 1;
    }
  } elsif ($self->{cpqScsiCntlrCondition} ne 'ok') {
    $self->add_message(CRITICAL,
        sprintf 'scsi controller in slot %s needs attention',
            $self->{cpqScsiCntlrSlot});
    $self->add_info(sprintf 'scsi controller in slot %s needs attention',
        $self->{cpqScsiCntlrSlot});
  } else {
    $self->add_info(sprintf 'scsi controller in slot %s is ok',
        $self->{cpqScsiCntlrSlot});
  }
  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},
    cpqScsiLogDrvCntlrIndex => $params{cpqScsiLogDrvCntlrIndex},
    cpqScsiLogDrvBusIndex => $params{cpqScsiLogDrvBusIndex},
    cpqScsiLogDrvIndex => $params{cpqScsiLogDrvIndex},
    cpqScsiLogDrvFaultTol => $params{cpqScsiLogDrvFaultTol},
    cpqScsiLogDrvStatus => $params{cpqScsiLogDrvStatus},
    cpqScsiLogDrvSize => $params{cpqScsiLogDrvSize},
    cpqScsiLogDrvPhyDrvIDs => $params{cpqScsiLogDrvPhyDrvIDs},
    cpqScsiLogDrvCondition => $params{cpqScsiLogDrvCondition},
    blacklisted => 0,
  };
  bless $self, $class;
  $self->{name} = $params{name} || 
      $self->{cpqScsiLogDrvCntlrIndex}.':'.
      $self->{cpqScsiLogDrvBusIndex}.':'.
      $self->{cpqScsiLogDrvIndex}; ####vorerst
  $self->{controllerindex} = $self->{cpqScsiLogDrvCntlrIndex};
  return $self;
}

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

sub dump {
  my $self = shift;
  printf "[LOGICAL_DRIVE]\n";
  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},
    cpqScsiPhyDrvCntlrIndex => $params{cpqScsiPhyDrvCntlrIndex},
    cpqScsiPhyDrvBusIndex => $params{cpqScsiPhyDrvBusIndex},
    cpqScsiPhyDrvIndex => $params{cpqScsiPhyDrvIndex},
    cpqScsiPhyDrvStatus => $params{cpqScsiPhyDrvStatus},
    cpqScsiPhyDrvSize => $params{cpqScsiPhyDrvSize},
    cpqScsiPhyDrvCondition => $params{cpqScsiPhyDrvCondition},
    blacklisted => 0,
  };
  $self->{name} = $params{name} || 
      $self->{cpqScsiPhyDrvCntlrIndex}.':'.$self->{cpqScsiPhyDrvIndex}; 
  $self->{controllerindex} = $self->{cpqScsiPhyDrvCntlrIndex};
  bless $self, $class;
  return $self;
}

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

sub dump {
  my $self = shift;
  printf "[PHYSICAL_DRIVE]\n";
  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 => [],
    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 => [],
    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 => [],
    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;
  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;
  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;
  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 => [],
    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 => [],
    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",
          2 => "fchc-p",
          3 => "fchc-e",
          4 => "fchc64",
          5 => "sa-sam",
          6 => "fca-2101",
          7 => "sw64-33",
          8 => "fca-221x",
          9 => "dpfcmc",
      },
      cpqFcaHostCntlrStatusValue => {
          1 => "other",
          2 => "ok",
          3 => "failed",
          4 => "shutdown",
          5 => "loopDegraded",
          6 => "loopFailed",
      },
      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",
      },
      cpqFcaCntlrStatusValue => {
          1 => "other",
          2 => "ok",
          3 => "failed",
          4 => "offline",
          4 => "redundantPathOffline",
      },
      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 => [],
    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 zusammenführen",
      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} 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},
    cpqFcaHostCntlrIndex => $params{cpqFcaHostCntlrIndex},
    cpqFcaHostCntlrSlot => $params{cpqFcaHostCntlrSlot},
    cpqFcaHostCntlrModel => $params{cpqFcaHostCntlrModel},
    cpqFcaHostCntlrStatus => $params{cpqFcaHostCntlrStatus},
    cpqFcaHostCntlrCondition => $params{cpqFcaHostCntlrCondition},
    cpqFcaHostCntlrOverallCondition => $params{cpqFcaHostCntlrOverallCondition},
    blacklisted => 0,
  };
  $self->{name} = $params{name} || $self->{cpqFcaHostCntlrIndex};
  $self->{controllerindex} = $self->{cpqFcaHostCntlrIndex};
  $self->{ident} = sprintf '%s in slot %s',
      $self->{cpqFcaHostCntlrIndex}, $self->{cpqFcaHostCntlrSlot};
  bless $self, $class;
  return $self;
}

sub check {
  my $self = shift;
  if ($self->is_blacklisted('fcahc',
      $self->{cpqFcaHostCntlrSlot}.'-'.$self->{cpqFcaHostCntlrIndex})) {
    $self->add_info(sprintf 'fcal host controller %s is %s (blacklisted)',
        $self->{ident}, $self->{cpqFcaHostCntlrCondition});
  } elsif ($self->{cpqFcaHostCntlrCondition} eq 'other') {
    $self->add_message(CRITICAL, 
        sprintf 'fcal host controller %s needs attention (%s)',
            $self->{ident}, $self->{cpqFcaHostCntlrStatus});
    $self->add_info(sprintf 'fcal host controller %s needs attention (%s)',
        $self->{ident}, $self->{cpqFcaHostCntlrStatus});
  } elsif ($self->{cpqFcaHostCntlrCondition} ne 'ok') {
    $self->add_message(CRITICAL, 
        sprintf 'fcal host controller %s needs attention (%s)',
            $self->{ident}, $self->{cpqFcaHostCntlrStatus});
    $self->add_info(sprintf 'fcal host controller %s needs attention (%s)',
        $self->{ident}, $self->{cpqFcaHostCntlrStatus});
  } else {
    $self->add_info(sprintf 'fcal host controller %s is ok',
        $self->{ident});
  }
  if ($self->is_blacklisted('fcahc',
      $self->{cpqFcaHostCntlrSlot}.'-'.$self->{cpqFcaHostCntlrIndex})) {
  } elsif ($self->{cpqFcaHostCntlrOverallCondition} ne 'ok') {
    $self->add_message(CRITICAL, 
        sprintf 'fcal host controller %s reports problems (%s)',
            $self->{ident}, $self->{cpqFcaHostCntlrStatus});
    $self->add_info(sprintf 'fcal host controller %s reports problems (%s)',
        $self->{ident}, $self->{cpqFcaHostCntlrStatus});
  }
}

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},
    cpqFcaCntlrBoxIndex => $params{cpqFcaCntlrBoxIndex},
    cpqFcaCntlrBoxIoSlot => $params{cpqFcaCntlrBoxIoSlot},
    cpqFcaCntlrModel => $params{cpqFcaCntlrModel},
    cpqFcaCntlrStatus => $params{cpqFcaCntlrStatus},
    cpqFcaCntlrCondition => $params{cpqFcaCntlrCondition},
    blacklisted => 0,
  };
  $self->{name} = $params{name} || 
      $self->{cpqFcaCntlrBoxIndex}.':'.$self->{cpqFcaCntlrBoxIoSlot};
  $self->{controllerindex} = $self->{cpqFcaCntlrBoxIndex};
  $self->{ident} = sprintf 'in box %s/slot %s',
      $self->{cpqFcaCntlrBoxIndex}, $self->{cpqFcaCntlrBoxIoSlot};
  bless $self, $class;
  return $self;
}

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

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},
    cpqFcaAccelBoxIndex => $params{cpqFcaAccelBoxIndex},
    cpqFcaAccelBoxIoSlot => $params{cpqFcaAccelBoxIoSlot},
    cpqFcaAccelStatus => $params{cpqFcaAccelStatus},
    cpqFcaAccelErrCode => $params{cpqFcaAccelErrCode},
    cpqFcaAccelBatteryStatus => $params{cpqFcaAccelBatteryStatus},
    cpqFcaAccelCondition => $params{cpqFcaAccelCondition},
    blacklisted => 0,
  };
  $self->{name} = $params{name} ||
      $self->{cpqFcaAccelBoxIndex}.':'.$self->{cpqFcaAccelBoxIoSlot};
  $self->{controllerindex} = $self->{cpqFcaAccelBoxIndex};
  $self->{ident} = sprintf 'in box %s/slot %s',
      $self->{cpqFcaAccelBoxIndex}, $self->{cpqFcaAccelBoxIoSlot};
  bless $self, $class;
  return $self;
}

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

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},
    cpqFcaLogDrvBoxIndex => $params{cpqFcaLogDrvBoxIndex},
    cpqFcaLogDrvIndex => $params{cpqFcaLogDrvIndex},
    cpqFcaLogDrvFaultTol => $params{cpqFcaLogDrvFaultTol},
    cpqFcaLogDrvStatus => $params{cpqFcaLogDrvStatus},
    cpqFcaLogDrvPercentRebuild => $params{cpqFcaLogDrvPercentRebuild},
    cpqFcaLogDrvSize => $params{cpqFcaLogDrvSize},
    cpqFcaLogDrvPhyDrvIDs => $params{cpqFcaLogDrvPhyDrvIDs},
    cpqFcaLogDrvCondition => $params{cpqFcaLogDrvCondition},
    blacklisted => 0,
  };
  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});
  if ($self->{cpqFcaLogDrvCondition} ne "ok") {
    if ($self->{cpqFcaLogDrvStatus} =~ 
        /rebuild|recovering|expand/) {
      $self->add_message(WARNING,
          sprintf "logical drive %s is %s (%s)", 
              $self->{name}, $self->{cpqFcaLogDrvStatus},
              $self->{cpqFcaLogDrvFaultTol});
    } else {
      $self->add_message(CRITICAL,
          sprintf "logical drive %s is %s (%s)",
              $self->{name}, $self->{cpqFcaLogDrvStatus},
              $self->{cpqFcaLogDrvFaultTol});
    }
  } 
  $self->add_info(
      sprintf "logical drive %s is %s (%s)", $self->{name},
          $self->{cpqFcaLogDrvStatus}, $self->{cpqFcaLogDrvFaultTol});
}

sub dump {
  my $self = shift;
  printf "[LOGICAL_DRIVE]\n";
  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},
    cpqFcaPhyDrvBoxIndex => $params{cpqFcaPhyDrvBoxIndex},
    cpqFcaPhyDrvIndex => $params{cpqFcaPhyDrvIndex},
    cpqFcaPhyDrvModel => $params{cpqFcaPhyDrvModel},
    cpqFcaPhyDrvBay => $params{cpqFcaPhyDrvBay},
    cpqFcaPhyDrvStatus => $params{cpqFcaPhyDrvStatus},
    cpqFcaPhyDrvCondition => $params{cpqFcaPhyDrvCondition},
    cpqFcaPhyDrvSize => $params{cpqFcaPhyDrvSize},
    cpqFcaPhyDrvBusNumber => $params{cpqFcaPhyDrvBusNumber},
    blacklisted => 0,
  };
  $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});
  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,
        sprintf "physical drive %s is %s", 
            $self->{name}, $self->{cpqFcaPhyDrvStatus});
  }
  $self->add_info(
      sprintf "physical drive %s is %s", 
          $self->{name}, $self->{cpqFcaPhyDrvStatus});
}

sub dump {
  my $self = shift;
  printf "[PHYSICAL_DRIVE]\n";
  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 zusammenführen",
      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 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 => {},
      logical_drives => {},
      physical_drives => {},
      spare_drives => {},
  };
  # found->{komponente}->{controllerindex} ist ein array
  # von teilen, die zu einem controller gehoeren
  foreach my $item (qw(accelerators logical_drives physical_drives spare_drives)) {
    foreach (@{$self->{$item}}) {
      $found->{item}->{$_->{controllerindex}} = []
          unless exists $found->{$item}->{$_->{controllerindex}};
      push(@{$found->{$item}->{$_->{controllerindex}}}, $_);
    }
  }
  foreach my $item (qw(accelerators 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}});
}


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,
      disk_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_disk_subsystem();
    $self->auto_blacklist();
    $self->check_cpus();
    $self->check_powersupplies();
    $self->check_fan_subsystem();
    $self->check_temperatures();
    $self->check_memory_subsystem();
    $self->check_disk_subsystem();
  }
}

sub identify {
  my $self = shift;
  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"
  );
  $self->{runtime}->{options}->{buggy_firmware} =
      grep /^$self->{romversion}/, @buggyfirmwares;
}

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_disk_subsystem {
  my $self = shift;
  $self->{components}->{disk_subsystem} =
      HP::Proliant::Component::DiskSubsystem->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_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 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 ($< != 0) {
      $hpasmcli = "sudo ".$hpasmcli;
    }
    $self->trace(2, sprintf "calling %s\n", $hpasmcli);
    $self->check_daemon();
    if (! $self->{runtime}->{plugin}->check_messages()) {
      $self->check_hpasm_client($hpasmcli);
      if (! $self->{runtime}->{plugin}->check_messages()) {
        foreach my $component (qw(server fans temp dimm powersupply)) {
          if (open HPASMCLI, "$hpasmcli -s \"show $component\"|") {
            my @output = <HPASMCLI>;
            close HPASMCLI;
            $self->{rawdata} .= join("\n", map {
                $component.' '.$_;
            } @output);
          }
        }
        if ($self->{runtime}->{options}->{hpacucli}) { 
          #1 oder 0. pfad selber finden
          my $hpacucli = undef;
          if (-e '/usr/sbin/hpacucli') {
            $hpacucli = '/usr/sbin/hpacucli';
          } elsif (-e '/usr/local/sbin/hpacucli') {
            $hpacucli = '/usr/local/sbin/hpacucli';
          } else {
            $hpacucli = $hpasmcli;
            $hpacucli =~ s/^sudo\s*//;
            $hpacucli =~ s/hpasmcli/hpacucli/;
            $hpacucli = -e $hpacucli ? $hpacucli : undef;
          }
          if ($hpacucli) {
            if ($< != 0) {
              $hpacucli = "sudo ".$hpacucli;
            }
            $self->trace(2, sprintf "calling %s\n", $hpacucli);
            $self->check_hpacu_client($hpacucli);
            if (! $self->{runtime}->{plugin}->check_messages()) {
              if (open HPACUCLI, "$hpacucli ctrl all show config 2>&1|") {
                my @output = <HPACUCLI>;
                close HPACUCLI;
                $self->{rawdata} .= join("\n", map {
                    'config '.$_;
                } @output);
              }
              if (open HPACUCLI, "$hpacucli ctrl all show status 2>&1|") {
                my @output = <HPACUCLI>;
                close HPACUCLI;
                $self->{rawdata} .= join("\n", map {
                    'status '.$_;
                } @output);
              }
            }
          } else {
            if ($self->{runtime}->{options}->{noinstlevel} eq 'ok') {
              $self->add_message(OK,
                  'hpacucli is not installed. let\'s hope the best...');
            } else {
              $self->add_message(
                  uc $self->{runtime}->{options}->{noinstlevel},
                  'hpacucli is not installed.');
            }
          }
        }
      }
    }
  }
}

sub check_daemon {
  my $self = shift;
  my $multiproc_os_signatures_files = {
      '/etc/SuSE-release' => 'VERSION\s*=\s*8',
      '/etc/trustix-release' => '.*',
      '/etc/redhat-release' => '.*Pensacola.*',
      '/etc/debian_version' => '3\.1',
      '/etc/issue' => '.*Kernel 2\.4\.9-vmnix2.*', # VMware ESX Server 2.5.4
  };
  if (open PS, "/bin/ps -e -ocmd|") {
    my $numprocs = 0;
    my $numcliprocs = 0;
    my @procs = <PS>;
    close PS;
    $numprocs = grep /hpasm.*d$/, map { (split /\s+/, $_)[0] } @procs;
    $numcliprocs = grep /hpasmcli/, grep !/check_hpasm/, @procs;
    if (! $numprocs ) {
      $self->add_message(CRITICAL, 'hpasmd needs to be restarted');
    } elsif ($numprocs > 1) {
      my $known = 0;
      foreach my $osfile (keys %{$multiproc_os_signatures_files}) {
        if (-f $osfile) {
          open OSSIG, $osfile;
          if (grep /$multiproc_os_signatures_files->{$osfile}/, <OSSIG>) {
            $known = 1;
          }
          close OSSIG;
        }
      }
      if (! $known) {
        $self->add_message(UNKNOWN, 'multiple hpasmd procs');
      }
    }
    if ($numcliprocs == 1) {
      $self->add_message(UNKNOWN, 'another hpasmdcli is running');
    } elsif ($numcliprocs > 1) {
      $self->add_message(UNKNOWN, 'hanging hpasmdcli processes');
    }
  }
}

sub check_hpasm_client {
  my $self = shift;
  my $hpasmcli = shift;
  if (open HPASMCLI, "$hpasmcli -s help 2>&1 |") {
    my @output = <HPASMCLI>;
    close HPASMCLI;
    if (grep /Could not communicate with hpasmd/, @output) {
      $self->add_message(CRITICAL, 'hpasmd needs to be restarted');
    } elsif (grep /(asswor[dt]:)|(You must be root)/, @output) {
      $self->add_message(UNKNOWN,
          sprintf "insufficient rights to call %s", $hpasmcli);
    } elsif (grep /must have a tty/, @output) {
      $self->add_message(CRITICAL,
          'sudo must be configured with requiretty=no (man sudo)');
    } elsif (! grep /CLEAR/, @output) {
      $self->add_message(UNKNOWN,
          sprintf "insufficient rights to call %s", $hpasmcli);
    }
  } else {
    $self->add_message(UNKNOWN,
        sprintf "insufficient rights to call %s", $hpasmcli);
  }
}

sub check_hpacu_client {
  my $self = shift;
  my $hpacucli = shift;
  if (open HPACUCLI, "$hpacucli help 2>&1 |") {
    my @output = <HPACUCLI>;
    close HPACUCLI;
    if (grep /Another instance of hpacucli is running/, @output) {
      $self->add_message(UNKNOWN, 'another hpacucli is running');
    } elsif (grep /You need to have administrator rights/, @output) {
      $self->add_message(UNKNOWN,
          sprintf "insufficient rights to call %s", $hpacucli);
    } elsif (grep /(asswor[dt]:)|(You must be root)/, @output) {
      $self->add_message(UNKNOWN,
          sprintf "insufficient rights to call %s", $hpacucli);
    } elsif (! grep /CLI Syntax/, @output) {
      $self->add_message(UNKNOWN,
          sprintf "insufficient rights to call %s", $hpacucli);
    }
  } else {
    $self->add_message(UNKNOWN,
        sprintf "insufficient rights to call %s", $hpacucli);
  }
}

sub set_serial {
  my $self = shift;
  foreach (grep(/^server/, split(/\n/, $self->{rawdata}))) {
    if (/System\s+:\s+(.*[^\s])/) {
      $self->{product} = lc $1;
    } elsif (/Serial No\.\s+:\s+(\w+)/) {
      $self->{serial} = $1;
    } elsif (/ROM version\s+:\s+(.*[^\s])/) {
      $self->{romversion} = $1;
    }
  }
  $self->{serial} = $self->{serial};
  $self->{product} = lc $self->{product};
  $self->{romversion} = $self->{romversion};
}


package HP::Proliant::SNMP;

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

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

sub collect {
  my $self = shift;
  if ($self->{runtime}->{plugin}->opts->snmpwalk) {
    my $cpqSeMibCondition = '1.3.6.1.4.1.232.1.1.3.0'; # 2=ok
    my $cpqHeMibCondition = '1.3.6.1.4.1.232.6.1.3.0'; # hat nicht jeder
    if ($self->{productname} =~ /4LEE/) {
      # rindsarsch!
      $self->{rawdata}->{$cpqHeMibCondition} = 0;
    }
    if (! exists $self->{rawdata}->{$cpqHeMibCondition} &&
        ! exists $self->{rawdata}->{$cpqSeMibCondition}) { # vlt. geht doch was
        $self->add_message(CRITICAL,
            'snmpwalk returns no health data (cpqhlth-mib)');
    }
  } else {
    my $net_snmp_version = Net::SNMP->VERSION(); # 5.002000 or 6.000000
    #$params{'-translate'} = [
    #  -all => 0x0
    #];
    my ($session, $error) = 
        Net::SNMP->session(%{$self->{runtime}->{snmpparams}});
    if (! defined $session) {
      $self->{plugin}->add_message(CRITICAL, 'cannot create session object');
      $self->trace(1, Data::Dumper::Dumper($self->{runtime}->{snmpparams}));
    } else {
      # revMajor is often used for discovery of hp devices
      my $cpqHeMibRev = '1.3.6.1.4.1.232.6.1';
      my $cpqHeMibRevMajor = '1.3.6.1.4.1.232.6.1.1.0';
      my $cpqHeMibCondition = '1.3.6.1.4.1.232.6.1.3.0';
      my $result = $session->get_request(
          -varbindlist => [$cpqHeMibCondition]
      );
      if ($self->{productname} =~ /4LEE/) {
        # rindsarsch!
        $result->{$cpqHeMibCondition} = 0;
      }
      if (!defined($result) || 
          $result->{$cpqHeMibCondition} eq 'noSuchInstance' ||
          $result->{$cpqHeMibCondition} eq 'noSuchObject' ||
          $result->{$cpqHeMibCondition} eq 'endOfMibView') {
        $self->add_message(CRITICAL,
            'snmpwalk returns no health data (cpqhlth-mib)');
        $session->close;
      } else {
        # this is not reliable. many agents return 4=failed
        #if ($result->{$cpqHeMibCondition} != 2) {
        #  $obstacle = "cmapeerstart";
        #}
      }
    }
    if (! $self->{runtime}->{plugin}->check_messages()) {
      # snmp peer is alive
      $self->trace(2, sprintf "Protocol is %s", 
          $self->{runtime}->{snmpparams}->{'-version'});
      my $cpqStdEquipment = "1.3.6.1.4.1.232";
      my $cpqSeProcessor =  "1.3.6.1.4.1.232.1.2.2";
      my $cpqSeRom =        "1.3.6.1.4.1.232.1.2.6";
      my $cpqHeComponent =  "1.3.6.1.4.1.232.6.2";
      my $cpqHePWSComponent = "1.3.6.1.4.1.232.6.2.9";
      my $cpqHeThermal = "1.3.6.1.4.1.232.6.2.6";
      #my $cpqHeFComponent = "1.3.6.1.4.1.232.6.2.6.7";
      #my $cpqHeTComponent = "1.3.6.1.4.1.232.6.2.6.8";
      my $cpqHeMComponent = "1.3.6.1.4.1.232.6.2.14";
      my $cpqDaComponent =  "1.3.6.1.4.1.232.3.2";
      my $cpqSasComponent =  "1.3.6.1.4.1.232.5";
      my $cpqIdeComponent =  "1.3.6.1.4.1.232.14";
      my $cpqFcaComponent =  "1.3.6.1.4.1.232.16.2";
      my $cpqSiComponent =  "1.3.6.1.4.1.232.2.2";
      $session->translate;
      my $response = {}; #break the walk up in smaller pieces
      my $tic = time; my $tac = $tic;
      my $response1 = $session->get_table(
          -baseoid => $cpqSeProcessor);
      $tac = time;
      $self->trace(2, sprintf "%03d seconds for walk cpqSeProcessor (%d oids)",
          $tac - $tic, scalar(keys %{$response1}));
      # Walk for PowerSupply
      $tic = time;
      my $response2p = $session->get_table(
          -maxrepetitions => 1,
          -baseoid => $cpqHePWSComponent);
      if (scalar (keys %{$response2p}) == 0) {
        $self->trace(2, sprintf "maxrepetitions failed. fallback");
        $response2p = $session->get_table(
            -baseoid => $cpqHePWSComponent);
      }
      $tac = time;
      $self->trace(2, sprintf "%03d seconds for walk cpqHePWSComponent (%d oids)",
          $tac - $tic, scalar(keys %{$response2p}));

      # Walk for Fans/Temp/Overall
      $tic = time;
      my $response2f = $session->get_table(
          -maxrepetitions => 1,
          -baseoid => $cpqHeThermal);
      if (scalar (keys %{$response2f}) == 0) {
        $self->trace(2, sprintf "maxrepetitions failed. fallback");
        $response2f = $session->get_table(
            -baseoid => $cpqHeThermal);
      }
      $tac = time;
      $self->trace(2, sprintf "%03d seconds for walk cpqHeThermal (%d oids)",
          $tac - $tic, scalar(keys %{$response2f}));

#      # Walk for Fans
#      $tic = time;
#      my $response2f = $session->get_table(
#          -maxrepetitions => 1,
#          -baseoid => $cpqHeFComponent);
#      if (scalar (keys %{$response2f}) == 0) {
#        $self->trace(2, sprintf "maxrepetitions failed. fallback");
#        $response2f = $session->get_table(
#            -baseoid => $cpqHeFComponent);
#      }
#      $tac = time;
#      $self->trace(2, sprintf "%03d seconds for walk cpqHeFComponent (%d oids)",
#          $tac - $tic, scalar(keys %{$response2f}));
#      # Walk for Temp
#      $tic = time;
#      my $response2t = $session->get_table(
#          -maxrepetitions => 1,
#          -baseoid => $cpqHeTComponent);
#      if (scalar (keys %{$response2t}) == 0) {
#        $self->trace(2, sprintf "maxrepetitions failed. fallback");
#        $response2t = $session->get_table(
#            -baseoid => $cpqHeTComponent);
#      }
#      $tac = time;
#      $self->trace(2, sprintf "%03d seconds for walk cpqHeTComponent (%d oids)",
#          $tac - $tic, scalar(keys %{$response2t}));
      # Walk for Mem
      $tic = time;
      my $response2m = $session->get_table(
          -maxrepetitions => 1,
          -baseoid => $cpqHeMComponent);
      if (scalar (keys %{$response2m}) == 0) {
        $self->trace(2, sprintf "maxrepetitions failed. fallback");
        $response2m = $session->get_table(
            -baseoid => $cpqHeMComponent);
      }
      $tac = time;
      $self->trace(2, sprintf "%03d seconds for walk cpqHeMComponent (%d oids)",
          $tac - $tic, scalar(keys %{$response2m}));
      #
      $tic = time;
      my $response3 = $session->get_table(
          -baseoid => $cpqDaComponent);
      $tac = time;
      $self->trace(2, sprintf "%03d seconds for walk cpqDaComponent (%d oids)",
          $tac - $tic, scalar(keys %{$response3}));
      $tic = time;
      my $response4 = $session->get_table(
          -baseoid => $cpqSiComponent);
      $tac = time;
      $self->trace(2, sprintf "%03d seconds for walk cpqSiComponent (%d oids)",
          $tac - $tic, scalar(keys %{$response4}));
      $tic = time;
      my $response5 = $session->get_table(
          -baseoid => $cpqSeRom);
      $tac = time;
      $self->trace(2, sprintf "%03d seconds for walk cpqSeRom (%d oids)",
          $tac - $tic, scalar(keys %{$response5}));
      $tic = time;
      my $response6 = $session->get_table(
          -baseoid => $cpqSasComponent);
      $tac = time;
      $self->trace(2, sprintf "%03d seconds for walk cpqSasComponent (%d oids)",
          $tac - $tic, scalar(keys %{$response6}));
      $tic = time;
      my $response7 = $session->get_table(
          -baseoid => $cpqIdeComponent);
      $tac = time;
      $self->trace(2, sprintf "%03d seconds for walk cpqIdeComponent (%d oids)",
          $tac - $tic, scalar(keys %{$response7}));
      $tic = time;
      my $response8 = $session->get_table(
          -baseoid => $cpqFcaComponent);
      $tac = time;
      $self->trace(2, sprintf "%03d seconds for walk cpqFcaComponent (%d oids)",
          $tac - $tic, scalar(keys %{$response8}));
      $tic = time;
      $session->close;
      map { $response->{$_} = $response1->{$_} } keys %{$response1};
      map { $response->{$_} = $response2p->{$_} } keys %{$response2p};
      map { $response->{$_} = $response2f->{$_} } keys %{$response2f};
#      map { $response->{$_} = $response2t->{$_} } keys %{$response2t};
      map { $response->{$_} = $response2m->{$_} } keys %{$response2m};
      map { $response->{$_} = $response3->{$_} } keys %{$response3};
      map { $response->{$_} = $response4->{$_} } keys %{$response4};
      map { $response->{$_} = $response5->{$_} } keys %{$response5};
      map { $response->{$_} = $response6->{$_} } keys %{$response6};
      map { $response->{$_} = $response7->{$_} } keys %{$response7};
      map { $response->{$_} = $response8->{$_} } keys %{$response8};
      map { $response->{$_} =~ s/^\s+//; $response->{$_} =~ s/\s+$//; }
          keys %$response;
      $self->{rawdata} = $response;
    }
  }
  return $self->{runtime}->{plugin}->check_messages();
}

sub set_serial {
  my $self = shift;

  my $cpqSiSysSerialNum = "1.3.6.1.4.1.232.2.2.2.1.0";
  my $cpqSiProductName = "1.3.6.1.4.1.232.2.2.4.2.0";
  my $cpqSeSysRomVer = "1.3.6.1.4.1.232.1.2.6.1.0";

  $self->{serial} = 
      SNMP::Utils::get_object($self->{rawdata}, $cpqSiSysSerialNum);
  $self->{product} =
      SNMP::Utils::get_object($self->{rawdata}, $cpqSiProductName);
  $self->{romversion} =
      SNMP::Utils::get_object($self->{rawdata}, $cpqSeSysRomVer);
  if ($self->{romversion} && $self->{romversion} =~
      #/(\d{2}\/\d{2}\/\d{4}).*?([ADP]{1}\d{2}).*/) {
      /(\d{2}\/\d{2}\/\d{4}).*?Family.*?([A-Z]{1})(\d+).*/) {
    $self->{romversion} = sprintf("%s%02d %s", $2, $3, $1);
  } elsif ($self->{romversion} && $self->{romversion} =~
      /([ADP]{1}\d{2})\-(\d{2}\/\d{2}\/\d{4})/) {
    $self->{romversion} = sprintf("%s %s", $1, $2);
  }
  if (!$self->{serial} && $self->{romversion}) {
    # this probably is a very, very old server.
    $self->{serial} = "METHUSALEM";
    $self->{runtime}->{scrapiron} = 1;
  }
  $self->{serial} = $self->{serial};
  $self->{product} = lc $self->{product};
  $self->{romversion} = $self->{romversion};
  $self->{runtime}->{product} = $self->{product};
}



package HP::BladeSystem::Component::CommonEnclosureSubsystem;
our @ISA = qw(HP::BladeSystem::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},
    common_enclosures => [],
    common_enclosure_temps => [],
    common_enclosure_fan_subsys => undef,
    common_enclosure_fuses => [],
    common_enclosure_frus => [],
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  bless $self, $class;
  $self->init();
  return $self;
}

sub init {
  my $self = shift;
  # jeweils ein block fuer
  # enclosures, temps, fans, fuses
  # loop ueber oids und entspr. new
  my $oids = {
      cpqRackCommonEnclosureEntry => '1.3.6.1.4.1.232.22.2.3.1.1.1',
      cpqRackCommonEnclosureRack => '1.3.6.1.4.1.232.22.2.3.1.1.1.1',
      cpqRackCommonEnclosureIndex => '1.3.6.1.4.1.232.22.2.3.1.1.1.2',
      cpqRackCommonEnclosureModel => '1.3.6.1.4.1.232.22.2.3.1.1.1.3',
      cpqRackCommonEnclosureSerialNum => '1.3.6.1.4.1.232.22.2.3.1.1.1.7',
      cpqRackCommonEnclosureFWRev => '1.3.6.1.4.1.232.22.2.3.1.1.1.8',
      cpqRackCommonEnclosureName => '1.3.6.1.4.1.232.22.2.3.1.1.1.9',
      cpqRackCommonEnclosureCondition => '1.3.6.1.4.1.232.22.2.3.1.1.1.16',
      cpqRackCommonEnclosureHasServerBlades => '1.3.6.1.4.1.232.22.2.3.1.1.1.17',
      cpqRackCommonEnclosureHasPowerBlades => '1.3.6.1.4.1.232.22.2.3.1.1.1.18',
      cpqRackCommonEnclosureHasNetConnectors => '1.3.6.1.4.1.232.22.2.3.1.1.1.19',
      cpqRackCommonEnclosureHasTempSensors => '1.3.6.1.4.1.232.22.2.3.1.1.1.20',
      cpqRackCommonEnclosureHasFans => '1.3.6.1.4.1.232.22.2.3.1.1.1.21',
      cpqRackCommonEnclosureHasFuses => '1.3.6.1.4.1.232.22.2.3.1.1.1.22',
      cpqRackCommonEnclosureConditionValue => {
          1 => 'other',
          2 => 'ok',
          3 => 'degraded',
          4 => 'failed',
      },
      cpqRackCommonEnclosureHasServerBladesValue => {
          1 => 'false',
          2 => 'true',
      },
  };
  $oids->{cpqRackCommonEnclosureHasPowerBladesValue} =
    $oids->{cpqRackCommonEnclosureHasServerBladesValue};
  $oids->{cpqRackCommonEnclosureHasNetConnectorsValue} =
    $oids->{cpqRackCommonEnclosureHasServerBladesValue};
  $oids->{cpqRackCommonEnclosureHasTempSensorsValue} =
    $oids->{cpqRackCommonEnclosureHasServerBladesValue};
  $oids->{cpqRackCommonEnclosureHasFansValue} =
    $oids->{cpqRackCommonEnclosureHasServerBladesValue};
  $oids->{cpqRackCommonEnclosureHasServerBladesValue} =
    $oids->{cpqRackCommonEnclosureHasServerBladesValue};
  # INDEX { cpqRackCommonEnclosureRack cpqRackCommonEnclosureIndex }
  foreach ($self->get_entries($oids, 'cpqRackCommonEnclosureEntry')) {
    push(@{$self->{common_enclosures}},
        HP::BladeSystem::Component::CommonEnclosureSubsystem::CommonEnclosure->new(%{$_}));
  }

  $self->{common_enclosure_fan_subsys} = HP::BladeSystem::Component::CommonEnclosureSubsystem::FanSubsystem->new(
      rawdata => $self->{rawdata},
      method => $self->{method},
      runtime => $self->{runtime},
  );
}

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

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


package HP::BladeSystem::Component::CommonEnclosureSubsystem::CommonEnclosure;
our @ISA = qw(HP::BladeSystem::Component::CommonEnclosureSubsystem);

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},
    cpqRackCommonEnclosureRack => $params{cpqRackCommonEnclosureRack},
    cpqRackCommonEnclosureIndex => $params{cpqRackCommonEnclosureIndex},
    cpqRackCommonEnclosureModel => $params{cpqRackCommonEnclosureModel},
    cpqRackCommonEnclosureSerialNum => $params{cpqRackCommonEnclosureSerialNum},
    cpqRackCommonEnclosureFWRev => $params{cpqRackCommonEnclosureFWRev},
    cpqRackCommonEnclosureName => $params{cpqRackCommonEnclosureName},
    cpqRackCommonEnclosureCondition => $params{cpqRackCommonEnclosureCondition},
    cpqRackCommonEnclosureHasServerBlades => $params{cpqRackCommonEnclosureHasServerBlades},
    cpqRackCommonEnclosureHasPowerBlades => $params{cpqRackCommonEnclosureHasPowerBlades},
    cpqRackCommonEnclosureHasNetConnectors => $params{cpqRackCommonEnclosureHasNetConnectors},
    cpqRackCommonEnclosureHasTempSensors => $params{cpqRackCommonEnclosureHasTempSensors},
    cpqRackCommonEnclosureHasFans => $params{cpqRackCommonEnclosureHasFans},
    cpqRackCommonEnclosureHasFuses => $params{cpqRackCommonEnclosureHasFuses},
    blacklisted => 0,
    info => undef, 
    extendedinfo => undef,
  };
  $self->{name} = $self->{cpqRackCommonEnclosureRack}.':'.$self->{cpqRackCommonEnclosureIndex};
  bless $self, $class;
  return $self;
}


sub check {
  my $self = shift;
  $self->blacklist('ce', $self->{cpqRackCommonEnclosureName});
  my $info = sprintf 'common enclosure %s condition is %s',
      $self->{cpqRackCommonEnclosureName}, $self->{cpqRackCommonEnclosureCondition};
  $self->add_info($info);
  if ($self->{cpqRackCommonEnclosureCondition} eq 'failed') {
    $self->add_message(CRITICAL, $info);
  } elsif ($self->{cpqRackCommonEnclosureCondition} eq 'degraded') {
    $self->add_message(WARNING, $info);
  } 
}

sub dump {
  my $self = shift;
    printf "[COMMON_ENCLOSURE_%s]\n", $self->{cpqRackCommonEnclosureName};
  foreach (qw(cpqRackCommonEnclosureRack cpqRackCommonEnclosureIndex cpqRackCommonEnclosureModel
      cpqRackCommonEnclosureSerialNum cpqRackCommonEnclosureFWRev cpqRackCommonEnclosureName
      cpqRackCommonEnclosureCondition cpqRackCommonEnclosureHasServerBlades 
      cpqRackCommonEnclosureHasPowerBlades cpqRackCommonEnclosureHasNetConnectors 
      cpqRackCommonEnclosureHasTempSensors cpqRackCommonEnclosureHasFans cpqRackCommonEnclosureHasFuses)) {
    printf "%s: %s\n", $_, $self->{$_};
  }
  printf "\n";
}


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

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},
    blacklisted => 0,
    fans => [],
    info => undef,
    extendedinfo => undef,
  };
  bless $self, $class;
  $self->init();
  return $self;
}

sub init {
  my $self = shift;
  my $oids = {
      cpqRackCommonEnclosureFanEntry => '1.3.6.1.4.1.232.22.2.3.1.3.1',
      cpqRackCommonEnclosureFanRack => '1.3.6.1.4.1.232.22.2.3.1.3.1.1',
      cpqRackCommonEnclosureFanChassis => '1.3.6.1.4.1.232.22.2.3.1.3.1.2',
      cpqRackCommonEnclosureFanIndex => '1.3.6.1.4.1.232.22.2.3.1.3.1.3',
      cpqRackCommonEnclosureFanEnclosureName => '1.3.6.1.4.1.232.22.2.3.1.3.1.4',
      cpqRackCommonEnclosureFanLocation => '1.3.6.1.4.1.232.22.2.3.1.3.1.5',
      cpqRackCommonEnclosureFanPartNumber => '1.3.6.1.4.1.232.22.2.3.1.3.1.6',
      cpqRackCommonEnclosureFanSparePartNumber => '1.3.6.1.4.1.232.22.2.3.1.3.1.7',
      cpqRackCommonEnclosureFanPresent => '1.3.6.1.4.1.232.22.2.3.1.3.1.8',
      cpqRackCommonEnclosureFanRedundant => '1.3.6.1.4.1.232.22.2.3.1.3.1.9',
      cpqRackCommonEnclosureFanRedundantGroupId => '1.3.6.1.4.1.232.22.2.3.1.3.1.10',
      cpqRackCommonEnclosureFanCondition => '1.3.6.1.4.1.232.22.2.3.1.3.1.11',
      cpqRackCommonEnclosureFanEnclosureSerialNum => '1.3.6.1.4.1.232.22.2.3.1.3.1.12',
      cpqRackCommonEnclosureFanPresentValue => {
          1 => 'other',
          2 => 'absent',
          3 => 'present',
      },
      cpqRackCommonEnclosureFanRedundantValue => {
          0 => 'other', # meiner phantasie entsprungen, da sich hp nicht aeussert
          1 => 'other',
          2 => 'notRedundant',
          3 => 'redundant',
      },
      cpqRackCommonEnclosureFanConditionValue => {
          1 => 'other',
          2 => 'ok',
          3 => 'degraded',
          4 => 'failed',
      }
  };
  # INDEX { cpqRackCommonEnclosureFanRack, cpqRackCommonEnclosureFanChassis, cpqRackCommonEnclosureFanIndex }
  foreach ($self->get_entries($oids, 'cpqRackCommonEnclosureFanEntry')) {
    push(@{$self->{fans}},
        HP::BladeSystem::Component::CommonEnclosureSubsystem::FanSubsystem::Fan->new(%{$_}));
  }

}

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

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


package HP::BladeSystem::Component::CommonEnclosureSubsystem::FanSubsystem::Fan;

our @ISA = qw(HP::BladeSystem::Component::CommonEnclosureSubsystem::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},
    method => $params{method},cklisted => 0,
#params
    cpqRackCommonEnclosureFanEntry => $params{cpqRackCommonEnclosureFanEntry},
    cpqRackCommonEnclosureFanRack => $params{cpqRackCommonEnclosureFanRack},
    cpqRackCommonEnclosureFanChassis => $params{cpqRackCommonEnclosureFanChassis},
    cpqRackCommonEnclosureFanIndex => $params{cpqRackCommonEnclosureFanIndex},
    cpqRackCommonEnclosureFanEnclosureName => $params{cpqRackCommonEnclosureFanEnclosureName},
    cpqRackCommonEnclosureFanLocation => $params{cpqRackCommonEnclosureFanLocation},
    cpqRackCommonEnclosureFanPartNumber => $params{cpqRackCommonEnclosureFanPartNumber},
    cpqRackCommonEnclosureFanSparePartNumber => $params{cpqRackCommonEnclosureFanSparePartNumber},
    cpqRackCommonEnclosureFanPresent => $params{cpqRackCommonEnclosureFanPresent},
    cpqRackCommonEnclosureFanRedundant => $params{cpqRackCommonEnclosureFanRedundant},
    cpqRackCommonEnclosureFanRedundantGroupId => $params{cpqRackCommonEnclosureFanRedundantGroupId},
    cpqRackCommonEnclosureFanCondition => $params{cpqRackCommonEnclosureFanCondition},
    cpqRackCommonEnclosureFanEnclosureSerialNum => $params{cpqRackCommonEnclosureFanEnclosureSerialNum},
    info => undef,
    extendedinfo => undef,
  };
  $self->{name} = $self->{cpqRackCommonEnclosureFanRack}.':'.$self->{cpqRackCommonEnclosureFanChassis}.':'.$self->{cpqRackCommonEnclosureFanIndex};
  bless $self, $class;
  return $self;
}

sub check {
  my $self = shift;
  $self->blacklist('f', $self->{name});
  $self->add_info(sprintf 'fan %s is %s, location is %s, redundance is %s',
      $self->{name}, $self->{cpqRackCommonEnclosureFanPresent},
      $self->{cpqRackCommonEnclosureFanLocation},
      $self->{cpqRackCommonEnclosureFanRedundant});
  if ($self->{cpqRackCommonEnclosureFanCondition} eq 'degraded') {
    $self->add_message(WARNING, 'fan %s is %s', $self->{name},
        $self->{cpqRackCommonEnclosureFanCondition});
  } elsif ($self->{cpqRackCommonEnclosureFanCondition} eq 'failed') {
    $self->add_message(CRITICAL, 'fan %s is %s', $self->{name},
        $self->{cpqRackCommonEnclosureFanCondition});
  }

}

sub dump {
  my $self = shift;
  printf "[FAN_%s]\n", $self->{name};
  foreach (qw(cpqRackCommonEnclosureFanRack cpqRackCommonEnclosureFanChassis 
      cpqRackCommonEnclosureFanIndex cpqRackCommonEnclosureFanEnclosureName 
      cpqRackCommonEnclosureFanLocation cpqRackCommonEnclosureFanPartNumber 
      cpqRackCommonEnclosureFanSparePartNumber cpqRackCommonEnclosureFanPresent 
      cpqRackCommonEnclosureFanRedundant cpqRackCommonEnclosureFanRedundantGroupId
      cpqRackCommonEnclosureFanCondition cpqRackCommonEnclosureFanEnclosureSerialNum)) {
    printf "%s: %s\n", $_, $self->{$_};
  }
  printf "info: %s\n", $self->{info};
  printf "\n";
}


package HP::BladeSystem::Component::PowerEnclosureSubsystem;
our @ISA = qw(HP::BladeSystem::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},
    power_enclosures => [],
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  bless $self, $class;
  $self->init();
  return $self;
}

sub init {
  my $self = shift;

# cpqRackPowerEnclosureTable
  my $oids = {
      cpqRackPowerEnclosureEntry => '1.3.6.1.4.1.232.22.2.3.3.1.1',
      cpqRackPowerEnclosureRack => '1.3.6.1.4.1.232.22.2.3.3.1.1.1',
      cpqRackPowerEnclosureIndex => '1.3.6.1.4.1.232.22.2.3.3.1.1.2',
      cpqRackPowerEnclosureName => '1.3.6.1.4.1.232.22.2.3.3.1.1.3',
      cpqRackPowerEnclosureMgmgtBoardSerialNum => '1.3.6.1.4.1.232.22.2.3.3.1.1.4',
      cpqRackPowerEnclosureRedundant => '1.3.6.1.4.1.232.22.2.3.3.1.1.5',
      cpqRackPowerEnclosureLoadBalanced => '1.3.6.1.4.1.232.22.2.3.3.1.1.6',
      cpqRackPowerEnclosureInputPwrType => '1.3.6.1.4.1.232.22.2.3.3.1.1.7',
      cpqRackPowerEnclosurePwrFeedMax => '1.3.6.1.4.1.232.22.2.3.3.1.1.8',
      cpqRackPowerEnclosureCondition => '1.3.6.1.4.1.232.22.2.3.3.1.1.9',
      cpqRackPowerEnclosureRedundantValue => {
          1 => 'other',
          2 => 'notRedundant',
          3 => 'redundant',
      },
      cpqRackPowerEnclosureLoadBalancedValue => {
          0 => 'aechz',
          1 => 'other',
          2 => 'notLoadBalanced',
          3 => 'loadBalanced',
      },
      cpqRackPowerEnclosureInputPwrTypeValue => {
          1 => 'other',
          2 => 'singlePhase',
          3 => 'threePhase',
          4 => 'directCurrent',
      },
      cpqRackPowerEnclosureConditionValue => {
          1 => 'other',
          2 => 'ok',
          3 => 'degraded',
      },
  };
 
 
  # INDEX { cpqRackPowerEnclosureRack, cpqRackPowerEnclosureIndex }
  # dreckada dreck, dreckada
  foreach ($self->get_entries($oids, 'cpqRackPowerEnclosureEntry')) {
    push(@{$self->{power_enclosures}},
        HP::BladeSystem::Component::PowerEnclosureSubsystem::PowerEnclosure->new(%{$_}));
  }
}

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

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


package HP::BladeSystem::Component::PowerEnclosureSubsystem::PowerEnclosure;
our @ISA = qw(HP::BladeSystem::Component::PowerEnclosureSubsystem);

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},
    cpqRackPowerEnclosureEntry => $params{cpqRackPowerEnclosureEntry},
    cpqRackPowerEnclosureRack => $params{cpqRackPowerEnclosureRack},
    cpqRackPowerEnclosureIndex => $params{cpqRackPowerEnclosureIndex},
    cpqRackPowerEnclosureName => $params{cpqRackPowerEnclosureName},
    cpqRackPowerEnclosureMgmgtBoardSerialNum => $params{cpqRackPowerEnclosureMgmgtBoardSerialNum},
    cpqRackPowerEnclosureRedundant => $params{cpqRackPowerEnclosureRedundant},
    cpqRackPowerEnclosureLoadBalanced => $params{cpqRackPowerEnclosureLoadBalanced},
    cpqRackPowerEnclosureInputPwrType => $params{cpqRackPowerEnclosureInputPwrType},
    cpqRackPowerEnclosurePwrFeedMax => $params{cpqRackPowerEnclosurePwrFeedMax},
    cpqRackPowerEnclosureCondition => $params{cpqRackPowerEnclosureCondition},
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  bless $self, $class;
  $self->init();
  return $self;
}

sub check {
  my $self = shift;
  $self->blacklist('pe', $self->{cpqRackPowerEnclosureName});
  my $info = sprintf 'power enclosure %s condition is %s',
      $self->{cpqRackPowerEnclosureName}, $self->{cpqRackPowerEnclosureCondition};
  $self->add_info($info);
  if ($self->{cpqRackPowerEnclosureCondition} eq 'degraded') {
    $self->add_message(WARNING, $info);
  } 
} 
  
sub dump {
  my $self = shift;
    printf "[POWER_ENCLOSURE_%s]\n", $self->{cpqRackPowerEnclosureName};
  foreach (qw(cpqRackPowerEnclosureRack cpqRackPowerEnclosureIndex 
      cpqRackPowerEnclosureName cpqRackPowerEnclosureMgmgtBoardSerialNum
      cpqRackPowerEnclosureRedundant cpqRackPowerEnclosureLoadBalanced
      cpqRackPowerEnclosureInputPwrType cpqRackPowerEnclosurePwrFeedMax
      cpqRackPowerEnclosureCondition)) {
    printf "%s: %s\n", $_, $self->{$_};
  }
  printf "\n";
}



package HP::BladeSystem::Component::PowerSupplySubsystem;
our @ISA = qw(HP::BladeSystem::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},
    power_supplies => [],
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  bless $self, $class;
  $self->init();
  return $self;
}

sub init {
  my $self = shift;
  my $oids = {
      cpqRackPowerSupplyEntry => '1.3.6.1.4.1.232.22.2.5.1.1.1',
      cpqRackPowerSupplyRack => '1.3.6.1.4.1.232.22.2.5.1.1.1.1',
      cpqRackPowerSupplyChassis => '1.3.6.1.4.1.232.22.2.5.1.1.1.2',
      cpqRackPowerSupplyIndex => '1.3.6.1.4.1.232.22.2.5.1.1.1.3',
      cpqRackPowerSupplyEnclosureName => '1.3.6.1.4.1.232.22.2.5.1.1.1.4',
      cpqRackPowerSupplyMaxPwrOutput => '1.3.6.1.4.1.232.22.2.5.1.1.1.9',
      cpqRackPowerSupplyCurPwrOutput => '1.3.6.1.4.1.232.22.2.5.1.1.1.10',
      cpqRackPowerSupplyIntakeTemp => '1.3.6.1.4.1.232.22.2.5.1.1.1.12',
      cpqRackPowerSupplyExhaustTemp => '1.3.6.1.4.1.232.22.2.5.1.1.1.13',
      cpqRackPowerSupplyStatus => '1.3.6.1.4.1.232.22.2.5.1.1.1.14',
      cpqRackPowerSupplySupplyInputLineStatus => '1.3.6.1.4.1.232.22.2.5.1.1.1.15',
      cpqRackPowerSupplyPresent => '1.3.6.1.4.1.232.22.2.5.1.1.1.16',
      cpqRackPowerSupplyCondition => '1.3.6.1.4.1.232.22.2.5.1.1.1.17',
      cpqRackPowerSupplySupplyInputLineStatusValue => {
          1 => 'noError',
          2 => 'lineOverVoltage',
          3 => 'lineUnderVoltage',
          4 => 'lineHit',
          5 => 'brownOut',
          6 => 'linePowerLoss',
      },
      cpqRackPowerSupplyStatusValue => {
          1 => 'noError',
          2 => 'generalFailure',
          3 => 'bistFailure',
          4 => 'fanFailure',
          5 => 'tempFailure',
          6 => 'interlockOpen',
          7 => 'epromFailed',
          8 => 'vrefFailed',
          9 => 'dacFailed',
          10 => 'ramTestFailed',
          11 => 'voltageChannelFailed',
          12 => 'orringdiodeFailed',
          13 => 'brownOut',
          14 => 'giveupOnStartup',
          15 => 'nvramInvalid',
          16 => 'calibrationTableInvalid',
      },
      cpqRackPowerSupplyPresentValue => {
          1 => 'other',
          2 => 'absent',
          3 => 'present',
      },
      cpqRackPowerSupplyConditionValue => {
          1 => 'other',
          2 => 'ok',
          3 => 'degraded',
          4 => 'failed',
      },
  };
 
 
  # INDEX { cpqRackPowerSupplyRack, cpqRackPowerSupplyChassis, cpqRackPowerSupplyIndex }
  # dreckada dreck, dreckada
  foreach ($self->get_entries($oids, 'cpqRackPowerSupplyEntry')) {
    push(@{$self->{power_supplies}},
        HP::BladeSystem::Component::PowerSupplySubsystem::PowerSupply->new(%{$_}));
  }
  my $total_power_output = 0;
  foreach my $powersupply (@{$self->{power_supplies}}) {
    $total_power_output += $powersupply->{cpqRackPowerSupplyCurPwrOutput};
  }
  $self->{runtime}->{plugin}->add_perfdata(
	label => 'total_power_output',
        value => $total_power_output,
        uom => 'Watts',
  );

}

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

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


package HP::BladeSystem::Component::PowerSupplySubsystem::PowerSupply;
our @ISA = qw(HP::BladeSystem::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},
    method => $params{method},
    cpqRackPowerSupplyRack => $params{cpqRackPowerSupplyRack},
    cpqRackPowerSupplyChassis => $params{cpqRackPowerSupplyChassis},
    cpqRackPowerSupplyIndex => $params{cpqRackPowerSupplyIndex},
    cpqRackPowerSupplyEnclosureName => $params{cpqRackPowerSupplyEnclosureName},
    cpqRackPowerSupplyMaxPwrOutput => $params{cpqRackPowerSupplyMaxPwrOutput},
    cpqRackPowerSupplyCurPwrOutput => $params{cpqRackPowerSupplyCurPwrOutput},
    cpqRackPowerSupplyIntakeTemp => $params{cpqRackPowerSupplyIntakeTemp},
    cpqRackPowerSupplyExhaustTemp => $params{cpqRackPowerSupplyExhaustTemp},
    cpqRackPowerSupplyStatus => $params{cpqRackPowerSupplyStatus},
    cpqRackPowerSupplySupplyInputLineStatus => $params{cpqRackPowerSupplySupplyInputLineStatus},
    cpqRackPowerSupplyPresent => $params{cpqRackPowerSupplyPresent},
    cpqRackPowerSupplyCondition => $params{cpqRackPowerSupplyCondition},
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  $self->{name} = $params{cpqRackPowerSupplyRack}.':'.$params{cpqRackPowerSupplyChassis}.':'.$params{cpqRackPowerSupplyIndex};
  bless $self, $class;
  return $self;
}

sub check {
  my $self = shift;
  $self->blacklist('ps', $self->{name});
  my $info = sprintf 'power supply %s is %s, condition is %s', 
      $self->{name}, $self->{cpqRackPowerSupplyPresent},
      $self->{cpqRackPowerSupplyCondition};
  $self->add_info($info) if $self->{cpqRackPowerSupplyPresent} eq 'present' ||
      $self->{runtime}->{options}->{verbose} >= 3;
  if ($self->{cpqRackPowerSupplyPresent} eq 'present') {

    $self->{runtime}->{plugin}->add_perfdata(
        label => sprintf('power_supply_output_%s', $self->{name}),
        value => $self->{cpqRackPowerSupplyCurPwrOutput},
        uom => 'Watts',
    );

    if ($self->{cpqRackPowerSupplyCondition} eq 'degraded') {
      $self->add_message(WARNING, $info);
      $self->add_info(sprintf 'power supply %s status is %s, inp.line status is %s',
          $self->{cpqRackPowerSupplyName}, $self->{cpqRackPowerSupplyStatus},
          $self->{cpqRackPowerSupplySupplyInputLineStatus});
    } elsif ($self->{cpqRackPowerSupplyCondition} eq 'failed') {
      $self->add_message(CRITICAL, $info);
      $self->add_info(sprintf 'power supply %s status is %s, inp.line status is %s',
          $self->{cpqRackPowerSupplyName}, $self->{cpqRackPowerSupplyStatus},
          $self->{cpqRackPowerSupplySupplyInputLineStatus});
    } 
  }
} 
  
sub dump {
  my $self = shift;
    printf "[POWER_SUPPLY%s]\n", $self->{name};
  foreach (qw(cpqRackPowerSupplyRack cpqRackPowerSupplyChassis cpqRackPowerSupplyIndex cpqRackPowerSupplyEnclosureName cpqRackPowerSupplyMaxPwrOutput cpqRackPowerSupplyCurPwrOutput cpqRackPowerSupplyIntakeTemp cpqRackPowerSupplyExhaustTemp cpqRackPowerSupplyStatus cpqRackPowerSupplySupplyInputLineStatus cpqRackPowerSupplyPresent cpqRackPowerSupplyCondition)) {
    printf "%s: %s\n", $_, $self->{$_};
  }
  printf "\n";
}



package HP::BladeSystem::Component::NetConnectorSubsystem;
our @ISA = qw(HP::BladeSystem::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},
    net_connectors => [],
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  bless $self, $class;
  $self->init();
  return $self;
}

sub init {
  my $self = shift;
  my $oids = {
      cpqRackNetConnectorEntry => '1.3.6.1.4.1.232.22.2.6.1.1.1',
      cpqRackNetConnectorRack => '1.3.6.1.4.1.232.22.2.6.1.1.1.1',
      cpqRackNetConnectorChassis => '1.3.6.1.4.1.232.22.2.6.1.1.1.2',
      cpqRackNetConnectorIndex => '1.3.6.1.4.1.232.22.2.6.1.1.1.3',
      cpqRackNetConnectorEnclosureName => '1.3.6.1.4.1.232.22.2.6.1.1.1.4',
      cpqRackNetConnectorName => '1.3.6.1.4.1.232.22.2.6.1.1.1.5',
      cpqRackNetConnectorModel => '1.3.6.1.4.1.232.22.2.6.1.1.1.6',
      cpqRackNetConnectorSerialNum => '1.3.6.1.4.1.232.22.2.6.1.1.1.7',
      cpqRackNetConnectorPartNumber => '1.3.6.1.4.1.232.22.2.6.1.1.1.8',
      cpqRackNetConnectorSparePartNumber => '1.3.6.1.4.1.232.22.2.6.1.1.1.9',
      cpqRackNetConnectorFWRev => '1.3.6.1.4.1.232.22.2.6.1.1.1.10',
      cpqRackNetConnectorType => '1.3.6.1.4.1.232.22.2.6.1.1.1.11',
      cpqRackNetConnectorLocation => '1.3.6.1.4.1.232.22.2.6.1.1.1.12',
      cpqRackNetConnectorPresent => '1.3.6.1.4.1.232.22.2.6.1.1.1.13',
      cpqRackNetConnectorHasFuses => '1.3.6.1.4.1.232.22.2.6.1.1.1.14',
      cpqRackNetConnectorEnclosureSerialNum => '1.3.6.1.4.1.232.22.2.6.1.1.1.15',
      cpqRackNetConnectorTypeValue => {
          0 => 'other', # undefined
          1 => 'other',
          2 => 'active',
          3 => 'passive',
      },
      cpqRackNetConnectorPresentValue => {
          1 => 'other',
          2 => 'absent',
          3 => 'present',
      },
      cpqRackNetConnectorHasFusesValue => {
          -1 => 'false', # wird geliefert, also vermute ich false
          1 => 'false',
          2 => 'true',
      },
  };
 
 
  # INDEX { cpqRackNetConnectorRack, cpqRackNetConnectorChassis, cpqRackNetConnectorIndex }
  # dreckada dreck, dreckada
  foreach ($self->get_entries($oids, 'cpqRackNetConnectorEntry')) {
    push(@{$self->{net_connectors}},
        HP::BladeSystem::Component::NetConnectorSubsystem::NetConnector->new(%{$_}));
  }
}

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

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


package HP::BladeSystem::Component::NetConnectorSubsystem::NetConnector;
our @ISA = qw(HP::BladeSystem::Component::NetConnectorSubsystem);

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},
    cpqRackNetConnectorRack => $params{cpqRackNetConnectorRack},
    cpqRackNetConnectorChassis => $params{cpqRackNetConnectorChassis},
    cpqRackNetConnectorIndex => $params{cpqRackNetConnectorIndex},
    cpqRackNetConnectorEnclosureName => $params{cpqRackNetConnectorEnclosureName},
    cpqRackNetConnectorName => $params{cpqRackNetConnectorName},
    cpqRackNetConnectorModel => $params{cpqRackNetConnectorModel},
    cpqRackNetConnectorSerialNum => $params{cpqRackNetConnectorSerialNum},
    cpqRackNetConnectorPartNumber => $params{cpqRackNetConnectorPartNumber},
    cpqRackNetConnectorSparePartNumber => $params{cpqRackNetConnectorSparePartNumber},
    cpqRackNetConnectorFWRev => $params{cpqRackNetConnectorFWRev},
    cpqRackNetConnectorType => $params{cpqRackNetConnectorType},
    cpqRackNetConnectorLocation => $params{cpqRackNetConnectorLocation},
    cpqRackNetConnectorPresent => $params{cpqRackNetConnectorPresent},
    cpqRackNetConnectorHasFuses => $params{cpqRackNetConnectorHasFuses},
    cpqRackNetConnectorEnclosureSerialNum => $params{cpqRackNetConnectorEnclosureSerialNum},
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  $self->{name} = $params{cpqRackNetConnectorRack}.':'.$params{cpqRackNetConnectorChassis}.':'.$params{cpqRackNetConnectorIndex};
  bless $self, $class;
  return $self;
}

sub check {
  my $self = shift;
  $self->blacklist('nc', $self->{name});
  my $info = sprintf 'net connector %s is %s, model is %s', 
      $self->{name}.($self->{cpqRackNetConnectorName} ? '('.$self->{cpqRackNetConnectorName}.')' : ''),
      $self->{cpqRackNetConnectorPresent},
      $self->{cpqRackNetConnectorModel};
  $self->add_info($info) if $self->{cpqRackNetConnectorPresent} eq 'present' || 
      $self->{runtime}->{options}->{verbose} >= 3;
  # hat weder status noch condition
} 
  
sub dump {
  my $self = shift;
    printf "[NET_CONNECTOR_%s]\n", $self->{cpqRackNetConnectorName};
  foreach (qw(cpqRackNetConnectorRack cpqRackNetConnectorChassis cpqRackNetConnectorIndex cpqRackNetConnectorEnclosureName cpqRackNetConnectorName cpqRackNetConnectorModel cpqRackNetConnectorSerialNum cpqRackNetConnectorPartNumber cpqRackNetConnectorSparePartNumber cpqRackNetConnectorFWRev cpqRackNetConnectorType cpqRackNetConnectorLocation cpqRackNetConnectorPresent cpqRackNetConnectorHasFuses cpqRackNetConnectorEnclosureSerialNum)) {
    printf "%s: %s\n", $_, $self->{$_};
  }
  printf "\n";
}



package HP::BladeSystem::Component::ServerBladeSubsystem;
our @ISA = qw(HP::BladeSystem::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},
    server_blades => [],
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  bless $self, $class;
  $self->init();
  return $self;
}

sub init {
  my $self = shift;
  my $oids = {
      cpqRackServerBladeEntry => '1.3.6.1.4.1.232.22.2.4.1.1.1',
      cpqRackServerBladeRack => '1.3.6.1.4.1.232.22.2.4.1.1.1.1',
      cpqRackServerBladeChassis => '1.3.6.1.4.1.232.22.2.4.1.1.1.2',
      cpqRackServerBladeIndex => '1.3.6.1.4.1.232.22.2.4.1.1.1.3',
      cpqRackServerBladeName => '1.3.6.1.4.1.232.22.2.4.1.1.1.4',
      cpqRackServerBladeEnclosureName => '1.3.6.1.4.1.232.22.2.4.1.1.1.5',
      cpqRackServerBladePartNumber => '1.3.6.1.4.1.232.22.2.4.1.1.1.6',
      cpqRackServerBladeSparePartNumber => '1.3.6.1.4.1.232.22.2.4.1.1.1.7',
      cpqRackServerBladePosition => '1.3.6.1.4.1.232.22.2.4.1.1.1.8',
      cpqRackServerBladeHeight => '1.3.6.1.4.1.232.22.2.4.1.1.1.9',
      cpqRackServerBladeWidth => '1.3.6.1.4.1.232.22.2.4.1.1.1.10',
      cpqRackServerBladeDepth => '1.3.6.1.4.1.232.22.2.4.1.1.1.11',
      cpqRackServerBladePresent => '1.3.6.1.4.1.232.22.2.4.1.1.1.12',
      cpqRackServerBladeHasFuses => '1.3.6.1.4.1.232.22.2.4.1.1.1.13',
      cpqRackServerBladeEnclosureSerialNum => '1.3.6.1.4.1.232.22.2.4.1.1.1.14',
      cpqRackServerBladeSlotsUsed => '1.3.6.1.4.1.232.22.2.4.1.1.1.15',
      cpqRackServerBladeStatus => '1.3.6.1.4.1.232.22.2.4.1.1.1.21',
      cpqRackServerBladeDiagnosticString => '1.3.6.1.4.1.232.22.2.4.1.1.1.24',
      cpqRackServerBladePowered => '1.3.6.1.4.1.232.22.2.4.1.1.1.25',
      cpqRackServerBladePOSTStatus => '1.3.6.1.4.1.232.22.2.4.1.1.1.35',
      cpqRackServerBladePresentValue => {
          1 => 'other',
          2 => 'absent',
          3 => 'present',
      },
      cpqRackServerBladeStatusValue => {
          1 => 'other',
          2 => 'ok',
          3 => 'degraded',
          4 => 'failed',
      },
      cpqRackServerBladePoweredValue => {
          0 => 'aechz',
          1 => 'other',
          2 => 'on',
          3 => 'off',
          4 => 'powerStagedOff',
          5 => 'reboot',
      },
      cpqRackServerBladePOSTStatusValue => {
          1 => 'other',
          2 => 'started',
          3 => 'completed',
          4 => 'failed',
      },
  };
 
 
  # INDEX { cpqRackServerBladeRack, cpqRackServerBladeChassis, cpqRackServerBladeIndex }
  # dreckada dreck, dreckada
  foreach ($self->get_entries($oids, 'cpqRackServerBladeEntry')) {
    push(@{$self->{server_blades}},
        HP::BladeSystem::Component::ServerBladeSubsystem::ServerBlade->new(%{$_}));
  }
}

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

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


package HP::BladeSystem::Component::ServerBladeSubsystem::ServerBlade;
our @ISA = qw(HP::BladeSystem::Component::ServerBladeSubsystem);

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},
    cpqRackServerBladeRack => $params{cpqRackServerBladeRack},
    cpqRackServerBladeChassis => $params{cpqRackServerBladeChassis},
    cpqRackServerBladeIndex => $params{cpqRackServerBladeIndex},
    cpqRackServerBladeName => $params{cpqRackServerBladeName},
    cpqRackServerBladeEnclosureName => $params{cpqRackServerBladeEnclosureName},
    cpqRackServerBladePartNumber => $params{cpqRackServerBladePartNumber},
    cpqRackServerBladeSparePartNumber => $params{cpqRackServerBladeSparePartNumber},
    cpqRackServerBladePosition => $params{cpqRackServerBladePosition},
    cpqRackServerBladeHeight => $params{cpqRackServerBladeHeight},
    cpqRackServerBladeWidth => $params{cpqRackServerBladeWidth},
    cpqRackServerBladeDepth => $params{cpqRackServerBladeDepth},
    cpqRackServerBladePresent => $params{cpqRackServerBladePresent},
    cpqRackServerBladeHasFuses => $params{cpqRackServerBladeHasFuses},
    cpqRackServerBladeEnclosureSerialNum => $params{cpqRackServerBladeEnclosureSerialNum},
    cpqRackServerBladeSlotsUsed => $params{cpqRackServerBladeSlotsUsed},
    cpqRackServerBladeStatus => $params{cpqRackServerBladeStatus},
    cpqRackServerBladeDiagnosticString => $params{cpqRackServerBladeDiagnosticString} || '',
    cpqRackServerBladePowered => $params{cpqRackServerBladePowered},
    cpqRackServerBladePOSTStatus => $params{cpqRackServerBladePOSTStatus},
    blacklisted => 0,
    info => undef,
    extendedinfo => undef,
  };
  bless $self, $class;
  $self->init();
  return $self;
}

sub check {
  my $self = shift;
  $self->blacklist('sb', $self->{cpqRackServerBladeName});
  my $info = sprintf 'serverblade %s is %s; partnumber=%s;  name=%s',
      $self->{cpqRackServerBladeIndex}, $self->{cpqRackServerBladePresent},
      $self->{cpqRackServerBladePartNumber}, $self->{cpqRackServerBladeName};
  $self->add_info($info) if $self->{cpqRackServerBladePresent} eq 'present' || 
      $self->{runtime}->{options}->{verbose} >= 3; # absent blades nur bei -vvv
  if ($self->{cpqRackServerBladePowered} eq 'on') {
    if ($self->{cpqRackServerBladeCondition} eq 'degraded') {
      $self->add_message(WARNING, sprintf 'server blade %s diag is \'%s\', post status is %s',
          $self->{cpqRackServerBladeName}, $self->{cpqRackServerBladeDiagnosticString},
          $self->{cpqRackServerBladePOSTStatus});
    } elsif ($self->{cpqRackServerBladeCondition} eq 'failed') {
      $self->add_message(CRITICAL, sprintf 'server blade %s diag is \'%s\', post status is %s',
          $self->{cpqRackServerBladeName}, $self->{cpqRackServerBladeDiagnosticString},
          $self->{cpqRackServerBladePOSTStatus});
    } 
  }
} 
  
sub dump {
  my $self = shift;
    printf "[SERVER_BLADE_%s]\n", $self->{cpqRackServerBladeName};
  foreach (qw(cpqRackServerBladeRack cpqRackServerBladeChassis cpqRackServerBladeIndex cpqRackServerBladeName cpqRackServerBladeEnclosureName cpqRackServerBladePartNumber cpqRackServerBladeSparePartNumber cpqRackServerBladePosition cpqRackServerBladeHeight cpqRackServerBladeWidth cpqRackServerBladeDepth cpqRackServerBladePresent cpqRackServerBladeHasFuses cpqRackServerBladeEnclosureSerialNum cpqRackServerBladeSlotsUsed cpqRackServerBladeStatus cpqRackServerBladeDiagnosticString cpqRackServerBladePowered cpqRackServerBladePOSTStatus)) {
    printf "%s: %s\n", $_, $self->{$_};
  }
  printf "\n";
}



package HP::BladeSystem::Component;

use strict;

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


package HP::BladeSystem;

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

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

sub init {
  my $self = shift;
  $self->{components} = {
      common_enclosure_subsystem => undef,
      power_enclosure_subsystem => undef,
      power_supply_subsystem => undef,
      net_connector_subsystem => undef,
      server_blade_subsystem => undef,
  };
  $self->{serial} = 'unknown';
  $self->{product} = 'unknown';
  $self->{romversion} = 'unknown';
  $self->trace(3, 'BladeSystem identified');
  $self->collect();
  if (! $self->{runtime}->{plugin}->check_messages()) {
    $self->set_serial();
    $self->analyze_common_enclosures();
    $self->analyze_power_enclosures();
    $self->analyze_power_supplies();
    $self->analyze_net_connectors();
    $self->analyze_server_blades();
    $self->check_common_enclosures();
    $self->check_power_enclosures();
    $self->check_power_supplies();
    $self->check_net_connectors();
    $self->check_server_blades();
  }
}

sub identify {
  my $self = shift;
  return sprintf "System: '%s', S/N: '%s'",
      $self->{product}, $self->{serial};
}

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->{enclosures});
}

sub analyze_common_enclosures {
  my $self = shift;
  $self->{components}->{common_enclosure_subsystem} =
      HP::BladeSystem::Component::CommonEnclosureSubsystem->new(
    rawdata => $self->{rawdata},
    method => $self->{method},
    runtime => $self->{runtime},
  );
}

sub analyze_power_enclosures {
  my $self = shift;
  $self->{components}->{power_enclosure_subsystem} =
      HP::BladeSystem::Component::PowerEnclosureSubsystem->new(
    rawdata => $self->{rawdata},
    method => $self->{method},
    runtime => $self->{runtime},
  );
}

sub analyze_power_supplies {
  my $self = shift;
  $self->{components}->{power_supply_subsystem} =
      HP::BladeSystem::Component::PowerSupplySubsystem->new(
    rawdata => $self->{rawdata},
    method => $self->{method},
    runtime => $self->{runtime},
  );
}

sub analyze_net_connectors {
  my $self = shift;
  $self->{components}->{net_connector_subsystem} =
      HP::BladeSystem::Component::NetConnectorSubsystem->new(
    rawdata => $self->{rawdata},
    method => $self->{method},
    runtime => $self->{runtime},
  );
}

sub analyze_server_blades {
  my $self = shift;
  $self->{components}->{server_blade_subsystem} =
      HP::BladeSystem::Component::ServerBladeSubsystem->new(
    rawdata => $self->{rawdata},
    method => $self->{method},
    runtime => $self->{runtime},
  );
}

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

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

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

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

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

sub collect {
  my $self = shift;
  if ($self->{runtime}->{plugin}->opts->snmpwalk) {
    my $cpqRackMibCondition = '1.3.6.1.4.1.232.22.1.3.0';
    $self->trace(3, 'getting cpqRackMibCondition');
    if (! exists $self->{rawdata}->{$cpqRackMibCondition}) {
        $self->add_message(CRITICAL,
            'snmpwalk returns no health data (cpqrack-mib)');
    }
  } else {
    my $net_snmp_version = Net::SNMP->VERSION(); # 5.002000 or 6.000000
    #$params{'-translate'} = [
    #  -all => 0x0
    #];
    my ($session, $error) =
        Net::SNMP->session(%{$self->{runtime}->{snmpparams}});
    if (! defined $session) {
      $self->{plugin}->add_message(CRITICAL, 'cannot create session object');
      $self->trace(1, Data::Dumper::Dumper($self->{runtime}->{snmpparams}));
    } else {
      # revMajor is often used for discovery of hp devices
      my $cpqSeMibRev = '1.3.6.1.4.1.232.22.1';
      my $cpqSeMibRevMajor = '1.3.6.1.4.1.232.22.1.1.0';
      my $cpqRackMibCondition = '1.3.6.1.4.1.232.22.1.3.0';
      $self->trace(3, 'getting cpqRackMibCondition');
      my $result = $session->get_request(
          -varbindlist => [$cpqRackMibCondition]
      );
      if (!defined($result) ||
          $result->{$cpqRackMibCondition} eq 'noSuchInstance' ||
          $result->{$cpqRackMibCondition} eq 'noSuchObject' ||
          $result->{$cpqRackMibCondition} eq 'endOfMibView') {
        $self->add_message(CRITICAL,
            'snmpwalk returns no health data (cpqrack-mib)');
        $session->close;
      } else {
        $self->trace(3, 'getting cpqRackMibCondition done');
      }
    }
    if (! $self->{runtime}->{plugin}->check_messages()) {
      # snmp peer is alive
      $self->trace(2, sprintf "Protocol is %s",
          $self->{runtime}->{snmpparams}->{'-version'});
      my $cpqSiComponent = "1.3.6.1.4.1.232.2.2";
      my $cpqSiAsset = "1.3.6.1.4.1.232.2.2.2";
      my $cpqRackInfo = "1.3.6.1.4.1.232.22";
      $session->translate;
      my $response = {}; #break the walk up in smaller pieces
      my $tic = time; my $tac = $tic;
      # Walk for Asset
      $tic = time;
      my $response0 = $session->get_table(
          -maxrepetitions => 1,
          -baseoid => $cpqSiComponent);
      if (scalar (keys %{$response0}) == 0) {
        $self->trace(2, sprintf "maxrepetitions failed. fallback");
        $response0 = $session->get_table(
            -baseoid => $cpqSiComponent);
      }
      $tac = time;
      $self->trace(2, sprintf "%03d seconds for walk cpqSiComponent (%d oids)",
          $tac - $tic, scalar(keys %{$response0}));
      $tic = time;
      my $response1 = $session->get_table(
          -maxrepetitions => 1,
          -baseoid => $cpqRackInfo);
      if (scalar (keys %{$response1}) == 0) {
        $self->trace(2, sprintf "maxrepetitions failed. fallback");
        $response1 = $session->get_table(
            -baseoid => $cpqRackInfo);
      }
      $tac = time;
      $self->trace(2, sprintf "%03d seconds for walk cpqRackInfo (%d oids)",
          $tac - $tic, scalar(keys %{$response1}));
      $session->close;
      map { $response->{$_} = $response0->{$_} } keys %{$response0};
      map { $response->{$_} = $response1->{$_} } keys %{$response1};
      map { $response->{$_} =~ s/^\s+//; $response->{$_} =~ s/\s+$//; }
          keys %$response;
      $self->{rawdata} = $response;
    }
  }
  return $self->{runtime}->{plugin}->check_messages();
}

sub set_serial {
  my $self = shift;

  my $cpqSiSysSerialNum = "1.3.6.1.4.1.232.2.2.2.1.0";
  my $cpqSiProductName = "1.3.6.1.4.1.232.2.2.4.2.0";

  $self->{serial} =
      SNMP::Utils::get_object($self->{rawdata}, $cpqSiSysSerialNum);
  $self->{product} =
      SNMP::Utils::get_object($self->{rawdata}, $cpqSiProductName);
  $self->{serial} = $self->{serial};
  $self->{product} = lc $self->{product};
  $self->{romversion} = 'unknown';
#####################################################################
$self->{runtime}->{product} = $self->{product};
}

package HP::Storage;

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,
      disk_subsystem => undef,
  };
  $self->{serial} = 'unknown';
  $self->{product} = 'unknown';
  $self->{romversion} = 'unknown';
  $self->collect();
  if (! $self->{runtime}->{plugin}->check_messages()) {
    $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_disk_subsystem();
#    $self->check_cpus();
#    $self->check_powersupplies();
#    $self->check_fan_subsystem();
#    $self->check_temperatures();
#    $self->check_memory_subsystem();
    $self->check_disk_subsystem();
  }
}

sub identify {
  my $self = shift;
  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"
  );
  $self->{runtime}->{options}->{buggy_firmware} =
      grep /^$self->{romversion}/, @buggyfirmwares;
}

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::Storage::Component::PowersupplySubsystem->new(
    rawdata => $self->{rawdata},
    method => $self->{method},
    runtime => $self->{runtime},
  );
}

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

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

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

sub analyze_memory_subsystem {
  my $self = shift;
  $self->{components}->{memory_subsystem} = 
      HP::Storage::Component::MemorySubsystem->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 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_disk_subsystem {
  my $self = shift;
  $self->{components}->{disk_subsystem}->check();
  $self->{components}->{disk_subsystem}->dump()
      if $self->{runtime}->{options}->{verbose} >= 1;
}


sub collect {
  my $self = shift;
  if ($self->{runtime}->{plugin}->opts->snmpwalk) {
    my $cpqSeMibCondition = '1.3.6.1.4.1.232.6.1.3.0';
    # rindsarsch!
    $self->{rawdata}->{$cpqSeMibCondition} = 0;
    if (! exists $self->{rawdata}->{$cpqSeMibCondition}) {
        $self->add_message(CRITICAL,
            'snmpwalk returns no health data (cpqhlth-mib)');
    }
  } else {
    my $net_snmp_version = Net::SNMP->VERSION(); # 5.002000 or 6.000000
    #$params{'-translate'} = [
    #  -all => 0x0
    #];
    my ($session, $error) = 
        Net::SNMP->session(%{$self->{runtime}->{snmpparams}});
    if (! defined $session) {
      $self->{plugin}->add_message(CRITICAL, 'cannot create session object');
      $self->trace(1, Data::Dumper::Dumper($self->{runtime}->{snmpparams}));
    } else {
      # revMajor is often used for discovery of hp devices
      my $cpqSeMibRev = '1.3.6.1.4.1.232.6.1';
      my $cpqSeMibRevMajor = '1.3.6.1.4.1.232.6.1.1.0';
      my $cpqSeMibCondition = '1.3.6.1.4.1.232.6.1.3.0';
      my $result = $session->get_request(
          -varbindlist => [$cpqSeMibCondition]
      );
      # rindsarsch!
      $result->{$cpqSeMibCondition} = 0;
      if (!defined($result) || 
          $result->{$cpqSeMibCondition} eq 'noSuchInstance' ||
          $result->{$cpqSeMibCondition} eq 'noSuchObject' ||
          $result->{$cpqSeMibCondition} eq 'endOfMibView') {
        $self->add_message(CRITICAL,
            'snmpwalk returns no health data (cpqhlth-mib)');
        $session->close;
      } else {
        # this is not reliable. many agents return 4=failed
        #if ($result->{$cpqSeMibCondition} != 2) {
        #  $obstacle = "cmapeerstart";
        #}
      }
    }
    if (! $self->{runtime}->{plugin}->check_messages()) {
      # snmp peer is alive
      $self->trace(2, sprintf "Protocol is %s", 
          $self->{runtime}->{snmpparams}->{'-version'});
      my $cpqSsSys =  "1.3.6.1.4.1.232.8";
      $session->translate;
      my $response = {}; #break the walk up in smaller pieces
      my $tic = time; my $tac = $tic;
      my $response1 = $session->get_table(
          -baseoid => $cpqSsSys);
      $tac = time;
      $self->trace(2, sprintf "%03d seconds for walk cpqSsSys (%d oids)",
          $tac - $tic, scalar(keys %{$response1}));
      $session->close;
      map { $response->{$_} = $response1->{$_} } keys %{$response1};
      map { $response->{$_} =~ s/^\s+//; $response->{$_} =~ s/\s+$//; }
          keys %$response;
      $self->{rawdata} = $response;
    }
  }
  return $self->{runtime}->{plugin}->check_messages();
}

sub set_serial {
  my $self = shift;
  my $snmpwalk = $self->{rawdata};
  my @serials = ();
  my @models = ();
  my @fws = ();
  my $cpqSsBackplaneEntry = '1.3.6.1.4.1.232.8.2.2.6.1';
  my $cpqSsBackplaneFWRev = '1.3.6.1.4.1.232.8.2.2.6.1.3';
  my $cpqSsBackplaneModel = '1.3.6.1.4.1.232.8.2.2.6.1.9';
  my $cpqSsBackplaneSerialNumber = '1.3.6.1.4.1.232.8.2.2.6.1.13';
  # INDEX { cpqSsBackplaneChassisIndex, cpqSsBackplaneIndex }
  my @indexes = SNMP::Utils::get_indices($snmpwalk,
      $cpqSsBackplaneEntry);
  foreach (@indexes) {
    my($idx1, $idx2) = ($_->[0], $_->[1]);
    my $fw = SNMP::Utils::get_object($snmpwalk,
        $cpqSsBackplaneFWRev, $idx1, $idx2);
    my $model = SNMP::Utils::get_object($snmpwalk,
        $cpqSsBackplaneModel, $idx1, $idx2);
    my $serial = SNMP::Utils::get_object($snmpwalk,
        $cpqSsBackplaneSerialNumber, $idx1, $idx2);
    push(@serials, $serial);
    push(@models, $model);
    push(@fws, $fw);
  }
  
  $self->{serial} = join('/', @serials);
  $self->{product} = join('/', @models);
  $self->{romversion} = join('/', @fws);
  $self->{runtime}->{product} = $self->{product};
}




















package HP::Server;

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

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
    runtime => $params{runtime},
    productname => 'unknown',
  };
  bless $self, $class;
  if (! ($self->{runtime}->{plugin}->opts->hostname ||
      $self->{runtime}->{plugin}->opts->snmpwalk)) {
    bless $self, 'HP::Proliant::CLI';
    $self->{method} = 'cli';
  } else {
    $self->check_snmp_and_model();
    if ($self->{runtime}->{options}->{servertype}) {
      $self->{productname} = 'ProLiant' if 
          $self->{runtime}->{options}->{servertype} eq 'proliant';
      $self->{productname} = 'BladeSystem' if 
          $self->{runtime}->{options}->{servertype} eq 'bladesystem';
      $self->{productname} = 'Storage' if 
          $self->{runtime}->{options}->{servertype} eq 'storage';
    }
    if (! $self->{runtime}->{plugin}->check_messages()) {
      if ($self->{productname} =~ /ProLiant/) {
        bless $self, 'HP::Proliant::SNMP';
        $self->trace(3, 'using HP::Proliant::SNMP');
      } elsif ($self->{productname} =~ /OpenView .* appliance/) {
        bless $self, 'HP::Proliant::SNMP';
        $self->trace(3, 'using HP::Proliant::SNMP');
      } elsif ($self->{productname} =~ /BladeSystem/) {
        bless $self, 'HP::BladeSystem';
        $self->trace(3, 'using HP::BladeSystem');
      } elsif ($self->{productname} =~ /PROLIANT 4LEE/) {
        bless $self, 'HP::Storage';
        $self->trace(3, 'using HP::Storage');
      } elsif ($self->{productname} =~ /Storage/) { # fake
        bless $self, 'HP::Storage';
        $self->trace(3, 'using HP::Storage');
      } else {
        $self->add_message(CRITICAL,
            sprintf('unknown device%s', $self->{productname} eq 'unknown' ?
                '' : '('.$self->{productname}.')'));
      }
      $self->{method} = 'snmp';
    }
  }
  if ($self->{runtime}->{options}->{blacklist} &&
      -f $self->{runtime}->{options}->{blacklist}) {
    $self->{runtime}->{options}->{blacklist} = do {
        local (@ARGV, $/) = $self->{runtime}->{options}->{blacklist}; <> };
  }
  return $self;
}

sub check_snmp_and_model {
# uptime pruefen
# dann whoami
  my $self = shift;
  if ($self->{runtime}->{plugin}->opts->snmpwalk) {
    my $response = {};
    if (! -f $self->{runtime}->{plugin}->opts->snmpwalk) {
      $self->{runtime}->{plugin}->add_message(CRITICAL, 
          sprintf 'file %s not found',
          $self->{runtime}->{plugin}->opts->snmpwalk);
    } elsif (-x $self->{runtime}->{plugin}->opts->snmpwalk) {
      my $cmd = sprintf "%s -On -v%s -c%s %s 1.3.6.1.4.1.232 2>&1",
          $self->{runtime}->{plugin}->opts->snmpwalk,
          $self->{runtime}->{plugin}->opts->protocol,
          $self->{runtime}->{plugin}->opts->community,
          $self->{runtime}->{plugin}->opts->hostname;
      open(WALK, "$cmd |");
      while (<WALK>) {
        if (/^.*?\.(232\.[\d\.]+) = .*?: (\-*\d+)/) {
          $response->{'1.3.6.1.4.1.'.$1} = $2;
        } elsif (/^.*?\.(232\.[\d\.]+) = .*?: "(.*?)"/) {
          $response->{'1.3.6.1.4.1.'.$1} = $2;
          $response->{'1.3.6.1.4.1.'.$1} =~ s/\s+$//;
        }
      }
      close WALK;
    } else {
      open(MESS, $self->{runtime}->{plugin}->opts->snmpwalk);
      while(<MESS>) {
        # SNMPv2-SMI::enterprises.232.6.2.6.7.1.3.1.4 = INTEGER: 6
        if (/^.*?\.(232\.[\d\.]+) = .*?: (\-*\d+)/) {
          $response->{'1.3.6.1.4.1.'.$1} = $2;
        } elsif (/^.*?\.(232\.[\d\.]+) = .*?: "(.*?)"/) {
          $response->{'1.3.6.1.4.1.'.$1} = $2;
          $response->{'1.3.6.1.4.1.'.$1} =~ s/\s+$//;
        } if (/^.*?\.(232\.[\d\.]+) = (\-*\d+)/) {
          $response->{'1.3.6.1.4.1.'.$1} = $2;
        } elsif (/^.*?\.(232\.[\d\.]+) = "(.*?)"/) {
          $response->{'1.3.6.1.4.1.'.$1} = $2;
          $response->{'1.3.6.1.4.1.'.$1} =~ s/\s+$//;
        }
      }
      close MESS;
    }
    map { $response->{$_} =~ s/^\s+//; $response->{$_} =~ s/\s+$//; }
        keys %$response;
    $self->{rawdata} = $response;
    $self->whoami();
  } else {
    if (eval "require Net::SNMP") {
      my %params = ();
      my $net_snmp_version = Net::SNMP->VERSION(); # 5.002000 or 6.000000
      #$params{'-translate'} = [
      #  -all => 0x0
      #];
      $params{'-hostname'} = $self->{runtime}->{plugin}->opts->hostname;
      $params{'-version'} = $self->{runtime}->{plugin}->opts->protocol;
      if ($self->{runtime}->{plugin}->opts->port) {
        $params{'-port'} = $self->{runtime}->{plugin}->opts->port;
      }
      if ($self->{runtime}->{plugin}->opts->protocol eq '3') {
        $params{'-username'} = $self->{runtime}->{plugin}->opts->username;
        if ($self->{runtime}->{plugin}->opts->authpassword) {
          $params{'-authpassword'} = $self->{runtime}->{plugin}->opts->authpassword;
        }
        if ($self->{runtime}->{plugin}->opts->authprotocol) {
          $params{'-authprotocol'} = $self->{runtime}->{plugin}->opts->authprotocol;
        }
        if ($self->{runtime}->{plugin}->opts->privpassword) {
          $params{'-privpassword'} = $self->{runtime}->{plugin}->opts->privpassword;
        }
        if ($self->{runtime}->{plugin}->opts->privprotocol) {
          $params{'-privprotocol'} = $self->{runtime}->{plugin}->opts->privprotocol;
        }
      } else {
        $params{'-community'} = $self->{runtime}->{plugin}->opts->community;
      }
      $self->{runtime}->{snmpparams} = \%params;
      my ($session, $error) = Net::SNMP->session(%params);
      $self->{session} = $session;
      if (! defined $session) {
        $self->{plugin}->add_message(CRITICAL, 'cannot create session object');
        $self->trace(1, Data::Dumper::Dumper(\%params));
      } else {
        my $sysUpTime = '1.3.6.1.2.1.1.3.0';
        my $result = $session->get_request(
            -varbindlist => [$sysUpTime]
        );
        if (!defined($result)) {
          $self->add_message(CRITICAL,
              'could not contact snmp agent');
          $session->close;
        } else {
          $self->trace(3, 'snmp agent answered');
          $self->whoami();
        }
      }
    } else {
      $self->add_message(CRITICAL,
          'could not find Net::SNMP module');
    }
  }
}

sub whoami {
  my $self = shift;
  my $productname = undef;
  if ($self->{runtime}->{plugin}->opts->snmpwalk) {
    my $cpqSiProductName = '1.3.6.1.4.1.232.2.2.4.2.0';
    my $cpqSsMibRevMajor = '1.3.6.1.4.1.232.8.1.1.0';
    my $cpqSsBackplaneModel = '1.3.6.1.4.1.232.8.2.2.6.1.9'.'.1.1';
    if ($productname = $self->{rawdata}->{$cpqSiProductName}) {
      if (! $productname) {
        $self->{productname} = 'ProLiant';
      } else {
        $self->{productname} = $self->{rawdata}->{$cpqSiProductName};
      }
    } elsif (exists $self->{rawdata}->{$cpqSsBackplaneModel}) {
      $self->{productname} = $self->{rawdata}->{$cpqSsBackplaneModel};
    } elsif (exists $self->{rawdata}->{$cpqSsMibRevMajor}) {
      # at least there is a CPQSTSYS-MIB
      $self->{productname} = 'Storage'
    } else {
      $self->add_message(CRITICAL,
          'snmpwalk returns no product name (cpqsinfo-mib)');
    }
  } else {
    my $cpqSiProductName = '1.3.6.1.4.1.232.2.2.4.2.0';
    my $cpqSsMibRevMajor = '1.3.6.1.4.1.232.8.1.1.0';
    my $cpqSsBackplaneModel = '1.3.6.1.4.1.232.8.2.2.6.1.9'.'.1.1';
    my $dummy = '1.3.6.1.2.1.1.5.0';
    if ($productname = $self->valid_response($cpqSiProductName)) {
      if ($productname eq '') {
        $self->{productname} = 'ProLiant';
      } else {
        $self->{productname} = $productname;
      }
    } elsif ($productname = $self->valid_response($cpqSsBackplaneModel)) {
      $self->{productname} = $productname;
    } elsif ($self->valid_response($cpqSsMibRevMajor)) {
      # at least there is a CPQSTSYS-MIB
      $self->{productname} = 'Storage'
    } else {
      $self->add_message(CRITICAL,
          'snmpwalk returns no product name (cpqsinfo-mib)');
      $self->{session}->close;
    }
    $self->trace(3, 'whoami: '.$self->{productname});
  }
}

sub valid_response {
  my $self = shift;
  my $oid = shift;
  my $result = $self->{session}->get_request(
      -varbindlist => [$oid]
  );
  if (!defined($result) ||
      ! defined $result->{$oid} ||
      $result->{$oid} eq 'noSuchInstance' ||
      $result->{$oid} eq 'noSuchObject' ||
      $result->{$oid} eq 'endOfMibView') {
    return undef;
  } else {
    return $result->{$oid};
  }
}

sub trace {
  my $self = shift;
  my $level = shift;
  my $message = shift;
  if ($self->{runtime}->{options}->{verbose} >= $level) {
    printf "%s\n", $message;
  }
}

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

sub add_blacklist {
  my $self = shift;
  my $list = shift;
  $self->{runtime}->{options}->{blacklist} = join('/',
      (split('/', $self->{runtime}->{options}->{blacklist}), $list));
}

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

sub add_message {
  my $self = shift;
  my $level = shift;
  my $message = shift;
  $self->{runtime}->{plugin}->add_message($level, $message) 
      unless $self->{blacklisted};
}

sub add_info {
  my $self = shift;
  my $info = shift;
  $info = $self->{blacklisted} ? $info.' (blacklisted)' : $info;
  $self->{info} = $info;
  if (! exists $self->{runtime}->{plugin}->{info}) {
    $self->{runtime}->{plugin}->{info} = [];
  }
  push(@{$self->{runtime}->{plugin}->{info}}, $info);
}

sub annotate_info {
  my $self = shift;
  my $annotation = shift;
  my $lastinfo = pop(@{$self->{runtime}->{plugin}->{info}});
  $lastinfo .= sprintf ' (%s)', $annotation;
  push(@{$self->{runtime}->{plugin}->{info}}, $lastinfo);
}

sub add_extendedinfo {
  my $self = shift;
  my $info = shift;
  $self->{extendedinfo} = $info;
  return if ! $self->{runtime}->{options}->{extendedinfo};
  if (! exists $self->{runtime}->{plugin}->{extendedinfo}) {
    $self->{runtime}->{plugin}->{extendedinfo} = [];
  }
  push(@{$self->{runtime}->{plugin}->{extendedinfo}}, $info);
}

sub get_extendedinfo {
  my $self = shift;
  if (! exists $self->{runtime}->{plugin}->{extendedinfo}) {
    $self->{runtime}->{plugin}->{extendedinfo} = [];
  }
  return join(' ', @{$self->{runtime}->{plugin}->{extendedinfo}});
}

sub add_summary {
  my $self = shift;
  my $summary = shift;
  if (! exists $self->{runtime}->{plugin}->{summary}) {
    $self->{runtime}->{plugin}->{summary} = [];
  }
  push(@{$self->{runtime}->{plugin}->{summary}}, $summary);
}

sub get_summary {
  my $self = shift;
  if (! exists $self->{runtime}->{plugin}->{summary}) {
    $self->{runtime}->{plugin}->{summary} = [];
  }
  return join(', ', @{$self->{runtime}->{plugin}->{summary}});
}

sub dumper {
  my $self = shift;
  my $object = shift;
  my $run = $object->{runtime};
  delete $object->{runtime};
  printf STDERR "%s\n", Data::Dumper::Dumper($object);
  $object->{runtime} = $run;
}
package main;
#! /usr/bin/perl

use strict;

my $CELSIUS = 1;
my $PERFDATA = 1;
my $EXTENDEDINFO = 1;
my $HWINFO = 1;
my $HPACUCLI = 0;
my $NOINSTLEVEL = 'unknown';

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

my $plugin = Nagios::MiniPlugin->new(
    shortname => '',
    usage => 'Usage: %s [ -v|--verbose ] [ -t <timeout> ] '.
        '--hostname <proliant> --community <snmp-community>'.
        '  ...]',
    version => '4.1.2',
    blurb => 'This plugin checks the hardware of hp/compaq proliant servers',
    url => 'http://labs.consol.de/nagios/check_hpasm',
    timeout => 60,
    shortname => '',
);
$plugin->add_arg(
    spec => 'blacklist|b=s',
    help => '--blacklist
   Blacklist some (missing/failed) components',
    required => 0,
    default => '',
);
$plugin->add_arg(
    spec => 'ignore-dimms|i',
    help => '--ignore-dimms
   Ignore "N/A"-DIMM status on misc. servers (e.g. older DL320)',
    required => 0,
);
$plugin->add_arg(
    spec => 'ignore-fan-redundancy',
    help => '--ignore-fan-redundancy
   Ignore missing redundancy partners',
    required => 0,
);
$plugin->add_arg(
    spec => 'customthresholds|c=s',
    help => '--customthresholds
   Use custom thresholds for certain temperatures',
    required => 0,
);
$plugin->add_arg(
    spec => 'perfdata=s',
    help => '--perfdata=[short]
   Output performance data. If your performance data string becomes
   too long and is truncated by Nagios, then you can use --perfdata=short
   instead. This will output temperature tags without location information',
    required => 0,
);
$plugin->add_arg(
    spec => 'hostname|H=s',
    help => '--hostname
   Hostname or IP-address of the server (SNMP mode only)',
    required => 0,
);
$plugin->add_arg(
    spec => 'port=i',
    help => '--port
   The SNMP port to use (default: 161)',
    required => 0,
    default => 161,
);
$plugin->add_arg(
    spec => 'protocol|P=s',
    help => '--protocol
   The SNMP protocol to use (default: 2c, other possibilities: 1,3)',
    required => 0,
    default => '2c',
);
$plugin->add_arg(
    spec => 'community|C=s',
    help => '--community
   SNMP community of the server (SNMP v1/2 only)',
    required => 0,
    default => 'public',
);
$plugin->add_arg(
    spec => 'username=s',
    help => '--username
   The securityName for the USM security model (SNMPv3 only)',
    required => 0,
);
$plugin->add_arg(
    spec => 'authpassword=s',
    help => '--authpassword
   The authentication password for SNMPv3',
    required => 0,
);
$plugin->add_arg(
    spec => 'authprotocol=s',
    help => '--authprotocol
   The authentication protocol for SNMPv3 (md5|sha)',
    required => 0,
);
$plugin->add_arg(
    spec => 'privpassword=s',
    help => '--privpassword
   The password for authPriv security level',
    required => 0,
);
$plugin->add_arg(
    spec => 'privprotocol=s',
    help => '--privprotocol
   The private protocol for SNMPv3 (des|aes|aes128|3des|3desde)',
    required => 0,
);
$plugin->add_arg(
    spec => 'snmpwalk=s',
    help => '--snmpwalk
   A file with the output of snmpwalk 1.3.6.1.4.1.232',
    required => 0,
);
$plugin->add_arg(
    spec => 'hpasmcli=s',
    help => '--hpasmcli
   A file with the output of hpasmcli',
    required => 0,
);
$plugin->add_arg(
    spec => 'servertype=s',
    help => '--servertype
   The type of the server: proliant (default) or bladecenter',
    required => 0,
);

$plugin->getopts();
if (! $PERFDATA && $plugin->opts->get('perfdata')) {
  $PERFDATA = 1;
}
if ($PERFDATA && $plugin->opts->get('perfdata') &&
    ($plugin->opts->get('perfdata') eq 'short')) {
  $PERFDATA = 2;
}
$plugin->{messages}->{unknown} = []; # wg. add_message(UNKNOWN,...)

$plugin->{info} = []; # gefrickel
my $server = HP::Server->new( runtime => {
    plugin => $plugin,
    options => {
        servertype => $plugin->opts->get('servertype'),
        verbose => $plugin->opts->get('verbose'),
        scrapiron => 0,
        ignore_fan_redundancy => $plugin->opts->get('ignore-fan-redundancy'),
        ignore_dimms => $plugin->opts->get('ignore-dimms'),
        customthresholds => $plugin->opts->get('customthresholds'),
        blacklist => $plugin->opts->get('blacklist'),
        celsius => $CELSIUS,
        perfdata => $PERFDATA,
        extendedinfo => $EXTENDEDINFO,
        hwinfo => $HWINFO,
        hpacucli => $HPACUCLI,
        noinstlevel => $NOINSTLEVEL,
    },
},);
if (! $plugin->check_messages()) {
  $server->init();
  $plugin->add_message(OK, $server->identify()) if $HWINFO;
  if (! $plugin->check_messages()) {
    $plugin->add_message(OK, 'hardware working fine');
    $plugin->add_message(OK, $server->get_summary()) 
        if $server->get_summary();
    $plugin->add_message(OK, $server->get_extendedinfo()) 
        if $server->get_extendedinfo();
  } 
} else {
  $plugin->add_message(CRITICAL, 'wrong device');
}

my ($code, $message) = $plugin->check_messages(join => ', ', join_all => ', ');
$message .= sprintf "\n%s\n", join("\n", @{$plugin->{info}})
    if $plugin->opts->get('verbose') >= 1;
#printf "%s\n", Data::Dumper::Dumper($plugin->{info});
$plugin->nagios_exit($code, $message);

