????
| Current Path : /usr/lib/raider/Raider/Info/ |
| Current File : //usr/lib/raider/Raider/Info/Adaptec.pm |
use strict;
use warnings;
package Raider::Info::Adaptec;
use base qw( Raider::Info );
use Raider::Jobs::Adaptec;
use JSON::Tiny qw(decode_json encode_json);
=head1 NAME
Raider::Info::Adaptec - Adaptec specific instructions for get-info
=head1 DESCRIPTION
Adaptec specific methods to gather information required for populating the .info
file.
=head1 USAGE
use Raider::Info::Adaptec;
my adaptecInfo = Raider::Info::Adaptec->new();
=head1 METHODS
=head1 get_info()
Initiates all needed steps to generate a finished Adaptec info file.
=head2 get_disk_info(\%)
Return info of a disk.
=head2 extract_LD_info(\%)
Return information on the given LD.
=head2 get_controller_attribute(\%args)
Return the specified attribute.
=head2 count_arrays(\%args)
Return the amount of present arrays.
=head2 count_disks(\%args)
Return the amount of present disks.
=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/StorMan/arcconf';
sub get_info {
my $self = shift;
my $opts = shift;
my $jobsAdaptec = Raider::Jobs::Adaptec->new();
$jobsAdaptec->icmd_in_path({ icmd => "$icmd" });
my $controller_list_ref = $jobsAdaptec->get_controller_list();
my @controller_list = @$controller_list_ref;
my $d_struct_controllers = { };
my $d_struct_disks = { };
my $storm_d_struct = { controllers => { }, };
unless ($self->host_supports_info()) {
$self->logger({cat => 'w', msg => "this host doesnt support get-info collection for Adaptec 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 $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_serial = $self->get_controller_attribute({ controller => $controller, attrib => 'serial' });
my $cntrl_memory = $self->get_controller_attribute({ controller => $controller, attrib => 'memory' });
my $cntrl_temp = $self->get_controller_attribute({ controller => $controller, attrib => 'temp' });
my $cntrl_numdrives = $self->count_disks({ controller => "$controller" });
my $cntrl_numarrays = $self->count_arrays({ controller => "$controller" });
$d_struct_controllers->{ $opts->{cntrl_cnt} }->{'identifier'} = "$cntrl_model\:::$cntrl_serial";
$d_struct_controllers->{ $opts->{cntrl_cnt} }->{'make'} = 'Adaptec';
$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;
$d_struct_controllers->{ $opts->{cntrl_cnt} }->{'memory'} = $cntrl_memory;
$d_struct_controllers->{ $opts->{cntrl_cnt} }->{'temperature'} = $cntrl_temp;
$storm_d_struct->{'controllers'}->{ $controller } = { disks => { } };
$storm_d_struct->{'controllers'}->{ $controller }->{'make'} = 'Adaptec';
$storm_d_struct->{'controllers'}->{ $controller }->{'model'} = $cntrl_model;
$storm_d_struct->{'controllers'}->{ $controller }->{'firmware'} = $cntrl_firmware;
$storm_d_struct->{'controllers'}->{ $controller }->{'numdrives'} = $cntrl_numdrives;
$storm_d_struct->{'controllers'}->{ $controller }->{'numarrays'} = $cntrl_numarrays;
my $serial_map_to_model = {};
my ($bbu_present,$bbu_status) = $self->controller_bbu_present({ controller => $controller });
$d_struct_controllers->{ $opts->{cntrl_cnt} }->{'bbu_present'} = $bbu_present;
if ( $bbu_present ) {
$d_struct_controllers->{ $opts->{cntrl_cnt} }->{'bbu_state'} = $bbu_status;
}
## Disk Info
################################
my $unidentifiable_disks_cntrl_total_counter = 0;
my $dnum = 1;
my $d_total = $self->count_disks({ controller => $controller });
while ( $dnum <= $d_total ) {
my ($disk_type,$size_mb,$port,$channel,$model,$firmware,$serial,$state,$smart_attribs,$block_device,$size_unparsed,$write_cache) = $self->get_disk_info({ controller => $controller, phys_disk => $dnum });
if ( $model =~ /^\[No|unknown/i || $serial =~ /^\[No|unknown/i ) {
$self->logger({ cat => 'i', msg => "PD [$dnum] not responding to SMART inquiries." });
if ( $self->map_file_exists({ device => 'adaptec_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'}/adaptec_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" }->{ "$channel\:::$port" }) ) {
$model = $d_struct_map_file->{ "$cntrl_model\:::$cntrl_serial" }->{ "$channel\:::$port" }->{model};
$serial = $d_struct_map_file->{ "$cntrl_model\:::$cntrl_serial" }->{ "$channel\:::$port" }->{serial};
if ( defined($d_struct_map_file->{ "$cntrl_model\:::$cntrl_serial" }->{ "$channel\:::$port" }->{disk_type}) ) {
$disk_type = $d_struct_map_file->{ "$cntrl_model\:::$cntrl_serial" }->{ "$channel\:::$port" }->{disk_type};
}
$self->logger({ cat => 'i', msg => "Adaptec: Got model+serial from MAP file." });
}
else {
$self->logger({ cat => 'w', msg => "No device entry found in MAP file for [$cntrl_model\:::$cntrl_serial -> $channel\:::$port]." });
}
}
}
else {
$self->logger({ cat => 'w', msg => "PD [$dnum] 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" }{ "$channel\:::$port" } = {
model => "$model",
serial => "$serial",
disk_type => "$disk_type"
};
$d_struct_disks->{ $opts->{disk_cnt} } = {
identifier => "$model\:::$serial",
type => $disk_type,
size_mb => $size_mb,
size_unparsed => $size_unparsed,
port => $port,
channel => $channel,
model => $model,
firmware => $firmware,
serial => $serial,
state => $state,
block_device => $block_device,
write_cache => $write_cache
};
if ( $smart_attribs ne 'unsupported' ) {
$d_struct_disks->{ $opts->{disk_cnt} }->{'smart_attributes'} = $smart_attribs;
}
# Increment our passed disk count.
$opts->{disk_cnt}++;
}
else {
# Increment unidentifiable_disks counter.
$unidentifiable_disks_cntrl_total_counter++;
}
$storm_d_struct->{'controllers'}->{ $controller }->{'disks'}->{ $dnum } = { type => $disk_type };
$serial_map_to_model->{ $serial } = { model => $model };
$dnum++;
};
# Populate unidentifiable_disks.
$d_struct_controllers->{ $opts->{cntrl_cnt} }->{'unidentifiable_disks'} = $unidentifiable_disks_cntrl_total_counter;
## Array Info
################################
my $unit_list_ref = $jobsAdaptec->get_array_list({ c => $controller });
my @unit_list = @{ $unit_list_ref };
for my $each_unit ( @unit_list ) {
my $unidentifiable_disks_unit = 0;
my $health_check = `$icmd getconfig $controller LD $each_unit`;
my $state = $jobsAdaptec->array_is_ok({ health_check => $health_check });
my ($raid_level,$numdrives,$size,$block_device,$serial,$raid_state,
$ld_type,$stripe_size,$size_unparsed,$read_cache,$write_cache) = $self->extract_LD_info({
controller => $controller,
controllers => \@controller_list,
unit => $each_unit,
units => \@unit_list
});
if ( ! defined($raid_level) ) {
next;
}
$d_struct_controllers->{ $opts->{cntrl_cnt} }->{'logical_disks'}->{ $each_unit } = {
identifier => "$cntrl_model\:::$cntrl_serial\:::$serial",
ld_num => $each_unit,
numdrives => $numdrives,
raidlevel => $raid_level,
state => $raid_state,
size_mb => $size,
size_unparsed => $size_unparsed,
stripe_size => $stripe_size,
block_device => $block_device,
serial => $serial,
type => $ld_type,
read_cache => $read_cache,
write_cache => $write_cache
};
$storm_d_struct->{'controllers'}->{ $controller }->{'arrays'}->{ $each_unit } = { numdrives => $numdrives, raidlevel => $raid_level, state => $state };
# Record PD serials in this unit where possible. If the disk is bad, it won't show the serial.
# As a result, if that scenario is found we increase the unit level unidentifiable_disks counter.
my @disks_in_unit_serials = ();
for my $line ( split /^/, `$icmd getconfig $controller LD $each_unit` ) {
if ( $line =~ /Segment.+\)\s+(\S+)/i ) {
push(@disks_in_unit_serials, $1);
}
elsif ( $line =~ /Segment\s+(\d+)(\s+)?:.+/i ) {
$unidentifiable_disks_unit++;
}
}
# Populate arrays disks..
my @phys_disks_in_unit = ();
for my $search_serial ( @disks_in_unit_serials ) {
if ( $serial_map_to_model->{ $search_serial } ) {
my $phys_disk_model = $serial_map_to_model->{ $search_serial }->{model};
push(@phys_disks_in_unit, "$phys_disk_model\:::$search_serial");
}
else {
$self->logger({ cat => 'c', msg => "Cannot find model for search serial [$search_serial]" });
}
}
$d_struct_controllers->{ $opts->{cntrl_cnt} }->{'logical_disks'}->{ $each_unit }->{'unidentifiable_disks'} = $unidentifiable_disks_unit;
$d_struct_controllers->{ $opts->{cntrl_cnt} }->{'logical_disks'}->{ $each_unit }->{physical_disks} = \@phys_disks_in_unit;
};
# Incrememt passed controller count.
$opts->{cntrl_cnt}++;
};
$self->write_json({data => $storm_d_struct, device => 'adaptec'});
if ( defined($d_struct_map_file_new) ) {
$self->write_json({data => $d_struct_map_file_new, device => 'adaptec_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 ($udev_info,$raid_level,$num_drives,$size_unparsed,$size_value,$size_unit,$block_device_name);
my $block_device = 'unknown';
my $raid_state = 'unknown';
my $ld_type = 'unknown';
my $write_cache = 'unknown';
my $read_cache = 'unknown';
# RAID 1's don't have stripe size
my $stripe_size = 'N/A';
my $size_mb = 0;
for my $line ( split /^/, `$icmd getconfig $opts->{controller} LD $opts->{unit}` ) {
if ( $line =~ /Segment\s+\d+\s+:/i ) {
$num_drives++;
}
if ( $line =~ /RAID\s+level\s+:\s+(\S+)/i ) {
$raid_level = $1;
$ld_type = 'RAID';
}
if ( $line =~ /^\s+Size(\s+)?:(\s+)?(\d+)\s+(\S+)/i ) {
$size_value = $3;
$size_unit = $4;
}
if ( $line =~ /^\s+Size(\s+)?:(\s+)?(.+)/i ) {
$size_unparsed = $3;
}
if ( $line =~ /Stripe-unit\s+size(\s+)?:(\s+)?(.+)/i ) {
$stripe_size = $3;
}
if ( $line =~ /Status\s+of\s+logical\s+device(\s+)?:\s+(\S+)/i ) {
$raid_state = $2;
}
if ( $line =~ /Read-cache\s+setting(\s+)?:(\s+)?(.+)/i ) {
$read_cache = $3;
}
if ( $line =~ /Write-cache\s+setting(\s+)?:(\s+)?(.+)/i ) {
$write_cache = $3;
}
}
if ( defined($size_mb) && defined($size_unit) ) {
$size_mb = $self->convert_to_mb({ unit => $size_unit, value => $size_value });
}
# Find block device of our LD
if ( defined($raid_level) ) {
$block_device = $self->find_ld_block_device({
ld_num => $opts->{unit},
device => 'adaptec',
lds_array => $opts->{units},
icmd => $icmd,
controller => $opts->{controller},
controllers => $opts->{controllers}
});
if ( $block_device !~ /^\/dev\//i ) {
$block_device = "/dev/$block_device";
}
$block_device_name = $block_device;
$block_device_name =~ s/\/dev\///g;
# Get serial of our LD
$udev_info = $self->get_udev_block_device_info({ block_device_name => $block_device_name });
}
my $id_serial = 'unknown';
$id_serial = $udev_info->{id_serial} if ( ref($udev_info) eq 'HASH' );
return ($raid_level,$num_drives,$size_mb,$block_device,$id_serial,$raid_state,$ld_type,$stripe_size,$size_unparsed,$read_cache,$write_cache);
}
sub get_disk_info {
my $self = shift;
my $opts = shift;
my $dtype = 'unknown';
my $vendor = 'unknown';
my $model = 'unknown';
my $port = 'unknown';
my $channel = 'unknown';
my $firmware = 'unknown';
my $serial = 'unknown';
my $state = 'unknown';
my $block_device = 'unknown';
my $size_unit = 'unknown';
my $size_unparsed = 'unknown';
my $ssd_check = 'No';
my $write_cache = 'unknown';
my $size = 0;
my $size_mb = 0;
my $disk_match_count = 0;
for my $line ( split /^/, `$icmd getconfig $opts->{controller} PD` ) {
if ( $line =~ /^\s+State/ ) {
$disk_match_count++;
}
# Only collect our data if the passthrough is for the physdisk.
if ( $opts->{phys_disk} == $disk_match_count ) {
if ( $line =~ /^\s+Write\s+Cache(\s+)?:\s+(.+)/i ) {
$write_cache = $2;
}
if ( $line =~ /^\s+Transfer\s+Speed\s+:\s+([A-z]+)/i ) {
$dtype = $1;
}
if ( $line =~ /^\s+Vendor\s+:\s+([A-z]+)/i ) {
$vendor = $1;
}
if ( $line =~ /^\s+SSD\s+:\s+([A-z]+)/i ) {
$ssd_check = $1;
}
if ( $line =~ /^\s+Model(\s+)?:(.+)/i ) {
$model = $2;
$model =~ tr/ //ds;
}
if ( $line =~ /^\s+Firmware(\s+)?:(.+)/i ) {
$firmware = $2;
$firmware =~ tr/ //ds;
}
if ( $line =~ /^\s+Serial\s+Number(\s+)?:(.+)/i ) {
$serial = $2;
$serial =~ tr/ //ds;
}
if ( $line =~ /^\s+State(\s+)?:\s+(\S+)/i ) {
$state = $2;
}
# arrconf B20618 (and newer?) have 'Total' in the front here..
if ( $line =~ /^\s+Size(\s+)?:\s+(\d+)\s+(\S+)/i || $line =~ /^\s+Total\s+Size(\s+)?:\s+(\d+)\s+(\S+)/i ) {
$size = $2;
$size_unit = $3;
}
# arrconf B20618 (and newer?) have 'Total' in the front here..
if ( $line =~ /^\s+Size(\s+)?:(\s+)?(.+)/i || $line =~ /^\s+Total\s+Size(\s+)?:(\s+)?(.+)/i ) {
$size_unparsed = $3;
}
if ( $line =~ /\s+Reported\s+Location(\s+)?:\s+Connector\s+(\d+),\s+Device\s+(\d+)/i ) {
$channel = $2;
$port = $3;
}
}
}
$size_mb = $self->convert_to_mb({ unit => $size_unit, value => $size });
if ( $vendor =~ /intel|crucial|kingston/i || $ssd_check =~ /Yes/i || $model =~ /M4-CT|SSD|KINGSTON|C300-/i ) {
$dtype = 'SSD';
}
my $smart_attribs = 'unsupported';
for my $disc_sg_device ( @{ $self->get_sg_devices() } ) {
if ( ! -e "/dev/sg$disc_sg_device" ) {
$self->logger({ cat => 'c', msg => "Discovered sg device (/dev/sg$disc_sg_device) does not exist!" });
}
my $d_sat_success = 0;
for my $line ( split /^/, `smartctl -d sat -i /dev/sg$disc_sg_device -T permissive` ) {
if ( $line =~ /Serial\s+number(\s+)?:\s+(\S+)/i ) {
if ( $2 eq $serial ) {
$block_device = "/dev/sg$disc_sg_device";
$smart_attribs = $self->get_smart_attribs({
device => $block_device,
d_flag => 'sat'
});
$d_sat_success = 1;
$smart_attribs = 'unsupported' if ( defined($smart_attribs->{error}) );
}
}
}
unless ( $d_sat_success ) {
for my $line ( split /^/, `smartctl -i /dev/sg$disc_sg_device -T permissive` ) {
if ( $line =~ /Serial\s+number(\s+)?:\s+(\S+)/i ) {
if ( $2 eq $serial ) {
$block_device = "/dev/sg$disc_sg_device";
$smart_attribs = $self->get_smart_attribs({
device => $block_device,
d_flag => 'sat'
});
$smart_attribs = 'unsupported' if ( defined($smart_attribs->{error}) );
}
}
}
}
}
return ($dtype, $size_mb, $port, $channel, $model, $firmware, $serial, $state, $smart_attribs, $block_device, $size_unparsed, $write_cache);
}
sub get_sg_devices {
my $self = shift;
my $opts = shift;
my @sg_devices;
for my $line ( split /^/, `sginfo -l` ) {
if ( $line =~ /\/dev\/sg(\d+)\s+/i ) {
push(@sg_devices, $1);
}
}
return \@sg_devices;
}
sub get_controller_attribute {
my $self = shift;
my $opts = shift;
my $cntrl_attrib;
for my $line ( split /^/, `$icmd getconfig $opts->{controller} AD` ) {
if ( $opts->{attrib} eq 'model' ) {
if ( $line =~ /Controller\s+Model(\s+)?:(.+)/i ) {
$cntrl_attrib = $2;
}
}
elsif ( $opts->{attrib} eq 'firmware' ) {
if ( $line =~ /Firmware(\s+)?:(.+)/i ) {
$cntrl_attrib = $2;
}
}
elsif ( $opts->{attrib} eq 'serial' ) {
if ( $line =~ /Controller\s+Serial\s+Number(\s+)?:(.+)/i ) {
$cntrl_attrib = $2;
}
}
elsif ( $opts->{attrib} eq 'memory' ) {
if ( $line =~ /Installed\s+Memory(\s+)?:(.+)/i ) {
$cntrl_attrib = $2;
}
}
elsif ( $opts->{attrib} eq 'temp' ) {
if ( $line =~ /Temperature(\s+)?:(.+)/i ) {
$cntrl_attrib = $2;
}
}
}
$cntrl_attrib = $self->strip_whitespace({ string => $cntrl_attrib });
# If empty, set to error.
$cntrl_attrib = 'error' if ( $cntrl_attrib !~ /\S/ );
return $cntrl_attrib;
}
sub count_disks {
my $self = shift;
my $opts = shift;
# Turns out, the assumption an entry for Device\s+#\d+ equals a physical
# device is not valid. We can have entries like this:
#
# Device #8
# Device is an Enclosure services device
#
# Account for this by not incrementing disk_count if the PD section has the
# above string.
# ~ssullivan June 9th, 2015
my $disk_count = 0;
my @ctrl_pds = split(/Device\s+#\d+/, `$icmd getconfig $opts->{controller} PD` );
for my $entry ( @ctrl_pds ) {
# Skip the first bogus entry in slot 0.
next if ( $entry =~ /controllers\s+found/i );
# Increment disk_count unless its an enclosure.
$disk_count++ unless ( $entry =~ /Device\s+is\s+an\s+Enclosure/i );
}
return $disk_count;
}
sub count_arrays {
my $self = shift;
my $opts = shift;
my $utotal = 0;
my $lg_device_num;
for my $line ( split /^/, `$icmd getconfig $opts->{controller} LD` ) {
if ( $line =~ /Logical\s+device\s+number\s+(\d+)/i ) {
$utotal++;
$lg_device_num = $1;
}
}
if ( $utotal == 1 && $lg_device_num != 0 ) {
return $lg_device_num;
}
else {
return $utotal;
}
}
sub controller_bbu_present {
my ($self,$opts) = @_;
my $bbu_check_cmd = `$icmd getconfig $opts->{controller} AD`;
# if ZMM information shows up there is no BBU
if ($bbu_check_cmd !~ /\s+Status(\s+)?:\s+Not\s+Installed/i &&
$bbu_check_cmd !~ /Controller\s+ZMM\s+Information/i) {
for my $line ( split /^/, $bbu_check_cmd ) {
if ( $line =~ /^\s+Status(\s+)?:\s+(.+)/i ) {
return (1,$2);
}
}
}
return (0,'unknown');
}
sub host_supports_info {
my ($self) = @_;
return 1;
}
1;