/
usr
/
lib
/
raider
/
Raider
/
Info
/
File Upload :
llllll
Current File: //usr/lib/raider/Raider/Info/StorCli.pm
use strict; use warnings; package Raider::Info::StorCli; use base qw( Raider::Info ); use Raider::Jobs::StorCli; use JSON::Tiny qw(decode_json encode_json); =head1 NAME Raider::Info::StorCli - StorCli specific instructions for get-info =head1 DESCRIPTION StorCli specific methods to gather information required for populating the .info file. =head1 USAGE use Raider::Info::StorCli; my $info = Raider::Info::StorCli->new(); =head1 METHODS =head2 get_info({ cntrl_cnt => int, disk_cnt => int }) Initiates all needed steps to generate a finished StorCli info file. Return tuple: HASH, HASH, int, int =head2 get_disk_info(jobsObj, int, int, string) Given a jobs object, controller, did, and block device, return the info of the disk. Returns: { dtype => string, model => string, port => int, firmware => string, serial => string, state => string, size_unparsed => string, size_mb => int, smart_attribs => HASH, } =head2 extract_LD_info(jobsObj, int, int) Given a jobs object, controller, and array, return info on the array (LD, logical disk). Retruns: { num_drives => int, raid_level => int, block_device => string, raid_state => string, stripe_size => string, size_unparsed => string, size_mb => int, pds => [ "model::serial" ], unidentifiable_disks => int, } =head2 get_controller_attributes(jobsObject, int) Given a jobsObj, and a controller, return attributes of the controller. Returns: { serial => string, model => string, firmware => string, numdrives => int, numarrays => int, bbu_present => int, memory => string, } =head2 get_cntrl_phys_disk_ids(jobsObj, int) Given a Jobs object and a controller, return the DID's (disk identifiers) attached to the controller. Returns: [ int, ] =head2 get_phys_disk_serial_firmware(int, string) Given a did and a block device, return disk firmware and serial Returns: { firmware => string, serial => string, } =head2 host_supports_info() Given no args, return whether or not this physical host supports get-info collection. Returns: BOOL (1 or 0) =cut sub get_info { my $self = shift; my $opts = shift; my $jobs = Raider::Jobs::StorCli->new(); my @controller_list = @{ $jobs->get_controller_list() }; my $d_struct_disks = { }; # sonar consumer my $d_struct_controllers = { }; # sonar consumer my $storm_d_struct = { controllers => { }, }; # storm consumer unless ($self->host_supports_info()) { $self->logger({cat => 'w', msg => "this host doesnt support get-info collection for StorCli 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 ) { ########################### # Controller data ########################### my $cntrl_attribs = $self->get_controller_attributes($jobs, $controller); $d_struct_controllers->{ $opts->{cntrl_cnt} }->{'identifier'} = "$cntrl_attribs->{model}\:::$cntrl_attribs->{serial}"; $d_struct_controllers->{ $opts->{cntrl_cnt} }->{'make'} = 'LSI'; $d_struct_controllers->{ $opts->{cntrl_cnt} }->{'model'} = $cntrl_attribs->{model}; $d_struct_controllers->{ $opts->{cntrl_cnt} }->{'firmware'} = $cntrl_attribs->{firmware}; $d_struct_controllers->{ $opts->{cntrl_cnt} }->{'numdrives'} = $cntrl_attribs->{numdrives}; $d_struct_controllers->{ $opts->{cntrl_cnt} }->{'numlds'} = $cntrl_attribs->{numarrays}; $d_struct_controllers->{ $opts->{cntrl_cnt} }->{'serial'} = $cntrl_attribs->{serial}; $d_struct_controllers->{ $opts->{cntrl_cnt} }->{'bbu_present'} = $cntrl_attribs->{bbu_present}; $d_struct_controllers->{ $opts->{cntrl_cnt} }->{'memory'} = $cntrl_attribs->{memory}; $storm_d_struct->{'controllers'}->{ $controller } = { disks => { } }; $storm_d_struct->{'controllers'}->{ $controller }->{'make'} = 'LSI'; $storm_d_struct->{'controllers'}->{ $controller }->{'model'} = $cntrl_attribs->{model}; $storm_d_struct->{'controllers'}->{ $controller }->{'firmware'} = $cntrl_attribs->{firmware}; $storm_d_struct->{'controllers'}->{ $controller }->{'numdrives'} = $cntrl_attribs->{numdrives}; $storm_d_struct->{'controllers'}->{ $controller }->{'numarrays'} = $cntrl_attribs->{numarrays}; if ( ! defined($d_struct_map_file_new->{ "$cntrl_attribs->{model}\:::$cntrl_attribs->{serial}" }) ) { $d_struct_map_file_new = { "$cntrl_attribs->{model}\:::$cntrl_attribs->{serial}" => {} }; } ########################### # Logical disk data ########################### my $unidentifiable_disks_total_controller = 0; my $ld_info; for my $array ( @{ $jobs->get_array_list($controller) } ) { $ld_info = $self->extract_LD_info($jobs, $controller, $array); $d_struct_controllers->{ $opts->{cntrl_cnt} }->{'logical_disks'}->{ $array } = { identifier => "$cntrl_attribs->{model}\:::$cntrl_attribs->{serial}\:::$array", numdrives => $ld_info->{num_drives}, ld_num => $array, type => 'RAID', # TODO: support cachecade etc? stripe_size => $ld_info->{stripe_size}, raidlevel => $ld_info->{raid_level}, state => $ld_info->{raid_state}, size_mb => $ld_info->{size_mb}, size_unparsed => $ld_info->{size_unparsed}, block_device => $ld_info->{block_device}, }; $d_struct_controllers->{ $opts->{cntrl_cnt} }->{'logical_disks'}->{ $array }->{'unidentifiable_disks'} = $ld_info->{unidentifiable_disks}; $d_struct_controllers->{ $opts->{cntrl_cnt} }->{'logical_disks'}->{ $array }->{physical_disks} = $ld_info->{pds}; $storm_d_struct->{'controllers'}->{ $controller }->{'arrays'}->{ $array } = { numdrives => $ld_info->{num_drives}, raidlevel => $ld_info->{raid_level}, state => $ld_info->{raid_state} =~ /^Optl/i ? 1 : 0, # prov wants this "bool" }; $unidentifiable_disks_total_controller = $unidentifiable_disks_total_controller + $ld_info->{unidentifiable_disks}; } # Populate unidentifiable_disks. $d_struct_controllers->{ $opts->{cntrl_cnt} }->{'unidentifiable_disks'} = $unidentifiable_disks_total_controller; ########################### # Physical disk data ########################### for my $did ( @{ $self->get_cntrl_phys_disk_ids($jobs, $controller) } ) { my $disk_info = $self->get_disk_info($jobs, $controller, $did, $ld_info->{block_device}); # when a disk fails, it typically isn't visible to the system anymore. Which means of course, we can't do # things like fetch its serial from smartctl. Because we don't want failed disks to disappear yet from the # server until its actually removed, this map stuff is below. This stems from a really old desire from two # now ex employees.. as a part of the original raider+sonar integration (see: sonar.info). The idea at the # time, was that this information would be used for aiding tracking inventory. That didn't really ever # happen tbh.. but lets play along and not create a martyr at least for now. # ssullivan - Oct 2019 if ( $disk_info->{model} =~ /^\[No|unknown/i || $disk_info->{serial} =~ /^\[No|unknown/i ) { $self->logger({ cat => 'i', msg => "PD [$did] on StorCli controller [$controller] not responding to SMART inquiries." }); if ( $self->map_file_exists({ device => 'StorCli_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'}/StorCli_device_map.info"; open FILE, "<$device_map_file" or $self->logger({cat => 'c', msg => "failed opening [$Raider::Base::base_conf{'data_path'}/StorCli_device_map.info]: $!"}); my $device_map_file_text = do { local $/; <FILE> }; close(FILE) or $self->logger({cat => 'c', msg => "failed closing [$Raider::Base::base_conf{'data_path'}/StorCli_device_map.info]: $!"}); # Decode. my $d_struct_map_file = eval { decode_json($device_map_file_text) }; if ( my $e = $@ ) { $self->logger({ cat => 'w', msg => "failed json decoding [$device_map_file]: $e" }); } else { # Check and see if this device is identified in our decoded MAP file. if ( defined($d_struct_map_file->{ "$cntrl_attribs->{model}\:::$cntrl_attribs->{serial}" }->{$did}) ) { $disk_info->{model} = $d_struct_map_file->{ "$cntrl_attribs->{model}\:::$cntrl_attribs->{serial}" }->{$did}->{model}; $disk_info->{serial} = $d_struct_map_file->{ "$cntrl_attribs->{model}\:::$cntrl_attribs->{serial}" }->{$did}->{serial}; if ( defined($d_struct_map_file->{ "$cntrl_attribs->{model}\:::$cntrl_attribs->{serial}" }->{$did}->{dtype}) ) { $disk_info->{dtype} = $d_struct_map_file->{ "$cntrl_attribs->{model}\:::$cntrl_attribs->{serial}" }->{$did}->{dtype}; } $self->logger({ cat => 'i', msg => "StorCli: Got model+serial from MAP file." }); } else { $self->logger({ cat => 'w', msg => "No device entry found in MAP file for [$did] on StorCli controller [$controller]" }); } } } else { $self->logger({ cat => 'w', msg => "PD [$did] on StorCli controller [$controller] not responding to SMART inquiries, and no RAIDER MAP entry. Disk will not identify properly!" }); } } # Only add to MAP file and sonar disks key if model+serial is valid. else { $d_struct_map_file_new->{ "$cntrl_attribs->{model}\:::$cntrl_attribs->{serial}" }{$did} = { model => $disk_info->{model}, serial => $disk_info->{serial}, dtype => $disk_info->{dtype}, }; $d_struct_disks->{ $opts->{disk_cnt} } = { identifier => "$disk_info->{model}\:::$disk_info->{serial}", type => $disk_info->{dtype}, size_mb => $disk_info->{size_mb}, size_unparsed => $disk_info->{size_unparsed}, port => $did, model => $disk_info->{model}, firmware => $disk_info->{firmware}, serial => $disk_info->{serial}, state => $disk_info->{state}, block_device => $ld_info->{block_device}, device_id => $did, }; $d_struct_disks->{ $opts->{disk_cnt} }->{'smart_attributes'} = $disk_info->{smart_attribs}; } $storm_d_struct->{'controllers'}->{ $controller }->{'disks'}->{ $did } = { type => $disk_info->{dtype} }; # Increment our passed disk counter. $opts->{disk_cnt}++; }; # Increment our passed controller counter. $opts->{cntrl_cnt}++; }; $self->write_json({data => $storm_d_struct, device => 'StorCli'}); if ( defined($d_struct_map_file_new) ) { $self->write_json({data => $d_struct_map_file_new, device => 'StorCli_device_map'}); } return ($d_struct_controllers, $d_struct_disks, $opts->{cntrl_cnt}, $opts->{disk_cnt}); } sub extract_LD_info { my ($self, $jobs, $controller, $array) = @_; my $data = $jobs->runStorCliCmdJsonSingleton("/c$controller/v$array show all")->{'Response Data'}; my ($raid_level) = $data->{"/c$controller/v$array"}[0]->{TYPE} =~ /^RAID(\d+)/i; my $block_device = $data->{"VD$array Properties"}{'OS Drive Name'}; my @phys_disks_in_ld; my $unidentifiable_disks = 0; if ($block_device) { my $disks = $jobs->runStorCliCmdJsonSingleton("/c$controller/v$array show all")->{'Response Data'}{"PDs for VD $array"}; for my $disk (@{$disks}) { my $model = $disk->{Model}; $model =~ tr/ //ds; my $did = $disk->{DID}; unless ($did || $model) { $unidentifiable_disks++; next; } my $serial = $self->get_phys_disk_serial_firmware($did, $block_device)->{serial}; unless ($serial) { $unidentifiable_disks++; next; } push(@phys_disks_in_ld, "$model\:::$serial"); } } else { $self->logger({cat => 'w', msg => "failed discovering block device of array [$array] on StorCli controller [$controller]. Some disk information will not be available."}); } my %info = ( num_drives => scalar @{$data->{"PDs for VD $array"}}, raid_level => $raid_level //= 'unknown', block_device => $block_device //= 'unknown', raid_state => $data->{"/c$controller/v$array"}[0]->{State} //= 'unknown', stripe_size => $data->{"VD$array Properties"}{'Strip Size'} //= 'unknown', size_unparsed => $data->{"/c$controller/v$array"}[0]->{Size} //= 'unknown', size_mb => 'unknown', pds => \@phys_disks_in_ld, unidentifiable_disks => $unidentifiable_disks, ); if ($data->{"VD$array Properties"}{'Number of Blocks'}) { $info{size_mb} = $self->convert_to_mb({ unit => 'block', value => $data->{"VD$array Properties"}{'Number of Blocks'} }); } return \%info; } sub get_cntrl_phys_disk_ids { my ($self, $jobs, $controller) = @_; my @phys_disk_ids; my $topology = $jobs->runStorCliCmdJsonSingleton("/c$controller/dall show")->{'Response Data'}{'Response Data'}{TOPOLOGY}; # "DID" is the disk identifier. We need this to fetch information from the physical disk. for my $ld ( @{ $topology } ) { next unless $ld->{Type} =~ /^DRIVE/i; # raid lds will show up here, go away next unless defined $ld->{DID}; # shouldnt happen.. push @phys_disk_ids, $ld->{DID}; } return \@phys_disk_ids; } sub get_disk_info { my ($self, $jobs, $controller, $did, $block_device) = @_; my %info = ( dtype => 'unknown', model => 'unknown', port => 'unknown', firmware => 'unknown', serial => 'unknown', state => 'unknown', size_unparsed => 'unknown', size_mb => 'unknown', smart_attribs => 'unsupported', ); my $drives = $jobs->runStorCliCmdJsonSingleton("/c$controller/dall show all")->{'Response Data'}{'Response Data'}{'DG Drive LIST'}; for my $drive (@{ $drives } ) { next unless defined $drive->{DID}; # 0 is valid next if $drive->{DID} != $did; $info{dtype} = $drive->{Intf}; $info{model} = $drive->{Model}; $info{model} =~ tr/ //ds; $info{port} = $drive->{DID}; $info{size_unparsed} = $drive->{Size}; $info{state} = $drive->{State}; my ($size_value, $size_unit) = $drive->{Size} =~ /(\S+)\s+(\S+)/; if ($size_value && $size_unit) { $info{size_mb} = $self->convert_to_mb({ value => $size_value, unit => $size_unit }); } } my $extra_disk_data = $self->get_phys_disk_serial_firmware($did, $block_device); $info{firmware} = $extra_disk_data->{firmware}; $info{serial} = $extra_disk_data->{serial}; my $smart_attribs = $self->get_smart_attribs({ device => $block_device, d_flag => "megaraid,$did" }); unless ($smart_attribs->{error}) { $info{smart_attribs} = $smart_attribs; } return \%info; } sub get_phys_disk_serial_firmware { my ($self, $did, $block_device) = @_; # smartctl provides integrated support for megaraid powered controllers. Access is obtained as such: # smartctl -i -d megaraid,N $block_device # where <N> stands for the device id on the controller (DID in storcli). my %info; my $smartctl_info = `smartctl -i -d megaraid,$did $block_device -T permissive`; for my $line (split /^/, $smartctl_info) { if ( $line =~ /^Serial\s+number(\s+)?:(.+)/i ) { $info{serial} = $2; $info{serial} =~ tr/ //ds; } elsif ( $line =~ /^Firmware\s+Version(\s+)?:(.+)/i ) { $info{firmware} = $2; $info{firmware} =~ tr/ //ds; } elsif ( $line =~ /^Revision(\s+)?:(.+)/i ) { $info{firmware} = $2; $info{firmware} =~ tr/ //ds; } } return \%info; } sub get_controller_attributes { my ($self, $jobs, $controller) = @_; my $data = $jobs->runStorCliCmdJsonSingleton("/c$controller show all")->{'Response Data'}; my %attribs = ( serial => $data->{'Basics'}{'Serial Number'} //= 'unknown', model => $data->{'Basics'}{Model} //= 'unknown', firmware => $data->{'Version'}{'Firmware Version'} //= 'unknown', numdrives => $data->{'Physical Drives'} //= 'unknown', numarrays => scalar @{ $jobs->get_array_list($controller) }, bbu_present => (ref($jobs->getBbuStatus($controller)) eq 'HASH') ? 1 : 0, memory => $data->{HwCfg}{'On Board Memory Size'} //= 'unknown', ); return \%attribs; } sub host_supports_info { my ($self) = @_; # in testing, the 1000:0014 device (AVAGO MegaRAID SAS 9460-16i) wouldn't fetch full details on the controller # and backing disks (/opt/MegaRAID/storcli/storcli64 /c0 show) without hanging until I updated megaraid_sas # kernel module to version 07.711.04.00 (MR_LINUX_DRIVER_7.11-07.711.04.00-1.tgz). All things required for jobs # would work on the older module, but full controller details required for Info would only work with the newer # version. # kmod-megaraid_sas-07.711.04.00_el7.6-1.x86_64 my $min_ver_numeric = '077110400'; if (`lsmod` !~ /megaraid_sas/) { $self->logger({cat => 'w', msg => "StorCli is registered, but megaraid_sas kernel module isn't loaded. Info collection will be disabled."}); return 0; } my $numeric_ver_running; for my $line (split /^/, `modinfo megaraid_sas`) { if ($line =~ /^version:\s+(\S+)/i) { my $raw_version = $1; $numeric_ver_running = $raw_version; $numeric_ver_running =~ s/\D+//g; } } if ($numeric_ver_running) { if ($numeric_ver_running < $min_ver_numeric) { return 0; } } return 1; } 1;
Copyright ©2k19 -
Hexid
|
Tex7ure