#!/usr/bin/perl

# This checks the status of one Modular Smart Array controller
# through the HP command line interface.
# Currently tested are MSA20 and (onboard) P400 controllers
# Written by and (C) Michael Meier 2007-2008.
# Released under the GNU GPL v2.

# Where the CLI is located
$hpcli='/usr/sbin/hpacucli';

# ----------------------------------------------------------------------------

$showhelp = 0; $verbose = 0; $savefilename = undef; $devicesel = undef;
$nosave = 0; $bequiet = 0;
for ($i = 0; $i < @ARGV; $i++) {
  if      ($ARGV[$i] eq '-h') {
    $showhelp = 1;
  } elsif ($ARGV[$i] eq '-f') {
    $i++;
    if ($i >= @ARGV) {
      print("Option -f requires an argument.\n");
      $showhelp = 1;
    }
    $savefilename = $ARGV[$i];
  } elsif ($ARGV[$i] eq '-d') {
    $i++;
    if ($i >= @ARGV) {
      print("Option -d requires an argument.\n");
      $showhelp = 1;
    }
    $devicesel = $ARGV[$i];
  } elsif ($ARGV[$i] eq '-v') {
    $verbose++;
  } elsif ($ARGV[$i] eq '-q') {
    $bequiet = 1;
  } elsif ($ARGV[$i] eq '-n') {
    $nosave = 1;
  } else {
    print("ERROR: Unknown parameter: $ARGV[$i]\n");
    $showhelp = 1;
  }
}
unless ((defined($devicesel)) && (length($devicesel) > 0)) {
  if ($showhelp == 0) {
    print("ERROR: No device selected.\n");
  }
  $showhelp = 1;
}
if ($showhelp) {
  print("Syntax: $0 -d deviceselection [-h] [-v] [-f savefilename] [-m mailaddr]\n");
  print(" -h              displays this helptext\n");
  print(" -d devicesel    Which device to query. Passed to the CLI.\n");
  print("                 e.g. 'chassisname=04' to query the MSA with number 04\n");
  print(" -v              verbose output. can be repeated multiple times.\n");
  print(" -f savefile     Filename for loading/saving status\n");
  print(" -n              only load, do not save status\n");
  print(" -q              be quiet unless you have something to tell.\n");
  # not implemented yet print(" -m mailto       Mailaddress to mail changes to\n");
  exit(1);
}
if (defined($savefilename)) {
  unless (open($sffd, '<'.$savefilename)) {
    print("WARNING: Could not open savefile '$savefilename' for reading - last status will be empty!\n");
  } else {
    while ($ll = <$sffd>) {
      $ll =~ s/[\r\n]//g;
      if ($ll =~ m/([^\{]+)\{([^\}]+)\}\{([^\}]+)\}=(.*)/) {
        $context = 'old'.$1; $drivenr = $2; $vn = $3; $vv = $4;
        if (($context eq 'oldmain')
         || ($context eq 'oldlogicaldrive')
         || ($context eq 'oldphysicaldrive')) {
            $$context{$drivenr}{$vn} = $vv;
        }
      }
    }
    close($sffd);
  }
  unless ($nosave == 1) {
    unless (open($sffd, '>'.$savefilename)) {
      print("ERROR: Could not open savefile '$savefilename' for writing.\n");
      exit(1);
    }
  }
}
unless (open($hpclifd, "$hpcli controller $devicesel show config detail|")) {
  print("ERROR: Could not execute HP RAID CLI ($hpcli)\n");
  exit(1);
}
$context = 'main'; $drivenr = -1;
while ($ll = <$hpclifd>) {
  $ll =~ s/[\r\n]//g;
  $ll =~ s/^[ \t]+//g;
  if ($ll =~ m/([^\:]+)\:[ \t]+(.*)/) {
    $vn = lc($1); $vv = $2;
    if ($vn eq 'logical drive') {
      $context = 'logicaldrive';
      $drivenr = $vv;
    } else {
      $$context{$drivenr}{$vn} = $vv;
    }
    if ($verbose > 0) {
      print("$context\{$drivenr\}\{$vn\} = $vv\n");
    }
    if ((defined($savefilename)) && ($nosave == 0) && ($vn ne 'logical drive')) {
      print($sffd "$context\{$drivenr\}\{$vn\}=$vv\n");
    }
  } else {
    if ($ll =~ m/^physicaldrive ([0-9IE\:]+)/) {
      $context = 'physicaldrive';
      $drivenr = $1;
    } elsif ($ll =~ m/^MSA20 at (\d+)/) {
      $context = 'main'; $drivenr = -1;
    }
  }
}
close($hpclifd);
if ((defined($savefilename)) && ($nosave == 0)) {
  close($sffd);
}
if (defined($savefilename)) {
  $changelist = '';
  foreach $cs ('main', 'logicaldrive', 'physicaldrive') {
    $ocs = 'old'.$cs;
    # Since the following line is nonobvious: It gets a sorted list of unique keys from both hashes.
    @ks = sort(keys(%{{%$cs, %$ocs}}));
    foreach $k (@ks) {
      @js = keys(%{{%{$$cs{$k}}, %{$$ocs{$k}}}});
      foreach $j (@js) {
        #print("$cs $k $j\n");
        if ($$cs{$k}{$j} ne $$ocs{$k}{$j}) {
          if ($j =~ m/^current temperature /) { next; }
          $changelist .= "Changed: $cs $k $j: from '".$$ocs{$k}{$j}."' to '".$$cs{$k}{$j}."'\n";
          if (($cs eq 'physicaldrive') && ($j eq 'status')) {
            $changelist .= "    status of physical drive changed (drive failed/replaced?),\n";
            $changelist .= "    therefore dumping info about it:\n";
            $changelist .= sprintf("    %3s %-24s %-25s %-25s %3s\n", '   ', 'variable', 'old value', 'new value', '   ');
            foreach $z (@js) {
              my $filler = ($$ocs{$k}{$z} eq $$cs{$k}{$z}) ? '   ' : '!!!';
              $changelist .= sprintf("    %3s %-24s %-25s %-25s %3s\n", $filler, $z, $$ocs{$k}{$z}, $$cs{$k}{$z}, $filler);
            }
          }
        }
      }
    }
  }
  if ($changelist eq '') {
    unless ($bequiet) {
      print("No changes in the RAID status detected.\n");
    }
  } else {
    $changelist = "Changed RAID status detected!\n\n" . $changelist;
    $changelist .= "\n";
    $changelist .= "RAID data:\n";
    $changelist .= "Serial number           : ".$main{-1}{'serial number'}." (".$main{-1}{'chassis serial number'}.")\n";
    @js = keys(%{{%{$main{-1}}, %{$main{-1}}}});
    foreach $vn (@js) {
      if (($vn eq 'serial number') || ($vn eq 'chassis serial number')) {
        next;
      }
      $changelist .= sprintf("%-24s: %s\n", $vn, $main{-1}{$vn});
    }
    print($changelist);
  }
}
