#!/usr/bin/perl -w
# Copyright 2012 Mathieu Alorent.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of either: the GNU General Public License as published
# by the Free Software Foundation; or the Artistic License.
#
# See http://dev.perl.org/licenses/ for more information.
package Parse::Arcconf;
use warnings;
use strict;
=head1 NAME
Parse::Arcconf - Parse the output of arcconf utility.
=head1 VERSION
Version 0.01
=cut
our $VERSION = '0.02';
=head1 SYNOPSIS
Parse::Arcconf parses the output of C<arcconf> utility to allow
programmatic access to the RAID configuration information provided by the
arcconf utility on Adaptec RAID cards.
use Parse::Arcconf;
my $arcconf = Parse::Arcconf->new();
Run the C<arcconf> tool directly to query the hardware (requires root):
my $controllers = $arcconf->parse_config();
Parse a text file created already by running C<arcconf> tool:
my $controllers = $arcconf->parse_config_file("foo.txt");
Read from a file descriptor already opened by the program:
my $controllers = $arcconf->parse_config_fh(\*STDIN);
=head1 SUBROUTINES/METHODS
=head2 new
Return an instance of the Parse::Arcconf class that can be used to parse
input in one of several ways.
=cut
sub new
{
my ($class, $config, $plugin) = @_;
my $self = {};
bless($self, $class);
return $self;
}
=head2 parse_config
Attempt to run the arcconf utility, and parse the output. This command
actually uses parse_config_fh() after opening a pipe to the relevant command.
The command that is actually run is approximately:
=over 4
arcconf GETCONFIG 1
=back
This command requires root access, and Parse::Arcconf makes no attempt to
use sudo or any other method to gain root access. It is recommended to call
your script which uses this module as root.
The parse_config_fh() and parse_config_file() will expect output equivalent
to that from the above command.
=cut
sub parse_config
{
my ($self) = @_;
my $arcconf = "/usr/sbin/arcconf";
my $argument = "GETCONFIG 1";
my $command = sprintf("%s %s|", $arcconf, $argument);
my $fh;
if(open $fh, $command)
{
my $c = $self->parse_config_fh($fh);
close $fh;
return $c;
}
return undef;
}
=head2 parse_config_file
Open and parse a file containing the output from arcconf.
=cut
sub parse_config_file
{
my ($self, $file) = @_;
my $fh;
if(open $fh, "<".$file)
{
my $c = $self->parse_config_fh($fh);
close $fh;
return $c;
}
return undef;
}
=head2 parse_config_fh
Read from the file handle and parse it, returning a hash-of-hashes.
=cut
sub parse_config_fh
{
my ($self, $fh) = @_;
my $controller = {};
my $total_controller = 0;
my $current_controller = 0;
my $current_logical_drive = undef;
my $current_physical_drive = undef;
my $ctrl = undef;
my $line = undef;
LEVEL1: while($line = <$fh>)
{
chomp $line;
next if($line =~ /^$/);
next if($line =~ /^-+$/);
if($line =~ /^Controllers found: (\d+)$/) {
$total_controller = $1;
}
if($line =~ /^Controller information/) {
$current_controller = $current_controller + 1;
$current_logical_drive = undef;
$current_physical_drive = undef;
$controller->{$current_controller} = {};
$ctrl = $controller->{$current_controller};
while($line = <$fh>) {
chomp $line;
if ($line =~ /^\s+(.*\w)\s+:\s+(.*)$/) {
$ctrl->{$1} = $2;
} elsif ($line =~ /^\s+-+$/) {
last;
}
}
LEVEL2: while($line = <$fh>) {
if ($line =~ /^\s+-+$/) {
$line = <$fh>;
chomp $line;
}
if($line =~ /^\s+(.*\w)\s*/) {
my $cat = $1;
$line = <$fh>;
LEVEL3: while($line = <$fh>) {
chomp $line;
if ($line =~ /^\s+(.*\w)\s+:\s+(.*)$/) {
$ctrl->{$cat}{$1} = $2;
} elsif ($line =~ /^\s+-+$/) {
last LEVEL3;
} elsif ($line =~ /^$/) {
last LEVEL2;
}
}
}
}
}
next if(!defined($current_controller));
if($line =~ /^Logical drive information/) {
LEVEL4: while($line = <$fh>) {
chomp $line;
if ($line =~ /^\S+.*\w\s+(\d+)$/) {
$current_logical_drive = $1;
} elsif ($line =~ /^\s+(\S.*\S+)\s+:\s+(.*)$/) {
$ctrl->{'logical drive'}{$current_logical_drive}{$1} = $2;
} elsif ($line =~ /^\s+-+$/) {
my $cat = <$fh>;
$cat =~ s/^\s+(\S.*\S+)\s+/$1/;
chomp $cat;
LEVEL5: while($line = <$fh>) {
chomp $line;
if ($line =~ /^\s+(\S.*\S+)\s+:\s+(.*)$/) {
$ctrl->{'logical drive'}{$current_logical_drive}{$cat}{$1} = $2;
} elsif ($line =~ /^\S+.*\w\s+(\d+)$/) {
$current_logical_drive = $1;
last LEVEL5;
} elsif ($line =~ /^$/) {
last LEVEL4;
} elsif ($line =~ /^\s+-+$/) {
next;
}
}
}
}
}
if($line =~ /^Physical Device information/) {
LEVEL2: while($line = <$fh>) {
if ($line =~ /^\s+-+$/) {
$line = <$fh>;
chomp $line;
}
if ($line =~ /^\s+Device\s+#(\d+)$/) {
$current_physical_drive = $1;
} elsif ($line =~ /^\s+Device is (.*\w)/) {
$ctrl->{'physical drive'}{$current_physical_drive}{'Device is'} = $1;
} elsif ($line =~ /^\s+(.*\w)\s+:\s+(.*)$/) {
$ctrl->{'physical drive'}{$current_physical_drive}{$1} = $2;
} elsif ($line =~ /^\s+-+$/) {
last LEVEL3;
} elsif ($line =~ /^$/) {
last LEVEL2;
}
}
}
}
return $controller;
}
=head1 AUTHOR
Mathieu Alorent, C<< <kumy at cpan.org> >>
=head1 BUGS
Please report any bugs or feature requests to C<bug-parse-arcconf at rt.cpan.org>, or through
the web interface at L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Parse-Arcconf>. I will be notified, and then you'll
automatically be notified of progress on your bug as I make changes.
=head1 SUPPORT
You can find documentation for this module with the perldoc command.
perldoc Parse::Arcconf
You can also look for information at:
=over 4
=item * Source code
L<https://github.com/kumy/Parse-Arcconf>
=item * RT: CPAN's request tracker
L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=Parse-Arcconf>
=item * AnnoCPAN: Annotated CPAN documentation
L<http://annocpan.org/dist/Parse-Arcconf>
=item * CPAN Ratings
L<http://cpanratings.perl.org/d/Parse-Arcconf>
=item * Search CPAN
L<http://search.cpan.org/dist/Parse-Arcconf/>
=back
=head1 ACKNOWLEDGEMENTS
=head1 LICENSE AND COPYRIGHT
Copyright 2012 Mathieu Alorent.
This program is free software; you can redistribute it and/or modify it
under the terms of either: the GNU General Public License as published
by the Free Software Foundation; or the Artistic License.
See http://dev.perl.org/licenses/ for more information.
This program is based on Parse::HP::ACU a work of Jeremy Cole.
=cut
1; # End of Parse::Arcconf