#! /usr/bin/perl -w
#
# check_aaccli - nagios plugin which checks the health of
# 		 Adaptec Raid using the aaccli command.
#
# Copyright (C) 2007 Gerhard Lausser, gerhard.lausser@consol.de
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
#   
#   
# Report bugs to:  gerhard.lausser@consol.de
# 
    

package AAC;

use strict;
use Data::Dumper;

{
  my @controllers = ();
  my @tasks = ();
  my @containers = ();
  my @disks = ();
  our $aaccli = "/usr/sbin/aaccli";
  our $verbose = 0;
  our $nagios_status = 0;
  our $nagios_message = "";

  sub init {
    if (! -x $aaccli) {
      if (lc "unknown" eq "warning") {
        printf "WARNING - %s is not installed\n", $aaccli;
        exit 1;
      } elsif (lc "unknown" eq "critical") {
        printf "CRITICAL - %s is not installed\n", $aaccli;
        exit 2;
      } elsif (lc "unknown" eq "unknown") {
        printf "UNKNOWN - %s is not installed\n", $aaccli;
        exit 3;
      } else {
        printf "OK - at least i hope so because %s is not installed\n", $aaccli;
        exit 0;
      }
    }
    printf STDERR "collecting controller informations\n" if $verbose;
    AAC::Controller->init();
    @controllers = AAC::Controller->get_controllers();
    #    AAC::Task->init();
    #    @tasks = AAC::Task->get_tasks();
  }

  sub nagios_status {
    my $errorlevel = 0;
    my @messages = ([], [], [], []);
    foreach my $controller (@controllers) {
      printf STDERR "checking controller %s\n", $controller->{name} if $verbose;
      my $controller_errorlevel;
      my $controller_errormessage;
      if ($controller_errorlevel = $controller->nagios_status(\$controller_errormessage)) {
        push(@{$messages[$controller_errorlevel]}, $controller_errormessage);
        if ($controller_errorlevel == 2) {
          $errorlevel = $controller_errorlevel;
        } elsif ($errorlevel != 2) {
          $errorlevel = $controller_errorlevel;
        }
      } else {
        push(@{$messages[0]}, $controller_errormessage);
      }
    }
    $nagios_message = join(", ", 
        @{$messages[2]}, @{$messages[1]}, @{$messages[3]}) ||
        sprintf "no problems (%s)", join(", ", @{$messages[0]});
    $nagios_status = $errorlevel;
    return $nagios_status;
  }
}


package AAC::Controller;

use strict;

{
  my @controllers = ();

  sub init {
    my $aacclicmd = sprintf "%s%s controller list </dev/zero 2>&1", 
        ($> == 0) ? "" : "sudo -S ", $AAC::aaccli;
    printf STDERR "calling %s\n", $aacclicmd if $AAC::verbose;
    my $aaccli = `$aacclicmd`;
    if ($aaccli =~ /(sudo)|(assword:)|(asswort:)/) {
      printf "UNKNOWN - sudo problem with %s\n", $aacclicmd;
      exit 3;
    } else {
      foreach (split(/\n/, $aaccli)) {
        if (/^(aac\d+)/) {
          push(@controllers, AAC::Controller->new(name => $1));
        }
      }
    }
  }
  
  sub get_controllers {
    return @controllers;
  }

}

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
      name => $params{name},
      disks => [],
      containers => []
  }; 
  bless $self, $class;
  $self->init_disks();
  $self->init_containers();
  #printf "%s\n", Data::Dumper::Dumper($self->{disks});
  #printf "%s\n", Data::Dumper::Dumper($self->{containers});
  return $self;
}

sub get_disks {
  my $self = shift;
  return @{$self->{disks}};
}

sub get_containers {
  my $self = shift;
  return @{$self->{containers}};
}

sub init_disks {
  my $self = shift;
  my @disks = ();
  my $aacclicmd = sprintf "%s%s open /readonly %s : disk show smart /full </dev/zero 2>&1", 
      ($> == 0) ? "" : "sudo -S ", $AAC::aaccli, $self->{name};
  printf STDERR "calling %s\n", $aacclicmd if $AAC::verbose;
  my $aaccli = `$aacclicmd`;
  if ($aaccli =~ /(sudo)|(assword:)|(asswort:)/) {
    printf "UNKNOWN - sudo problem with %s\n", $aacclicmd;
    exit 3;
  } else {
    foreach (split(/\n/, $aaccli)) {
      if (/^(\d+):(\d+):(\d+)\s+([YN])\s+\d\s+[YN]\s+[YN]\s+[YN]\s+\d+\s+(\d+)\s+(\d+)/) {
        push(@disks, AAC::Disk->new(controller => $1,
            target => $2, lun => $3, smart => ($4 eq "Y") ? 1 : 0, 
            reportcount => $5, errorcount => $6));
      } elsif (/^(\d+):(\d+):(\d+)\s+N/) {
        push(@disks, AAC::Disk->new(controller => $1,
            target => $2, lun => $3, smart => 0));
      } 
    }
  }
  $self->{disks} = \@disks;
}

sub init_containers {
  my $self = shift;
  my @containers = ();
  my $aacclicmd = sprintf "%s%s open /readonly %s : container list /full </dev/zero 2>&1", 
      ($> == 0) ? "" : "sudo -S ", $AAC::aaccli, $self->{name};
  printf STDERR "calling %s\n", $aacclicmd if $AAC::verbose;
  my $aaccli = `$aacclicmd`;
  if ($aaccli =~ /(sudo)|(assword:)|(asswort:)/) {
    printf "UNKNOWN - sudo problem with %s\n", $aacclicmd;
    exit 3;
  } else {
    my $in_container = "";
    foreach (split(/\n/, $aaccli)) {
      if (/^\s*(\d+)\s+Stripe\s+([\w\.]+)\s+/) {
      } elsif (/^\s*(\d+)\s+Mirror\s+([\w\.]+)\s+(\d+:\d+:\d+)\s+([\w\.]+)[:!]\s*([\w\.]+)\s+(\w+)\s+?([RO]*?)\s+?(L*?)\s+?(Create|FmtFAT|FmtNTFS|Rebuild|Reconfig|Scrub|Verify|VfyRepl|Zero)*?\s+?(\d+%)*?\s+?(\d+)\s+(\d{6})\s+(\d\d:\d\d:\d\d)\s*$/) {
        # example 2.0, 2.4
        my $mirror = AAC::Container::Mirror->new(label => $1,
            size => $2);
        $mirror->add_half(element => $11, state => lc $6, task => lc $9, done => $10);
        push(@containers, $mirror);
        $in_container = "mirror";
      } elsif (/^\s*(\d+)\s+Mirror\s+([\w\.]+)\s+Valid\s+(\d+:\d+:\d+)\s+([\w\.]+)[:!]\s*([\w\.]+)\s+(\w+)\s+?([RO]*?)\s+?(L*?)\s+?(Create|FmtFAT|FmtNTFS|Rebuild|Reconfig|Scrub|Verify|VfyRepl|Zero)*?\s+?(\d+%)*?\s+?(\d+)\s+(\d{6})\s+(\d\d:\d\d:\d\d)\s*$/) {
        # example 2.8
        my $mirror = AAC::Container::Mirror->new(label => $1,
            size => $2);
        $mirror->add_half(element => $11, state => lc $6, task => lc $9, done => $10);
        push(@containers, $mirror);
        $in_container = "mirror";
      } elsif (/^\s*(\d+:\d+:\d+)\s+([\w\.]+)[:!]\s*([\w\.]+)\s+(\w*?)\s+([RO]*)\s+(L*)\s+(Create|FmtFAT|FmtNTFS|Rebuild|Reconfig|Scrub|Verify|VfyRepl|Zero)*\s+([\d%]*)\s+(\d+)\s+\d{6}\s+\d\d:\d\d:\d\d\s*$/) {
        # example 2.0, 2.4 2nd line
        if ($in_container eq "mirror") {
          $containers[$#containers]->add_half(element => $9, state => lc $4, task => lc $7, done => $8);
        } elsif ($in_container eq "raid5") {
          # example 3.0 3rd line
          $containers[$#containers]->add_stripe(element => $9, state => lc $4, task => lc $7, done => $8);
        }
      } elsif (/^\s*[\w\/]*\s+(\d+:\d+:\d+)\s+([\w\.]+)[:!]\s*([\w\.]+)\s+(\w*?)\s+([RO]*)\s+(L*)\s+(Create|FmtFAT|FmtNTFS|Rebuild|Reconfig|Scrub|Verify|VfyRepl|Zero)*\s+([\d%]*)\s+(\d+)\s+\d{6}\s+\d\d:\d\d:\d\d\s*$/) {
        # example 2.8 2nd line
        if ($in_container eq "mirror") {
          $containers[$#containers]->add_half(element => $9, state => lc $4, task => lc $7, done => $8);
        }
      } elsif (/^\s*[\/\w]+\s+(\d+:\d+:\d+)\s+([\w\.]+)[:!]\s*([\w\.]+)\s+(\w*?)\s+([RO]*)\s+(L*)\s+(Create|FmtFAT|FmtNTFS|Rebuild|Reconfig|Scrub|Verify|VfyRepl|Zero)*\s+([\d%]*)\s+(\d+)\s+\d{6}\s+\d\d:\d\d:\d\d\s*$/) {
        if ($in_container eq "raid5") {
          # example 3.0 2nd line
          $containers[$#containers]->add_stripe(element => $9, state => lc $4, task => lc $7, done => $8);
        }
      } elsif (/^\s*[\/\w]+\s+[\w\-]+\s+(\d+:\d+:\d+)\s+([\w\.]+)[:!]\s*([\w\.]+)\s+(\w*?)\s+([RO]*)\s+(L*)\s+(Create|FmtFAT|FmtNTFS|Rebuild|Reconfig|Scrub|Verify|VfyRepl|Zero)*\s+([\d%]*)\s+(\d+)\s+\d{6}\s+\d\d:\d\d:\d\d\s*$/) {
        if ($in_container eq "raid5") {
          # example 3.1 2nd line
          $containers[$#containers]->add_stripe(element => $9, state => lc $4, task => lc $7, done => $8);
        }
      } elsif (/^\s*(\?+:\?+:\?+)\s+(- Missing -)\s+(\w+)\s+.*(\d+)\s+\d{6}\s+\d\d:\d\d:\d\d\s*$/) {
        # example 2.2
        if ($in_container eq "mirror") {
          $containers[$#containers]->add_half(element => $4, state => lc $3 );
        }
      } elsif (/^\s*(\d+)\s+RAID\-5\s+([\w\.]+)\s+[\w\.]+\s+[\w]+\s+(\d+:\d+:\d+)\s+([\w\.]+)[:!]\s*([\w\.]+)\s+(\w*?)\s+?([RO]*?)\s+?(L*?)\s+?(Create|FmtFAT|FmtNTFS|Rebuild|Reconfig|Scrub|Verify|VfyRepl|Zero)*?\s+?(\d+%)*?\s+?(\d+)\s+(\d{6})\s+(\d\d:\d\d:\d\d)\s*$/) {
        my $raid5 = AAC::Container::Raid5->new(label => $1,
            size => $2);
        $raid5->add_stripe(element => $11, state => lc $6, task => lc $9, done => $10);
        push(@containers, $raid5);
        $in_container = "raid5";
      }
    }
  }
  $self->{containers} = \@containers;
}


sub nagios_status {
  my $self = shift;
  my $perrormessage = shift;
  my $errorlevel = 0;
  my @messages = ([], [], [], []);
  foreach my $disk ($self->get_disks()) {
    my $disk_errormessage;
    my $disk_errorlevel;
    if ($disk_errorlevel = $disk->nagios_status(\$disk_errormessage)) {
      push(@{$messages[$disk_errorlevel]}, $disk_errormessage);
      $errorlevel = $disk_errorlevel;
    }
  }
  if (! $errorlevel) {
    push(@{$messages[0]}, sprintf "%d disks",
        scalar($self->get_disks()));
  }
  foreach my $container ($self->get_containers()) {
    my $container_errormessage;
    my $container_errorlevel;
    if ($container_errorlevel = $container->nagios_status(\$container_errormessage)) {
      push(@{$messages[$container_errorlevel]}, $container_errormessage);
      $errorlevel = $container_errorlevel;
    }
  }
  if (! $errorlevel) {
    push(@{$messages[0]}, sprintf "%d containers",
        scalar($self->get_containers()));
  }
  if (! $errorlevel) {
    $$perrormessage = join(", ", @{$messages[0]});
  } else {
    $$perrormessage = join(", ", 
        @{$messages[2]}, @{$messages[1]}, @{$messages[3]});
  }
  return $errorlevel;
}

package AAC::Disk;

use strict;

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
      controller => $params{controller},
      target => $params{target},
      lun => $params{lun},
      smart => $params{smart},
      reportcount => $params{reportcount},
      errorcount => $params{errorcount},
  };
  bless $self, $class;
  return $self;
}

sub nagios_status {
  my $self = shift;
  my $perrormessage = shift;
  my $errorlevel = 0;
  printf STDERR "checking disk %d:%d:%d\n", $self->{controller}, $self->{target}, $self->{lun} if $AAC::verbose;
  if ($self->{smart} && $self->{errorcount}) {
    if ($self->{reportcount} > 10) {
      $errorlevel = 2;
    } else {
      $errorlevel = 1;
    }
    $$perrormessage = sprintf "disk %d:%d:%d s.m.a.r.t counts %d errors",
       $self->{controller}, $self->{target}, $self->{lun}, $self->{errorcount};
  }
  return $errorlevel;
}


package AAC::Container;

use strict;

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

sub nagios_status {
  my $self = shift;
  my $perrormessage = shift; 
  my $errorlevel = 0;
  printf STDERR "checking disk %d:%d:%d\n", $self->{controller}, $self->{target}, $self->{lun} if $AAC::verbose;
  if ($self->{smart} && $self->{errorcount}) {
    if ($self->{reportcount} > 10) { 
      $errorlevel = 2;
    } else {
      $errorlevel = 1;
    }
    $$perrormessage = sprintf "disk %d:%d:%d s.m.a.r.t counts %d errors",
       $self->{controller}, $self->{target}, $self->{lun}, $self->{errorcount};
  }
  return $errorlevel;
} 


package AAC::Container::Mirror;

use strict;

our @ISA = qw(AAC::Container);

sub new {
  my $class = shift;
  my %params = @_;
  my $self = $class->SUPER::new(%params);
  $self->{type} = "mirror";
  $self->{halfs} = [];
  return $self;
}

sub add_half {
  my $self = shift;
  my %params = @_;
  push(@{$self->{halfs}}, AAC::Container::Mirror::Half->new(
      element => $params{element}, state => $params{state}, task => $params{task},
      done => $params{done} ));
}

sub nagios_status {
  my $self = shift;
  my $perrormessage = shift; 
  my $errorlevel = 0;
  printf STDERR "checking mirror %s\n", $self->{label} if $AAC::verbose;
  foreach my $half (@{$self->{halfs}}) {
    if ($half->{state} eq "unprot") {
      if ($half->{task} eq "rebuild") {
        $errorlevel = 1;
        $$perrormessage = sprintf "mirror %s rebuilding (%s)", $self->{label}, $half->{done};
      } else {
        $errorlevel = 2;
        $$perrormessage = sprintf "mirror %s broken", $self->{label};
      }
    }
  }
  return $errorlevel;
} 


package AAC::Container::Mirror::Half;

use strict;

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
      element => $params{element},
      state => $params{state},
      task => $params{task} || "idle",
      done => $params{done} || 0,
  };
  bless $self, $class;
  return $self;
}


package AAC::Container::Raid5;

use strict;

our @ISA = qw(AAC::Container);

sub new {
  my $class = shift;
  my %params = @_;
  my $self = $class->SUPER::new(%params);
  $self->{type} = "raid5";
  $self->{stripes} = [];
  return $self;
}

sub add_stripe {
  my $self = shift;
  my %params = @_;
  push(@{$self->{stripes}}, AAC::Container::Raid5::Stripe->new(
      element => $params{element}, state => $params{state}, task => $params{task},
      done => $params{done} ));
}

sub nagios_status {
  my $self = shift;
  my $perrormessage = shift;
  my $errorlevel = 0;
  printf STDERR "checking raid5 %s\n", $self->{label} if $AAC::verbose;
  foreach my $stripe (@{$self->{stripes}}) {
    if ($stripe->{state} eq "unprot") {
      if ($stripe->{task} eq "rebuild") {
        $errorlevel = 1;
        $$perrormessage = sprintf "raid %s rebuilding (%s)", $self->{label}, $stripe->{done};
      } else {
        $errorlevel = 2;
        $$perrormessage = sprintf "raid %s broken", $self->{label};
      }
    }
  }
  return $errorlevel;
}

package AAC::Container::Raid5::Stripe;

use strict;

sub new {
  my $class = shift;
  my %params = @_;
  my $self = {
      element => $params{element},
      state => $params{state},
      task => $params{task} || "idle",
      done => $params{done} || 0,
  };
  bless $self, $class;
  return $self;
}


package AAC::Task;

package main;

use strict;
use Getopt::Long qw(:config no_ignore_case getopt_compat);
use vars qw($PROGNAME $REVISION $TIMEOUT $opt_V $opt_h $opt_t $opt_v $opt_a
    $opt_blacklist);

$PROGNAME = "check_aaccli";
$REVISION = '$Revision: 1.1.1 $';
$TIMEOUT = 60;

my %ERRORS=( OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 );
my %ERRORCODES=( 0 => 'OK', 1 => 'WARNING', 2 => 'CRITICAL', 3 => 'UNKNOWN' );
  

sub print_usage () {
  print "Usage:\n";
  print "  $PROGNAME [-v] [-t <timeout>] [--aaccli=<path_to_aaccli>\n";
  print "  $PROGNAME [-h | --help]\n";
  print "  $PROGNAME [-V | --version]\n";
  print "\n\nOptions:\n";
  print "  -v, --verbose\n";
  print "     Be verbose\n";
  print "  -t, --timeout\n";
  print "     The number of seconds after which the plugin will abort\n";
  print "  --aaccli\n";
  print "     The fully qualified path to the aaccli binary\n";
  print "  -h, --help\n"; 
  print "     Print detailed help screen\n";
  print "  -V, --version\n";
  print "     Print version information\n\n";
}

sub print_help () {
  print "Copyright (c) 2007 Gerhard Lausser\n\n";
  print_usage();
  print "\n";
  print "  Check the health of raid controllers with aaccli\n";
  print "\n";
  support();
}
  
sub print_revision ($$) {
  my $commandName = shift;
  my $pluginRevision = shift;
  $pluginRevision =~ s/^\$Revision: //;
  $pluginRevision =~ s/ \$\s*$//;
  print "$commandName $pluginRevision\n";
  print "This nagios plugin comes with ABSOLUTELY NO WARRANTY. You may redistribute\ncopies of this plugin under the terms of the GNU General Public License.\n";
}

sub support () {
  my $support='Send email to gerhard.lausser@consol.de if you have questions\nregarding use of this software. \nPlease include version information with all correspondence (when possible,\nuse output from the --version option of the plugin itself).\n';
  $support =~ s/@/\@/g;
  $support =~ s/\\n/\n/g;
  print $support;
}


if (! GetOptions(
    "t|timeout=i" => \$opt_t,
    "V|version" => \$opt_V,
    "h|help" => \$opt_h,
    "v|verbose" => \$opt_v,
    "a|aaccli=s" => \$opt_a,
    "b|blacklist=s" => \$opt_blacklist
 )) {
  print_help();
  exit 3;
}

if ($opt_t) {
  $TIMEOUT = $opt_t;
}

$SIG{'ALRM'} = sub {
  printf "UNKNOWN - %s timed out after %d seconds\n", $PROGNAME, $TIMEOUT;
  exit $ERRORS{UNKNOWN};
};
alarm($TIMEOUT);

if ($opt_V) {
  print_revision($PROGNAME, $REVISION);
  exit $ERRORS{OK};
}

if ($opt_h) {
  print_help();
  exit $ERRORS{OK};
}


$AAC::verbose = $opt_v;
$AAC::aaccli = $opt_a if $opt_a;
AAC->init();
AAC->nagios_status();
printf "%s - %s\n", $ERRORCODES{$AAC::nagios_status},
    $AAC::nagios_message;
exit $AAC::nagios_status;

__END__

#
# 1 status of the single disks
# 
open /readonly aac0 : disk show smart /full
        Smart    Method of         Enable 
        Capable  Informational     Exception  Performance  Log     Interval       Report  Error  
C:ID:L  Device   Exceptions(MRIE)  Control    Enabled      Errors  Timer (secs.)  Count   Count  
------  -------  ----------------  ---------  -----------  ------  -------------  ------  ------ 
0:00:0     Y            6             Y           N          N       3600             0       0
0:02:0     Y            6             Y           N          N       3600             0       0
0:03:0     Y            6             Y           N          N       3600             0       0
0:01:0     Y            6             Y           N          N       3600             0       0
2:00:0     N


#
# 2.0 status of the containers. normal output
#
open /readonly aac0 : container list /full
Num          Total  Oth Stripe          Scsi   Partition                                      Creation
Label Type   Size   Ctr Size   Usage   C:ID:L Offset:Size   State   RO Lk Task    Done%  Ent Date   Time
----- ------ ------ --- ------ ------- ------ ------------- ------- -- -- ------- ------ --- ------ --------
 0    Stripe  136GB      256KB Open
 /dev/sda             Drive 1
  255  Mirror 68.2GB                    0:01:0 64.0KB:68.2GB Normal                        0  051607 09:58:42
                                       0:00:0 64.0KB:68.2GB Normal                        1  051607 09:58:42
  254  Mirror 68.2GB                    0:03:0 64.0KB:68.2GB Normal                        0  051607 09:58:42
                                       0:02:0 64.0KB:68.2GB Normal                        1  051607 09:58:42


#
# 2.1 status of the containers. output after a disk was pulled
#
open /readonly aac0 : container list /full
Num          Total  Oth Stripe          Scsi   Partition                                      Creation
Label Type   Size   Ctr Size   Usage   C:ID:L Offset:Size   State   RO Lk Task    Done%  Ent Date   Time
----- ------ ------ --- ------ ------- ------ ------------- ------- -- -- ------- ------ --- ------ --------
 0    Stripe  136GB      256KB Valid
 /dev/sda             R1
  255  Mirror 68.2GB                    0:00:0 64.0KB!68.2GB UnProt                        0  041007 10:25:12
                                       0:01:0 64.0KB:68.2GB UnProt                        1  041007 10:25:12
  254  Mirror 68.2GB                    0:02:0 64.0KB:68.2GB Normal                        0  041007 10:25:12
                                       0:04:0 64.0KB:68.2GB Normal                        1  041007 10:25:12


#
# 2.2 status of the containers. output after the disk was put back in
#
open /readonly aac0 : container list /full
Partition                                      Creation
Label Type   Size   Ctr Size   Usage   C:ID:L Offset:Size   State   RO Lk Task    Done%  Ent Date   Time
----- ------ ------ --- ------ ------- ------ ------------- ------- -- -- ------- ------ --- ------ --------
 0    Stripe  136GB      256KB Valid
 /dev/sda             R1
  255  Mirror 68.2GB                    0:01:0 64.0KB:68.2GB UnProt                        0  041007 10:25:12
                                       ?:??:?  - Missing -  UnProt                        1  041007 10:25:12
  254  Mirror 68.2GB                    0:02:0 64.0KB:68.2GB Normal                        0  041007 10:25:12
                                       0:04:0 64.0KB:68.2GB Normal                        1  041007 10:25:12


#
# 2.3 status of the containers. output after resyncing starts
#
open /readonly aac0 : container list /full
Num          Total  Oth Stripe          Scsi   Partition                                      Creation
Label Type   Size   Ctr Size   Usage   C:ID:L Offset:Size   State   RO Lk Task    Done%  Ent Date   Time
----- ------ ------ --- ------ ------- ------ ------------- ------- -- -- ------- ------ --- ------ --------
 0    Stripe  136GB      256KB Valid
 /dev/sda             R1
  255  Mirror 68.2GB                    0:01:0 64.0KB:68.2GB UnProt     L  Rebuild   1%    0  041007 10:25:12
                                       0:00:0 64.0KB:68.2GB UnProt     L  Rebuild   1%    1  041007 10:25:12
  254  Mirror 68.2GB                    0:02:0 64.0KB:68.2GB Normal     L                  0  041007 10:25:12
                                       0:04:0 64.0KB:68.2GB Normal     L                  1  041007 10:25:12


#
# 2.4 !!! there is a variant with a space between offset and size
#
open /readonly aac0 : container list /full
Label Type   Size   Ctr Size   Usage   C:ID:L Offset:Size   State   RO Lk Task    Done%  Ent Date   Time
----- ------ ------ --- ------ ------- ------ ------------- ------- -- -- ------- ------ --- ------ --------
 0    Stripe  409GB      256KB Valid
 /dev/sda             Drive 1
  255  Mirror  136GB                    0:00:0 64.0KB: 136GB Normal                        0  062707 13:22:59
                                       0:01:0 64.0KB: 136GB Normal                        1  062707 13:22:59
  254  Mirror  136GB                    0:02:0 64.0KB: 136GB Normal                        0  062707 13:22:59
                                       0:04:0 64.0KB: 136GB Normal                        1  062707 13:22:59
  253  Mirror  136GB                    0:05:0 64.0KB: 136GB Normal                        0  062707 13:22:59
                                       0:06:0 64.0KB: 136GB Normal                        1  062707 13:22:59

#
# 2.8 just 1 mirror
#
Num          Total  Oth Stripe          Scsi   Partition                                      Creation        System
Label Type   Size   Ctr Size   Usage   C:ID:L Offset:Size   State   RO Lk Task    Done%  Ent Date   Time      Files
----- ------ ------ --- ------ ------- ------ ------------- ------- -- -- ------- ------ --- ------ -------- ------
 0    Mirror 74.4GB            Valid   0:01:0 64.0KB:74.4GB Normal                        0  041007 12:40:12
 /dev/sda                              0:00:0 64.0KB:74.4GB Normal                        1  041007 12:40:12


#
# 3.0 Raid5
#
Num          Total  Oth Stripe          Scsi   Partition                                      Creation        System
Label Type   Size   Ctr Size   Usage   C:ID:L Offset:Size   State   RO Lk Task    Done%  Ent Date   Time      Files
----- ------ ------ --- ------ ------- ------ ------------- ------- -- -- ------- ------ --- ------ -------- ------
 0    RAID-5 1.35TB      256KB Valid   0:00:0 64.0KB: 464GB            L  Scrub    37%    0  051007 01:20:12
 /dev/sde                              0:01:0 64.0KB: 464GB            L  Scrub    37%    1  051007 01:20:12
                                       0:02:0 64.0KB: 464GB            L  Scrub    37%    2  051007 01:20:12
                                       0:03:0 64.0KB: 464GB            L  Scrub    37%    3  051007 01:20:12

#
# 3.1 2 x Raid5
#
Num          Total  Oth Stripe          Scsi   Partition                                      Creation        System
Label Type   Size   Ctr Size   Usage   C:ID:L Offset:Size   State   RO Lk Task    Done%  Ent Date   Time      Files
----- ------ ------ --- ------ ------- ------ ------------- ------- -- -- ------- ------ --- ------ -------- ------
 0    RAID-5  228GB       64KB Open    0:00:0 64.0KB:76.2GB                               0  100504 10:23:32
 /dev/sda             raid5-1          0:01:0 64.0KB:76.2GB                               1  100504 10:23:32
                                       0:02:0 64.0KB:76.2GB                               2  100504 10:23:32
                                       0:03:0 64.0KB:76.2GB                               3  100504 10:23:32

 1    RAID-5  469GB       64KB Open    0:00:0 76.2GB: 156GB                               0  082307 20:35:33
 /dev/sdb             raid5-2          0:01:0 76.2GB: 156GB                               1  082307 20:35:33
                                       0:02:0 76.2GB: 156GB                               2  082307 20:35:33
                                       0:03:0 76.2GB: 156GB                               3  082307 20:35:33

