/
usr
/
lib
/
raider
/
Raider
/
Info
/
File Upload :
llllll
Current File: //usr/lib/raider/Raider/Info/FusionMPTSAS2.pm
use strict; use warnings; package Raider::Info::FusionMPTSAS2; use base qw( Raider::Info ); use Raider::Jobs::FusionMPTSAS2; use JSON::Tiny qw(decode_json encode_json); =head1 NAME Raider::Info::FusionMPTSAS2 - FusionMPTSAS2 specific instructions for get-info =head1 DESCRIPTION FusionMPTSAS2 specific methods to gather information required for populating the .info file. =head1 USAGE use Raider::Info::FusionMPTSAS2; my $infoFusionMPTSAS2 = Raider::Info::FusionMPTSAS2->new(); =head1 METHODS =head2 get_info() Initiates all needed steps to generate a finished FusionMPTSAS2 info file. =head2 get_disk_block_device(\%) Return given serials block device. =head2 extract_LD_info(\%) Return info on a LD. =head2 get_base_cntrl_info(\%args) Return base controller info. =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/bin/sas2ircu'; sub get_info { my $self = shift; my $opts = shift; my $jobsFusionMPTSAS2 = Raider::Jobs::FusionMPTSAS2->new(); $jobsFusionMPTSAS2->icmd_in_path({ icmd => $icmd }); my $controller_list_ref = $jobsFusionMPTSAS2->get_controller_list(); my @controller_list = @$controller_list_ref; my $d_struct_disks = { }; my $d_struct_controllers = { }; unless ($self->host_supports_info()) { $self->logger({cat => 'w', msg => "this host doesnt support get-info collection for FusionMPTSAS2 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_info = `$icmd $controller display`; my $base_cntrl_info = $self->get_base_cntrl_info({ c => $controller, cntrl_info => $cntrl_info }); my $cntrl_model = $base_cntrl_info->{'cntrl_model'}; my $cntrl_serial = $base_cntrl_info->{'cntrl_serial'}; $d_struct_controllers->{ $opts->{cntrl_cnt} }->{'identifier'} = "$cntrl_model\:::$cntrl_serial"; $d_struct_controllers->{ $opts->{cntrl_cnt} }->{'make'} = 'LSI'; $d_struct_controllers->{ $opts->{cntrl_cnt} }->{'model'} = $cntrl_model; $d_struct_controllers->{ $opts->{cntrl_cnt} }->{'firmware'} = $base_cntrl_info->{'cntrl_firmware'}; $d_struct_controllers->{ $opts->{cntrl_cnt} }->{'numdrives'} = $base_cntrl_info->{'cntrl_numdrives'}; $d_struct_controllers->{ $opts->{cntrl_cnt} }->{'numlds'} = $base_cntrl_info->{'cntrl_numarrays'}; $d_struct_controllers->{ $opts->{cntrl_cnt} }->{'serial'} = $cntrl_serial; if ( ! defined($d_struct_map_file_new->{ "$cntrl_model\:::$cntrl_serial" }) ) { $d_struct_map_file_new = { "$cntrl_model\:::$cntrl_serial" => {} }; } # Populate physical disks array. my $pd_list = $self->get_pds({ cntrl_info => $cntrl_info }); # Populate logical disks array. my $unit_list = $self->get_lds({ cntrl_info => $cntrl_info }); ########################## ## Physical Disks ## ########################## my $pd_info_store_ld = {}; for my $each_pd ( @{ $pd_list } ) { # Get detailed info on pd my $pd_info = $self->get_pd_info({ cntrl_info => $cntrl_info, pd => $each_pd, cntrl_model => $cntrl_model, cntrl_serial => $cntrl_serial }); # If we got back valid PD data, update our MAP file. if ( defined($pd_info->{'model'}) && defined($pd_info->{'serial'}) && defined($pd_info->{'type'}) ) { $d_struct_map_file_new->{ "$cntrl_model\:::$cntrl_serial" }{ "$pd_info->{'channel'}\:::$pd_info->{'port'}" } = { model => $pd_info->{'model'}, serial => $pd_info->{'serial'}, disk_type => $pd_info->{'type'} }; } $d_struct_disks->{ $opts->{disk_cnt} } = { identifier => "$pd_info->{'model'}\:::$pd_info->{'serial'}", type => $pd_info->{'type'}, size_mb => $pd_info->{'size_mb'}, size_unparsed => $pd_info->{'size_unparsed'}, port => $pd_info->{'port'}, channel => $pd_info->{'channel'}, model => $pd_info->{'model'}, firmware => $pd_info->{'firmware'}, serial => $pd_info->{'serial'}, state => $pd_info->{'state'}, block_device => $pd_info->{'block_device'} }; # Attempt to get the PD's SMART attributes. my $smart_attribs = 'unsupported'; $smart_attribs = $self->get_smart_attribs({ device => $pd_info->{'block_device'}, }); $smart_attribs = 'unsupported' if ( defined($smart_attribs->{error}) ); # Record PD's SMART attributes if supported. if ( $smart_attribs ne 'unsupported' ) { $d_struct_disks->{ $opts->{disk_cnt} }->{'smart_attributes'} = $smart_attribs; } $pd_info_store_ld->{ $each_pd } = $d_struct_disks->{ $opts->{disk_cnt} }; # Increment our passed disks counter. $opts->{disk_cnt}++; } ########################## ## Logical Disks ## ########################## # Make sure pd_list and unit_list are same size. If not, make unit_list # equal pd_list. This is a "temporaryish" thing, full explanation look at # comments in $self->get_lds(). # --ssullivan Oct 29th, 2013 if ( scalar @{ $pd_list } != scalar @{ $unit_list } ) { my $pd_list_size = @{ $pd_list }; $pd_list_size--; # start at 0 $unit_list = [ 0..$pd_list_size ]; } my $unit_pass = 0; for my $each_unit ( @{ $unit_list } ) { $d_struct_controllers->{ $opts->{cntrl_cnt} }->{'logical_disks'}->{ $unit_pass }->{physical_disks} = [ "$pd_info_store_ld->{ @{ $pd_list }[$unit_pass] }->{model}\:::$pd_info_store_ld->{ @{ $pd_list}[$unit_pass] }->{serial}" ]; $d_struct_controllers->{ $opts->{cntrl_cnt} }->{'logical_disks'}->{ $unit_pass }->{size_mb} = $pd_info_store_ld->{ @{ $pd_list }[$unit_pass] }->{size_mb}; $d_struct_controllers->{ $opts->{cntrl_cnt} }->{'logical_disks'}->{ $unit_pass }->{block_device} = $pd_info_store_ld->{ @{ $pd_list }[$unit_pass] }->{block_device}; $unit_pass++; } # Increment our passed controller counter. $opts->{cntrl_cnt}++; } if ( defined($d_struct_map_file_new) ) { $self->write_json({data => $d_struct_map_file_new, device => 'FusionMPTSAS2_device_map'}); } return ($d_struct_controllers,$d_struct_disks,$opts->{cntrl_cnt},$opts->{disk_cnt}); } sub get_pd_info { my $self = shift; my $opts = shift; if ( ! defined($opts->{pd}) || ! defined($opts->{cntrl_info}) || ! defined($opts->{cntrl_model}) || ! defined($opts->{cntrl_serial}) ) { $self->logger({ cat => 'c', msg => "get_pd_info() missing required args!" }); } my ($enc, $slot) = split(/:/,$opts->{pd}); my $pd_store = {}; my $pd_store_tmp = {}; my $pd_section = 0; my $my_pd_section = 0; # Based on our given controller output, attempt to collect the dataz.. for my $line ( split /^/, $opts->{cntrl_info} ) { # Each PD section starts with this if ( $line =~ /Device\s+is\s+a\s+Hard\s+disk/i ) { $pd_section = 1; # Clear out tmp data, this is a new PD. $pd_store_tmp = {}; } # Don't even bother checking $line unless we're in PD section of output. if ( $pd_section ) { if ( $line =~ /^\s+Enclosure\s+#(\s+)?:\s+(\d+)/i ) { $pd_store_tmp->{'enc'} = $2; } elsif ( $line =~ /^\s+Slot\s+#(\s+)?:\s+(\d+)/i ) { $pd_store_tmp->{'slot'} = $2; } # If we have both enc and slot for this PD... if ( defined($pd_store_tmp->{'enc'}) && defined($pd_store_tmp->{'slot'}) ) { # if enc and slot match our given query PD.. if ( $pd_store_tmp->{'enc'} == $enc && $pd_store_tmp->{'slot'} == $slot ) { # this is our given PDs section $my_pd_section = 1; # Update our channel/port data. $pd_store->{'port'} = $pd_store_tmp->{'slot'}; $pd_store->{'channel'} = $pd_store_tmp->{'enc'}; } else { # this is not our given PDs section $my_pd_section = 0; } } # If this is our given PDs section.. if ( $my_pd_section ) { if ( $line =~ /^\s+Drive\s+Type(\s+)?:\s+(\S+)/i ) { $pd_store->{'type'} = $2; } elsif ( $line =~ /^\s+Size\s+\S+\s+MB.+:\s(\d+)\/\d+/i ) { $pd_store->{'size_mb'} = $1; } elsif ( $line =~ /^\s+Size\s+.+:(.+)/i ) { $pd_store->{'size_unparsed'} = $1; } elsif ( $line =~ /^\s+Model\s+Number(\s+)?:\s+(.+)/i ) { my $model_stripped = $2; $model_stripped =~ tr/ //ds; $pd_store->{'model'} = $model_stripped; } elsif ( $line =~ /^\s+Firmware\s+Revision(\s+)?:\s+(\S+)/i ) { $pd_store->{'firmware'} = $2; } elsif ( $line =~ /^\s+Serial\s+No(\s+)?:\s+(\S+)/i ) { $pd_store->{'serial'} = $2; } elsif ( $line =~ /^\s+State(\s+)?:\s+(\S+)/i ) { $pd_store->{'state'} = $2; } # If we haven't yet gotten the block_device.. if ( ! defined($pd_store->{'block_device'}) ) { # But we have our serial, retrieve our block_device if ( defined($pd_store->{'serial'}) ) { $pd_store->{'block_device'} = $self->get_disk_block_device({ serial => $pd_store->{'serial'} }); } } } } } # If our given data wasn't nice, and we couldn't determine the disks model, # serial, or type, let's check our MAP file. Yes, sometimes this happens. # Usually you see this when the given PD is dead, the vendor tool will often # no longer display its model or serial, or type. We attempt to get this # data in this case from the MAP file, so the JSON data collected looks more # sane. This is especially true for Mr. RADAR.. # --ssullivan Oct 29th, 2013 if ( ! defined($pd_store->{'model'}) || ! defined($pd_store->{'serial'}) || ! defined($pd_store->{'type'}) ) { $self->logger({ cat => 'i', msg => "Unable to extract model-serial-type from PD [$pd_store->{'channel'}:$pd_store->{'port'}]" }); # MAP file fallback if ( $self->map_file_exists({ device => 'FusionMPTSAS2_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'}/FusionMPTSAS2_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->{ "$opts->{'cntrl_model'}\:::$opts->{'cntrl_serial'}" }->{ "$pd_store->{'channel'}\:::$pd_store->{'port'}" }) ) { $pd_store->{'model'} = $d_struct_map_file->{ "$opts->{'cntrl_model'}\:::$opts->{'cntrl_serial'}" }->{ "$pd_store->{'channel'}\:::$pd_store->{'port'}" }->{model}; $pd_store->{'serial'} = $d_struct_map_file->{ "$opts->{'cntrl_model'}\:::$opts->{'cntrl_serial'}" }->{ "$pd_store->{'channel'}\:::$pd_store->{'port'}" }->{serial}; $pd_store->{'type'} = $d_struct_map_file->{ "$opts->{'cntrl_model'}\:::$opts->{'cntrl_serial'}" }->{ "$pd_store->{'channel'}\:::$pd_store->{'port'}" }->{type}; $self->logger({ cat => 'i', msg => "FusionMPTSAS2: Got model+serial from MAP file." }); } else { $self->logger({ cat => 'w', msg => "No device entry found in MAP file for [$opts->{'cntrl_model'}\:::$opts->{'cntrl_serial'} -> $pd_store->{'channel'}\:::$pd_store->{'port'}]. Disk will not identify properly!" }); } } } } return $pd_store; } sub get_pds { my $self = shift; my $opts = shift; my @pd_list = (); my $pd_store = {}; my $pd_section = 0; my $first_pass = 1; for my $line ( split /^/, $opts->{cntrl_info} ) { # Each PD section starts with this if ( $line =~ /Device\s+is\s+a\s+Hard\s+disk/i ) { # If this is not the first pass.. if ( ! $first_pass ) { # consider this an error if we don't have enc and slot recorded. if ( ! defined($pd_store->{'enc'}) || ! defined($pd_store->{'slot'}) ) { $self->logger({ cat => 'c', msg => "Unable to find either enclosure [$pd_store->{'enc'}] or slot [$pd_store->{'slot'}]" }); } else { # We have enc and slot. Add to pd_list. push(@pd_list, "$pd_store->{'enc'}:$pd_store->{'slot'}"); } } else { $first_pass = 0; } # Since this is the start of a new PD section, clear out collected enc # and slot info. $pd_store = {}; $pd_section = 1; } if ( $pd_section ) { if ( $line =~ /^\s+Enclosure\s+#(\s+)?:\s+(\d+)/i ) { $pd_store->{'enc'} = $2; $pd_store->{'last'}->{'enc'} = $2; } elsif ( $line =~ /^\s+Slot\s+#(\s+)?:\s+(\d+)/i ) { $pd_store->{'slot'} = $2; $pd_store->{'last'}->{'slot'} = $2; } } } # Don't leave out the last discovered PD push(@pd_list, "$pd_store->{'last'}->{'enc'}:$pd_store->{'last'}->{'slot'}"); return \@pd_list; } sub get_lds { my $self = shift; my $opts = shift; # Right now, we just show every PD as a LD. This isn't really right, since # some FusionMPTSAS2 cards can support RAID (healthchecks are supported in # Jobs/FusionMPTSAS2). However, that setup we are not currently using anywhere # within LiquidWeb, so I have no test hardware available. If we start # offering such setups, we will probably want to update this method to list # RAID LD's, and the disks they contain. However doing this with no test # hardware available is far to error prone, so for now every PD will show as # its own LD (like JBOD, which is typically what these HBA cards are used # for anyways). # --ssullivan Oct 29th, 2013 my @unit_list = (); my $ld_num = 0; for my $line ( split /^/, $opts->{'cntrl_info'} ) { if ( $line =~ /Device is a Hard disk/i ) { push(@unit_list, $ld_num); $ld_num++; } } return \@unit_list; } sub get_disk_block_device { my $self = shift; my $opts = shift; my $block_devices = $self->get_block_devices(); for my $b_device ( @{ $block_devices } ) { for my $line ( split /^/, `smartctl -i /dev/$b_device` ) { if ( $line =~ /^Serial\s+Number(\s+)?:\s+(\S+)/i ) { my $smartctl_serial = $2; # Turns out, sas2ircu removes the - character from a serial. This # causes problems when you are comparing serials as returned by # sas2ircu and smartctl. Remedy this situation by removing the - # character from the serial returned by smartctl. Clearly, one is left # wondering if other characters are filtered out by sas2ircu.. add # here if this turns out to be the case. # --ssullivan Oct 29th, 2013 $smartctl_serial =~ tr/-//ds; if ( $smartctl_serial eq $opts->{serial} ) { return "/dev/$b_device"; } } } } $self->logger({ cat => 'w', msg => "Failed to find corresponding block device for discovered disk serial [$opts->{serial}]" }); return 'unknown'; } sub get_base_cntrl_info { my $self = shift; my $opts = shift; my $cntrl_info = {}; my $pd_counter = 0; my $ld_counter = 0; for my $line ( split /^/, $opts->{cntrl_info} ) { # Controller model if ( $line =~ /^\s+Controller\s+type(\s+)?:\s+(.+)/i ) { $cntrl_info->{'cntrl_model'} = $2; } # Controller firmware version elsif ( $line =~ /^\s+Firmware\s+version(\s+)?:\s+(.+)/i ) { $cntrl_info->{'cntrl_firmware'} = $2; } # Controller numdrives elsif ( $line =~ /^\s+Slot\s+#(\s+)?:\s+\d+/i ) { $pd_counter++; } # Controller numlds elsif ( $line =~ /^Device\s+is\s+a\s+Hard\s+disk/i ) { #elsif ( $line =~ /^IR\s+volume\s+\d+/i ) { $ld_counter++; } } $cntrl_info->{'cntrl_numdrives'} = $pd_counter; $cntrl_info->{'cntrl_numarrays'} = $ld_counter; # sas2ircu doesn't provide a way to get the serial of the controller. So # just use PCI address for now. for my $line ( split /^/, `$icmd list` ) { if ( $line =~ /^\s+$opts->{c}\s+\S+\s+\S+\s+\S+\s+(\S+)/i ) { $cntrl_info->{'cntrl_serial'} = $1; } } return $cntrl_info; } sub host_supports_info { my ($self) = @_; return 1; } 1;
Copyright ©2k19 -
Hexid
|
Tex7ure