????
| Current Path : /lib/raider/Raider/Info/ |
| Current File : //lib/raider/Raider/Info/3ware.pm |
use strict;
use warnings;
package Raider::Info::3ware;
use base qw( Raider::Info );
use Raider::Jobs::3ware;
use JSON::Tiny qw(decode_json encode_json);
=head1 NAME
Raider::Info::3ware - 3ware specific instructions for get-info
=head1 DESCRIPTION
3ware specific methods to gather information required for populating the .info
file.
=head1 USAGE
use Raider::Info::3ware;
my $info3ware = Raider::Info::3ware->new();
=head1 METHODS
=head2 get_info()
Initiates all needed steps to generate a finished 3ware info file.
=head2 get_disk_info(\%)
Return the info of a disk.
=head2 extract_LD_info(\%)
Return info on a LD.
=head2 get_controller_attribute(\%args)
Return the specified attribute.
=head2 host_supports_info()
Given no args, return whether or not this physical host supports get-info collection.
Returns:
BOOL (1 or 0)
=cut
my $icmd = '/usr/local/bin/tw_cli';
sub get_info {
my $self = shift;
my $opts = shift;
my $jobs3ware = Raider::Jobs::3ware->new();
$jobs3ware->icmd_in_path({ icmd => "$icmd" });
my $controller_list_ref = $jobs3ware->get_controller_list();
my @controller_list = @$controller_list_ref;
my $d_struct_disks = { };
my $d_struct_controllers = { };
my $storm_d_struct = { controllers => { }, };
unless ($self->host_supports_info()) {
$self->logger({cat => 'w', msg => "this host doesnt support get-info collection for 3ware devices; disabling"});
return ($d_struct_controllers,$d_struct_disks,$opts->{cntrl_cnt},$opts->{disk_cnt});
}
my $d_struct_map_file_new;
for my $controller ( @controller_list ) {
my $controller_num = $controller;
$controller_num =~ s/\D//g; # remove non-numeric characters.
my $cntrl_model = $self->get_controller_attribute({ controller => "$controller", attrib => 'model' });
$cntrl_model =~ tr/ //ds;
my $cntrl_firmware = $self->get_controller_attribute({ controller => "$controller", attrib => 'firmware' });
my $cntrl_numdrives = $self->get_controller_attribute({ controller => "$controller", attrib => 'numdrives' });
my $cntrl_numarrays = $self->get_controller_attribute({ controller => "$controller", attrib => 'numunits' });
my $cntrl_serial = $self->get_controller_attribute({ controller => $controller, attrib => 'serial' });
my $cntrl_memory;
for my $line ( split /^/, `$icmd info $controller diag` ) {
if ( $line =~ /^###\s+Memory\s+Installed(\s+)?:\s+(.+)/i ) {
$cntrl_memory = $2;
}
}
$d_struct_controllers->{ $opts->{cntrl_cnt} }->{'identifier'} = "$cntrl_model\:::$cntrl_serial";
$d_struct_controllers->{ $opts->{cntrl_cnt} }->{'make'} = '3ware';
$d_struct_controllers->{ $opts->{cntrl_cnt} }->{'model'} = $cntrl_model;
$d_struct_controllers->{ $opts->{cntrl_cnt} }->{'firmware'} = $cntrl_firmware;
$d_struct_controllers->{ $opts->{cntrl_cnt} }->{'numdrives'} = $cntrl_numdrives;
$d_struct_controllers->{ $opts->{cntrl_cnt} }->{'numlds'} = $cntrl_numarrays;
$d_struct_controllers->{ $opts->{cntrl_cnt} }->{'serial'} = $cntrl_serial;
# Fudge this since none of our legacy 3ware 8000 series cards have BBU...
$d_struct_controllers->{ $opts->{cntrl_cnt} }->{'bbu_present'} = 0;
$d_struct_controllers->{ $opts->{cntrl_cnt} }->{'memory'} = $cntrl_memory;
$storm_d_struct->{'controllers'}->{ $controller_num } = { disks => { } };
$storm_d_struct->{'controllers'}->{ $controller_num }->{'make'} = '3ware';
$storm_d_struct->{'controllers'}->{ $controller_num }->{'model'} = $cntrl_model;
$storm_d_struct->{'controllers'}->{ $controller_num }->{'firmware'} = $cntrl_firmware;
$storm_d_struct->{'controllers'}->{ $controller_num }->{'numdrives'} = $cntrl_numdrives;
$storm_d_struct->{'controllers'}->{ $controller_num }->{'numarrays'} = $cntrl_numarrays;
if ( ! defined($d_struct_map_file_new->{ "$cntrl_model\:::$cntrl_serial" }) ) {
$d_struct_map_file_new = { "$cntrl_model\:::$cntrl_serial" => {} };
}
my $cntrl_info = `$icmd info $controller`;
my @unit_list = ();
for my $line ( split /^/, $cntrl_info ) {
if ( $line =~ /^(u\d+)\s+RAID-\d+/i ) {
push(@unit_list, $1);
}
}
my @phys_disk_list = ();
for my $line ( split /^/, $cntrl_info ) {
if ( $line =~ /^(p\d+)\s+/i ) {
if ( $line !~ /NOT-PRESENT/i ) {
push(@phys_disk_list, $1);
}
}
}
for my $each_unit ( @unit_list ) {
my $each_unit_stripped = $each_unit;
$each_unit_stripped =~ s/\D//g; # remove non-numeric characters.
my @phys_disks_in_unit = ();
my $unidentifiable_disks_unit = 0;
# Get port(s) assigned to $each_unit.
for my $line ( split /^/, $cntrl_info ) {
if ( $line =~ /^(p\d+)\s+\S+\s+$each_unit\s+/i ) {
my $port = $1;
my ($serial,$model);
for my $line ( split /^/, `$icmd /$controller/$port show all` ) {
if ( $line =~ /^\S+\s+Serial\s+=\s+(\S+)/i ) {
$serial = $1;
}
# If no serial, it's not identifiable.
elsif ( $line =~ /^\S+\s+Serial\s+=.+/i ) {
$unidentifiable_disks_unit++;
}
elsif ( $line =~ /^\S+\s+Model\s+=\s+(\S+)/i ) {
$model = $1;
}
}
if ( defined($model) && defined($serial) ) {
push(@phys_disks_in_unit, "$model\:::$serial");
}
}
}
my $health_check = `$icmd info $controller $each_unit`;
my $state = $jobs3ware->array_is_ok({ health_check => $health_check });
my ($raid_level,$numdrives,$size,$block_device,$raid_state,$stripe_size,$size_unparsed) = $self->extract_LD_info({ controller => $controller, unit => $each_unit });
# We don't identify 3ware LD's by serial like we do for LSI or Adaptec because 3ware 8000 series cards (which is all our current 3ware stock) don't support
# serial identification for a LD (9000 series appear to FYI). Instead, we just identify by unit number here.
# --ssullivan May 6th, 2013
$d_struct_controllers->{ $opts->{cntrl_cnt} }->{'logical_disks'}->{ $each_unit_stripped } = {
identifier => "$cntrl_model\:::$cntrl_serial\:::$each_unit_stripped",
numdrives => $numdrives,
ld_num => $each_unit_stripped,
type => 'RAID',
stripe_size => $stripe_size,
raidlevel => $raid_level,
state => $raid_state,
size_mb => $size,
size_unparsed => $size_unparsed,
block_device => $block_device
};
# Populate arrays disks..
$d_struct_controllers->{ $opts->{cntrl_cnt} }->{'logical_disks'}->{ $each_unit_stripped }->{'unidentifiable_disks'} = $unidentifiable_disks_unit;
$d_struct_controllers->{ $opts->{cntrl_cnt} }->{'logical_disks'}->{ $each_unit_stripped }->{physical_disks} = \@phys_disks_in_unit;
$storm_d_struct->{'controllers'}->{ $controller_num }->{'arrays'}->{ $each_unit_stripped } = { numdrives => $numdrives, raidlevel => $raid_level, state => $state };
};
my $unidentifiable_disks_cntrl_total_counter = 0;
for my $each_phys_disk ( @phys_disk_list ) {
my $disk_num = $each_phys_disk;
$disk_num =~ s/[A-z]//g;
my ($disk_type,$size_mb,$port,$model,$firmware,$serial,$state,$smart_attribs,
$block_device,$device_id,$size_unparsed) = $self->get_disk_info({ controller => $controller, phys_disk => $each_phys_disk });
if ( $model =~ /^\[No|unknown/i || $serial =~ /^\[No|unknown/i ) {
$self->logger({ cat => 'i', msg => "PD [$disk_num] not responding to SMART inquiries." });
if ( $self->map_file_exists({ device => '3ware_device_map' }) ) {
$self->logger({ cat => 'i', msg => "RAIDER MAP entry file exists." });
# Open file, read contents.
my $device_map_file = "$Raider::Base::base_conf{'data_path'}/3ware_device_map.info";
open FILE, "<$device_map_file";
my $device_map_file_text = do { local $/; <FILE> };
close(FILE);
# Decode.
my $d_struct_map_file;
eval {
$d_struct_map_file = decode_json($device_map_file_text);
};
if ( $@ ) {
$self->logger({ cat => 'w', msg => "Invalid JSON: [$device_map_file] Cannot decode MAP file!" });
}
else {
# Check and see if this device is identified in our decoded MAP file.
if ( defined($d_struct_map_file->{ "$cntrl_model\:::$cntrl_serial" }->{ "0\:::$port" }) ) {
$model = $d_struct_map_file->{ "$cntrl_model\:::$cntrl_serial" }->{ "0\:::$port" }->{model};
$serial = $d_struct_map_file->{ "$cntrl_model\:::$cntrl_serial" }->{ "0\:::$port" }->{serial};
if ( defined($d_struct_map_file->{ "$cntrl_model\:::$cntrl_serial" }->{ "0\:::$port" }->{disk_type}) ) {
$disk_type = $d_struct_map_file->{ "$cntrl_model\:::$cntrl_serial" }->{ "0\:::$port" }->{disk_type};
}
$self->logger({ cat => 'i', msg => "3ware: Got model+serial from MAP file." });
}
else {
$self->logger({ cat => 'w', msg => "No device entry found in MAP file for [0\:::$port]." });
}
}
}
else {
$self->logger({ cat => 'w', msg => "PD [$disk_num] not responding to SMART inquiries, and no RAIDER MAP entry. Disk will not identify properly!" });
}
}
# Only add to MAP file and disks key if model-serial is valid.
if ( $model !~ /^\[No|unknown/i && $serial !~ /^\[No|unknown/i ) {
$d_struct_map_file_new->{ "$cntrl_model\:::$cntrl_serial" }{ "0\:::$port" } = {
model => "$model",
serial => "$serial",
disk_type => "$disk_type"
};
# Our 3ware cards (8000 series) only have single channel, so hard code for now.
$d_struct_disks->{ $opts->{disk_cnt} } = {
identifier => "$model\:::$serial",
type => $disk_type,
size_mb => $size_mb,
size_unparsed => $size_unparsed,
port => $port,
channel => '0',
model => $model,
firmware => $firmware,
serial => $serial,
state => $state,
block_device => $block_device,
device_id => $device_id
};
if ( $smart_attribs ne 'unsupported' ) {
$d_struct_disks->{ $opts->{disk_cnt} }->{'smart_attributes'} = $smart_attribs;
}
}
else {
# Increment unidentifiable_disks counter.
$unidentifiable_disks_cntrl_total_counter++;
}
$storm_d_struct->{'controllers'}->{ $controller_num }->{'disks'}->{ $disk_num } = { type => $disk_type };
# Increment our passed disk counter.
$opts->{disk_cnt}++;
};
# Populate unidentifiable_disks.
$d_struct_controllers->{ $opts->{cntrl_cnt} }->{'unidentifiable_disks'} = $unidentifiable_disks_cntrl_total_counter;
# Increment our passed controller counter.
$opts->{cntrl_cnt}++;
};
$self->write_json({data => $storm_d_struct, device => '3ware'});
if ( defined($d_struct_map_file_new) ) {
$self->write_json({data => $d_struct_map_file_new, device => '3ware_device_map'});
}
return ($d_struct_controllers,$d_struct_disks,$opts->{cntrl_cnt},$opts->{disk_cnt});
}
sub extract_LD_info {
my $self = shift;
my $opts = shift;
my $num_drives = 0;
my $raid_level = 'unknown';
my $block_device = 'unknown';
my $raid_state = 'unknown';
my $stripe_size = 'unknown';
my $size_unparsed = 'unknown';
my $size_value = 0;
my $size_unit;
my $size_mb = 0;
for my $line ( split /^/, `$icmd info $opts->{controller} $opts->{unit}` ) {
if ( $line =~ /^Unit\s+UnitType\s+Status\s+\S+\s+Port\s+Stripe\s+Size\((\S+)\)\s+Blocks/i ) {
$size_unit = $1;
}
if ( $line =~ /^u\d+-\d+\s+DISK/i ) {
$num_drives++;
}
if ( $line =~ /^u\d+\s+RAID-(\d+)\s+(\S+)\s+\S+\s+\S+\s+(\S+)\s+(\S+)/i ) {
$raid_level = $1;
$raid_state = $2;
$stripe_size = $3;
$size_value = $4;
}
if ( $line =~ /^u\d+\s+RAID-\d+\s+\S+\s+\S+\s+\S+\s+\S+\s+(\S+)/i ) {
$size_unparsed = $1;
# 3ware doesn't tack unit after value
$size_unparsed .= " $size_unit";
}
}
$size_mb = $self->convert_to_mb({ unit => $size_unit, value => $size_value });
# Find block device of our LD
my $numeric_unit;
if ( $opts->{unit} =~ /u(\d+)/i ) {
$numeric_unit = $1;
}
my $disc_block_devices_ref = $self->get_block_devices();
for my $each_block_device ( @{ $disc_block_devices_ref } ) {
my $smartctl_info_out = `smartctl -i /dev/$each_block_device`;
if ( $smartctl_info_out =~ /3ware/ && $smartctl_info_out !~ /Device(\s+)?:\s+3ware\s+/ ) {
$block_device = "/dev/$each_block_device";
}
elsif ( $smartctl_info_out =~ /Device(\s+)?:\s+3ware\s+Logical\s+Disk\s+$numeric_unit\s+/i ) {
$block_device = "/dev/$each_block_device";
}
}
return ($raid_level, $num_drives, $size_mb, $block_device, $raid_state, $stripe_size, $size_unparsed);
}
sub get_disk_info {
my $self = shift;
my $opts = shift;
my $dtype = 'unknown';
my $model = 'unknown';
my $port = 'unknown';
my $firmware = 'unknown';
my $serial = 'unknown';
my $state = 'unknown';
my $size_unparsed = 'unknown';
my $size_value = 0;
my $size_unit;
my $size_mb = 0;
for my $line ( split /^/, `$icmd /$opts->{controller}/$opts->{phys_disk} show all` ) {
if ( $line =~ /^\S+\s+Model\s+=(.+)/i ) {
$model = $1;
$model =~ tr/ //ds;
}
if ( $line =~ /^\S+\s+Status\s+=\s+(\S+)/i ) {
$state = $1;
}
if ( $line =~ /^\S+\s+Serial\s+=(.+)/i ) {
$serial = $1;
$serial =~ tr/ //ds;
}
if ( $line =~ /^\S+\s+Firmware\s+Version\s+=(.+)/i ) {
$firmware = $1;
$firmware =~ tr/ //ds;
}
if ( $line =~ /^\S+\s+Capacity\s+=\s+(\S+)\s+(\S+)/i ) {
$size_value = $1;
$size_unit = $2;
}
if ( $line =~ /^\S+\s+Capacity\s+=(.+)/i ) {
$size_unparsed = $1;
}
if ( $line =~ /^\/c\d+\/p(\d+)/i ) {
$port = $1;
}
}
if ( $model =~ /SSD|INTEL|M4-CT/ ) {
$dtype = 'SSD';
}
else {
$dtype = 'SATA';
}
$size_mb = $self->convert_to_mb({ value => $size_value, unit => $size_unit });
my $smart_attribs = 'unsupported';
# /dev/twa0 refers to the first 9000-series controller, /dev/twa1 refers to the second 9000 series controller, and so on.
# Likewise /dev/twe0 refers to the first 6/7/8000-series controller, /dev/twa1 refers to the second 6/7/8000 series
# controller, and so on.
my $device_prefix;
if ( $model =~ /^9\d\d\d/i ) {
$device_prefix = '/dev/twa';
}
else {
$device_prefix = '/dev/twe';
}
# Verify our device exists
my $controller_numeric;
if ( $opts->{controller} =~ /\S(\d+)/ ) {
$controller_numeric = $1;
}
my $device_to_query = "$device_prefix$controller_numeric";
if ( ! -e $device_to_query ) {
$self->logger({ cat => 'c', msg => "Device to query ($device_to_query) doesn't exist!" });
}
my $smartctl_info = `smartctl -i $device_to_query -d 3ware,$port -T permissive`;
for my $line ( split /^/, $smartctl_info ) {
if ( $line =~ /Serial\s+number(\s+)?:\s+(\S+)/i ) {
if ( $2 eq $serial ) {
$smart_attribs = $self->get_smart_attribs({
device => $device_to_query,
d_flag => "3ware,$port"
});
$smart_attribs = 'unsupported' if ( defined($smart_attribs->{error}) );
}
}
}
# On 3ware, port is also device id
my $device_id = $port;
return ($dtype,$size_mb,$port,$model,$firmware,$serial,$state,$smart_attribs,$device_to_query,$device_id,$size_unparsed);
}
sub get_controller_attribute {
my $self = shift;
my $opts = shift;
my $cntrl_attrib = `$icmd info $opts->{controller} $opts->{attrib}`;
$cntrl_attrib =~ s/.*=//;
$cntrl_attrib = $self->strip_whitespace({ string => $cntrl_attrib });
# If empty, set to error.
$cntrl_attrib = 'error' if ( $cntrl_attrib !~ /\S/ );
return $cntrl_attrib;
}
sub host_supports_info {
my ($self) = @_;
return 1;
}
1;