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

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

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

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

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

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

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

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

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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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

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

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

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

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


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

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

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


1;
