/
usr
/
lib
/
raider
/
Raider
/
File Upload :
llllll
Current File: //usr/lib/raider/Raider/Info.pm
use strict; use warnings; package Raider::Info; use base qw( Raider::Base ); use Raider::Jobs; use Raider::Devices; use JSON::Tiny qw(decode_json encode_json); use File::Find (); use vars qw/*name *dir *prune/; *name = *File::Find::name; *dir = *File::Find::dir; *prune = *File::Find::prune; use Cwd (); # Included in perl =head1 NAME Raider::Info - Base class for Info related tasks =head1 DESCRIPTION Base class for all info objects. =head1 USAGE use Raider::Info; my $info_obj = Raider::Info->new(); =head1 METHODS =head2 do_get_info() Delegates the first steps in --get-info command line argument. =head2 get_os() Returns either redhat or debian. If neither, then error is thrown. =head2 get_os_release() Returns LSB OS release. =head2 is_redhat() Returns true if OS is Red Hat. =head2 is_debian() Returns true if OS is Debian (or fork Ubuntu). =head2 write_json(\%args) Write JSON to file. =head2 get_pkg_mngr_cmd() Returns what package manager command to use. =head2 clean() Clean .info file(s). =head2 get_info_files() Return present .info file(s). =cut sub do_get_info { my $self = shift; my $jobsObj = Raider::Jobs->new(); my $devObj = Raider::Devices->new(); my @jobs = @{ $jobsObj->get_job_list() }; if ( scalar @jobs == 0 ) { $self->logger({ cat => 'i', msg => "do_get_info() called but no job files present, calling get_devices()..." }); $devObj->get_devices(); @jobs = @{ $jobsObj->get_job_list() }; } $self->clean(); # removes all .info files my $d_struct_controllers = { controllers => { }, }; my $d_struct_disks = { disks => { }, }; my $d_struct; my ($d_struct_controllers_adaptec,$d_struct_disks_adaptec); my ($d_struct_controllers_3ware,$d_struct_disks_3ware); my ($d_struct_controllers_megaraidsas,$d_struct_disks_megaraidsas); my ($d_struct_controllers_FusionMPTSAS2,$d_struct_disks_FusionMPTSAS2); my ($d_struct_controllers_StorCli,$d_struct_disks_StorCli); my ($d_struct_controllers_PercCli,$d_struct_disks_PercCli); my ($d_struct_controllers_PercCli2,$d_struct_disks_PercCli2); my ($d_struct_usb_storage_satascsiata,$d_struct_controllers_satascsiata,$d_struct_disks_satascsiata); # Always start at zero. my $cntrl_cnt = 0; my $disk_cnt = 0; for my $info_task ( @jobs ) { if ( $info_task eq '3ware' ) { use Raider::Info::3ware; my $info3ware = Raider::Info::3ware->new(); ($d_struct_controllers_3ware,$d_struct_disks_3ware,$cntrl_cnt,$disk_cnt) = $info3ware->get_info({ cntrl_cnt => $cntrl_cnt, disk_cnt => $disk_cnt }); } elsif ( $info_task eq 'MegaraidSAS' ) { use Raider::Info::MegaraidSAS; my $infoMegaraidSAS = Raider::Info::MegaraidSAS->new(); ($d_struct_controllers_megaraidsas,$d_struct_disks_megaraidsas,$cntrl_cnt,$disk_cnt) = $infoMegaraidSAS->get_info({ cntrl_cnt => $cntrl_cnt, disk_cnt => $disk_cnt }); } elsif ( $info_task eq 'MegaraidSATA' ) { use Raider::Info::MegaraidSATA; my $infoMegaraidSATA = Raider::Info::MegaraidSATA->new(); $infoMegaraidSATA->get_info({ cntrl_cnt => $cntrl_cnt, disk_cnt => $disk_cnt }); } elsif ( $info_task eq 'Adaptec' ) { use Raider::Info::Adaptec; my $infoAdaptec = Raider::Info::Adaptec->new(); ($d_struct_controllers_adaptec,$d_struct_disks_adaptec,$cntrl_cnt,$disk_cnt) = $infoAdaptec->get_info({ cntrl_cnt => $cntrl_cnt, disk_cnt => $disk_cnt }); } elsif ( $info_task eq 'satascsiata' ) { use Raider::Info::satascsiata; my $infoSatascsiata = Raider::Info::satascsiata->new(); ($d_struct_usb_storage_satascsiata,$d_struct_controllers_satascsiata,$d_struct_disks_satascsiata,$cntrl_cnt,$disk_cnt) = $infoSatascsiata->get_info({ cntrl_cnt => $cntrl_cnt, disk_cnt => $disk_cnt }); } elsif ( $info_task eq 'Mdraid' ) { next; # TODO "someday"? when MikeN wants to go over this... <-- update: October, 2019.. :( } elsif ( $info_task eq 'FusionMPTSAS2' ) { use Raider::Info::FusionMPTSAS2; my $infoFusionMPTSAS2 = Raider::Info::FusionMPTSAS2->new(); ($d_struct_controllers_FusionMPTSAS2,$d_struct_disks_FusionMPTSAS2,$cntrl_cnt,$disk_cnt) = $infoFusionMPTSAS2->get_info({ cntrl_cnt => $cntrl_cnt, disk_cnt => $disk_cnt }); } elsif ( $info_task eq 'StorCli' ) { # hey, so past self was a bit of a jerk here. use Raider::Info::StorCli; my $info = Raider::Info::StorCli->new(); ($d_struct_controllers_StorCli,$d_struct_disks_StorCli,$cntrl_cnt,$disk_cnt) = $info->get_info({ cntrl_cnt => $cntrl_cnt, disk_cnt => $disk_cnt }); } elsif ( $info_task eq 'PercCli' ) { # hey, so past self was a bit of a jerk here. use Raider::Info::PercCli; my $info = Raider::Info::PercCli->new(); ($d_struct_controllers_PercCli,$d_struct_disks_PercCli,$cntrl_cnt,$disk_cnt) = $info->get_info({ cntrl_cnt => $cntrl_cnt, disk_cnt => $disk_cnt }); } elsif ( $info_task eq 'PercCli2' ) { use Raider::Info::PercCli2; my $info = Raider::Info::PercCli2->new(); ($d_struct_controllers_PercCli2,$d_struct_disks_PercCli2,$cntrl_cnt,$disk_cnt) = $info->get_info({ cntrl_cnt => $cntrl_cnt, disk_cnt => $disk_cnt, }); } else { $self->logger({ cat => 'w', msg => "Unrecognized device file found ($info_task), ignoring..." }); } }; $d_struct_disks = { %{ $d_struct_disks_adaptec || { } }, %{ $d_struct_disks_3ware || { } }, %{ $d_struct_disks_megaraidsas || { } }, %{ $d_struct_disks_FusionMPTSAS2 || { } }, %{ $d_struct_disks_StorCli || { } }, %{ $d_struct_disks_PercCli || { } }, %{ $d_struct_disks_PercCli2 || { } }, %{ $d_struct_disks_satascsiata || { } } }; $d_struct_controllers = { %{ $d_struct_controllers_adaptec || { } }, %{ $d_struct_controllers_3ware || { } }, %{ $d_struct_controllers_megaraidsas || { } }, %{ $d_struct_controllers_FusionMPTSAS2 || { } }, %{ $d_struct_controllers_StorCli || { } }, %{ $d_struct_controllers_PercCli || { } }, %{ $d_struct_controllers_PercCli2 || { } }, %{ $d_struct_controllers_satascsiata || { } } }; my $d_struct_usb_storage = { %{ $d_struct_usb_storage_satascsiata || { } }, }; $d_struct = { controllers => { %{ $d_struct_controllers }, %{ $d_struct_usb_storage } }, physical_disks => { %{ $d_struct_disks } } }; $self->write_json({data => $d_struct, device => 'sonar'}); } sub get_os { my $self = shift; if ( $self->is_redhat ) { return 'redhat'; } elsif ( $self->is_debian ) { return 'debian'; } else { $self->logger({ cat => 'c', msg => "Unsupported operating system." }); } } sub get_os_release { my $self = shift; my $release = `lsb_release -r`; $release =~ s/Release://g; $release =~ s/\n//g; $release = $self->strip_whitespace({ string => $release }); return $release; } sub fetch_cent_ver { my $self = shift; if ( $self->is_centos() ) { my $os_rel = $self->get_os_release(); if ( $os_rel =~ /(\d)\.[0-9]+/ ) { return $1; } else { $self->logger({ cat => 'c', msg => "fetch_cent_ver() Failed to fetch CentOS major version!" }); } } else { $self->logger({ cat => 'w', msg => "fetch_cent_ver() called on non-CentOS platform!" }); } } sub is_centos { my $self = shift; return 0 unless ( $self->is_redhat() ); return 1 if ( -e '/etc/centos-release' ); # The above centos-release file doesn't exist on CentOS 5 and earlier of course. # We have to support back to CentOS 4.. so check redhat-release for centos. if ( -e '/etc/redhat-release' ) { my $rhrel = do { local $/ = undef; open my $fh, "<", '/etc/redhat-release' or $self->logger({ cat => 'c', msg => "Unable to open [/etc/redhat-release]: $!" }); <$fh>; }; return 1 if ( $rhrel =~ /centos/i ); } return 0; } sub is_fedora { return ( -e '/etc/fedora-release' ) ? 1 : 0; } sub is_redhat { my $ret = ( -e '/etc/redhat-release' ) ? 1 : 0; return $ret; } sub is_debian { my $ret = ( -e '/etc/debian_version' ) ? 1 : 0; return $ret; } sub write_json { my $self = shift; my $opts = shift; my $data = $opts->{data}; my $model = $data->{controllers}->{0}->{model}; if ( $model && $model =~ /(SAS 3908|PERC H755)/ ) { my @disks = values %{ $data->{controllers}->{0}->{disks}}; $_->{type} = 'SSD' foreach @disks; } my $json_to_write = encode_json($opts->{data}); my $info_filename; if ( $opts->{device} =~ /_device_map/i ) { $info_filename = "$Raider::Base::base_conf{'data_path'}/$opts->{device}.info"; } else { $info_filename = "$Raider::Base::base_conf{'info_path'}/$opts->{device}.info"; } open INFO_FILE, ">", "$info_filename" or $self->logger({ cat => 'c', msg => "Unable to save $info_filename; got system error: $!" }); print INFO_FILE $json_to_write; close(INFO_FILE); } sub get_pkg_mngr_cmd { my $self = shift; my $os = $self->get_os(); if ( $os eq 'debian' ) { ## Don't use aptitude as of 01/12/12. Whether or not aptitude was able to install the package, its exit ## code will always be 0. This brings about much fail in detecting package manager cmd failures. --ssullivan return 'apt-get -y'; } elsif ( $os eq 'redhat' ) { ## Support legacy 'lpyum'... if ( -e '/usr/local/lp/configs/yum/yum.conf' ) { return 'yum -y -c /usr/local/lp/configs/yum/yum.conf'; } else { return 'yum -y'; } } $self->logger({cat => 'c', msg => "troubles detecting the package manager command os: [$os]"}); } sub clean { my $self = shift; for my $info_file ( @{ $self->get_info_files() } ) { if ( $info_file =~ /raider.lock/ ) { next; } my $to_rm = "$Raider::Base::base_conf{'info_path'}/"; $to_rm .= $info_file; unlink($to_rm); }; } sub get_info_files { my $self = shift; my $info_files_ref = $self->sysFindFile({ search_path => "$Raider::Base::base_conf{'info_path'}" }); return $info_files_ref; } sub get_smart_attribs { my $self = shift; my $opts = shift; if ( $opts->{device} !~ /^\/dev\// ) { $opts->{device} = "/dev/" . $opts->{device}; } my $smart_attribs; my $smart_attrib_output; if ( $opts->{d_flag} ) { $smart_attrib_output = `smartctl -A $opts->{device} -T permissive -d $opts->{d_flag} -s on`; } elsif ( $opts->{bus} && $opts->{bus} eq 'usb' ) { $smart_attrib_output = `smartctl -A $opts->{device} -T permissive -d usbsunplus -s on`; } else { $smart_attrib_output = `smartctl -A $opts->{device} -T permissive -s on`; } my $id_key; for my $line ( split /^/, $smart_attrib_output ) { if ( $line =~ /(\d+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(.+)/i ) { my $id = $1; $id_key = $id; my $attribute_name = $2; my $flag = $3; my $value = $4; my $worst = $5; my $thresh = $6; my $type = $7; my $updated = $8; my $when_failed = $9; my $raw_value = $10; # Turns out, some drives (like the Crucial_CT480M500SSD1 with the MU03 firmware) # show invalid values for some SMART attributes. Example: # # [root@host ~]# smartctl -A /dev/sda|grep 194 # 194 Temperature_Celsius 0x0022 067 059 --- Old_age Always - 33 (0 41 255 246) # 194 Temperature_Celsius <== Data Page | WARNING: PREVIOUS ATTRIBUTE HAS TWO # 194 Temperature_Celsius <== Threshold Page | INCONSISTENT IDENTITIES IN THE DATA # [root@host ~]# # # To account for this, if a ID is defined more than once, set all values # of the attribute to "INVALID". # # --ssullivan Feb 13th, 2014 if ( defined($smart_attribs->{ $id }) ) { $self->logger({ cat => 'w', msg => "get_smart_attribs() Attribute ID [$id] was already defined! Marking INVALID!" }); $smart_attribs->{ $id } = {}; $flag = 'INVALID'; $value = 'INVALID'; $worst = 'INVALID'; $thresh = 'INVALID'; $type = 'INVALID'; $updated = 'INVALID'; $when_failed = 'INVALID'; $raw_value = 'INVALID'; } else { # Only do RAW_VALUE_EXTRA if there is a space. if ( $raw_value =~ /\s/ ) { if ( $raw_value =~ /(\S+)(.+)/i ) { $raw_value = $1; if ( defined($2) && ! defined($smart_attribs->{ $id }) ) { $smart_attribs->{ $id }->{ 'RAW_VALUE_EXTRA' } = $2; } } } } $smart_attribs->{ $id }->{ 'ATTRIBUTE_NAME' } = $attribute_name; $smart_attribs->{ $id }->{ 'FLAG' } = $flag; $smart_attribs->{ $id }->{ 'VALUE' } = $value; $smart_attribs->{ $id }->{ 'WORST' } = $worst; $smart_attribs->{ $id }->{ 'THRESH' } = $thresh; $smart_attribs->{ $id }->{ 'TYPE' } = $type; $smart_attribs->{ $id }->{ 'UPDATED' } = $updated; $smart_attribs->{ $id }->{ 'WHEN_FAILED' } = $when_failed; $smart_attribs->{ $id }->{ 'RAW_VALUE' } = $raw_value; } } # If this doesn't exist, there was an error. Perhaps more than one LSI controller exists, so our # device port is incorrect for this controller. if ( ! $id_key ) { $self->logger({ cat => 'w', msg => "Info/get_smart_attribs() failed to get SMART attributes." }); $smart_attribs = { error => 1 }; } return $smart_attribs; } sub get_smart_info { my $self = shift; my $opts = shift; if ( $opts->{device} !~ /^\/dev\// ) { $opts->{device} = "/dev/" . $opts->{device}; } my $smart_info_output; if ( $opts->{d_flag} ) { $smart_info_output = `smartctl -i $opts->{device} -T permissive -d $opts->{d_flag}`; } else { $smart_info_output = `smartctl -i $opts->{device} -T permissive`; } return $smart_info_output; } sub get_block_devices { my $self = shift; my $opts = shift; my $sysfs_block = '/sys/block'; my @block_devices = (); opendir my $dh, $sysfs_block || $self->logger({ cat => 'c', msg => "get_block_devices() failed to open [$sysfs_block]: $!" }); while (defined(my $name = readdir $dh)) { @block_devices = grep {-d "$sysfs_block/$name" && ! /^\.{1,2}$/} readdir($dh); } closedir $dh; return \@block_devices; } sub get_udev_block_device_info { my $self = shift; my $opts = shift; my $id_serial = 0; my $id_serial_short = 0; my $model = 'unknown'; my $disk_bus = 'unknown'; my $id_vendor = 'unknown'; unless ( $opts->{block_device_name} ) { $self->logger({ cat => 'w', msg => "get_udev_block_device_info() not passed block_device_name, bailing out.." }); return 'unknown'; } # Older distros had a /dev/.udev/db/ dir, use that if it exists. Otherwise # use udevadm. # Ubuntu 12.04 (and greater?) doesn't seem to have a /dev/.udev/db.. my $file = 0; if ( -e "/dev/.udev/db/block:$opts->{block_device_name}" ) { $file = "/dev/.udev/db/block:$opts->{block_device_name}"; } elsif ( -e "/dev/.udev/db/block\@$opts->{block_device_name}" ) { $file = "/dev/.udev/db/block\@$opts->{block_device_name}"; } if ( $file ) { open my $fh, '<', $file || $self->logger({ cat => 'c', msg => "Unable to open $file [$!]" }); while ( defined( my $line = <$fh> ) ) { if ( $line =~ /^E:ID_SERIAL=(.+)/i ) { $id_serial = $1; } if ( $line =~ /^E:ID_SERIAL_SHORT=(.+)/i ) { $id_serial_short = $1; } if ( $line =~ /^E:ID_MODEL=(.+)/i ) { $model = $1; } if ( $line =~ /^E:ID_BUS=(.+)/i ) { $disk_bus = $1; } if ( $line =~ /^E:ID_VENDOR=(.+)/i ) { $id_vendor = $1; } }; close $fh; } else { # Old distros like CentOS 4 do not have udevadm. Prevent non fatal split # warning in this case. if ( $self->in_path({ cmds => [ 'udevadm' ], mode => 'nofatal' }) eq 'present' ) { for my $line ( split /^/, `udevadm info --query=property --path=/sys/block/$opts->{block_device_name}` ) { if ( $line =~ /^ID_SERIAL=(.+)/i ) { $id_serial = $1; } if ( $line =~ /^ID_SERIAL_SHORT=(.+)/i ) { $id_serial_short = $1; } if ( $line =~ /^ID_MODEL=(.+)/i ) { $model = $1; } if ( $line =~ /^ID_BUS=(.+)/i ) { $disk_bus = $1; } if ( $line =~ /^ID_VENDOR=(.+)/i ) { $id_vendor = $1; } } } else { $self->logger({ cat => 'i', msg => "Unable to fetch block device information from udev for [$opts->{block_device_name}]" }); } } my $udev_info = {}; if ( $id_serial_short ) { $udev_info = { model => $model, id_serial => $id_serial_short, disk_bus => $disk_bus, id_vendor => $id_vendor }; } elsif ( $id_serial ) { $udev_info = { model => $model, id_serial => $id_serial, disk_bus => $disk_bus, id_vendor => $id_vendor }; } else { $udev_info = { model => $model, id_serial => 'unknown', disk_bus => $disk_bus, id_vendor => $id_vendor }; } return $udev_info; } sub convert_to_mb { my $self = shift; my $opts = shift; my $size_mb = 0; chomp($opts->{unit}) if ($opts->{unit}); chomp($opts->{value}) if ($opts->{value}); $opts->{unit} = $self->strip_whitespace({ string => $opts->{unit} }) if ($opts->{unit}); $opts->{value} = $self->strip_whitespace({ string => $opts->{value} }); if ( $opts->{value} =~ /MB/i ) { if ( $opts->{value} =~ /(\d+)/i ) { return $1; } else { $self->logger({ cat => 'c', msg => "convert_to_mb() got invalid value [$opts->{value}]" }); } } if ( $opts->{unit} =~ /kb|kib/i ) { $size_mb = $opts->{value} / 1024; } elsif ( $opts->{unit} =~ /mb|mib/i ) { $size_mb = $opts->{value}; } elsif ( $opts->{unit} =~ /gb|gib/i ) { $size_mb = $opts->{value} * 1024; } elsif ( $opts->{unit} =~ /tb|tib/i ) { $size_mb = $opts->{value} * 1024 * 1024; } elsif ( $opts->{unit} =~ /block/i ) { my $block_size = $opts->{block_size} // 512; $size_mb = $opts->{value} * $block_size / 1024 / 1024; } elsif ( $opts->{unit} =~ /b|bytes/i ) { $size_mb = $opts->{value} / 1024 / 1024; } else { $self->logger({ cat => 'w', msg => "convert_to_mb() unsupported unit [$opts->{unit}]" }); } return $size_mb; } sub map_file_exists { my $self = shift; my $opts = shift; unless ( $opts->{device} ) { $self->logger({ cat => 'c', msg => "map_file_exists() not given device!" }); } if ( -e "$Raider::Base::base_conf{'data_path'}/$opts->{device}.info" ) { return 1; } else { return 0; } } sub find_ld_block_device { my $self = shift; my $opts = shift; # This currently only applies to megaraidSAS and Adaptec modules. This is # not needed for 3ware, because on a 3ware LD's block device the SMART info # from smartctl -i actually tells us what LD# the block device belongs to. # --ssullivan May 28th, 2013 if ( ! defined($opts->{icmd}) || ! defined($opts->{controller}) || ! defined($opts->{controllers}) || ! defined($opts->{lds_array}) || ! defined($opts->{ld_num}) || ! defined($opts->{device}) ) { $self->logger({ cat => 'c', msg => "find_ld_block_device() Missing required arguments!" }); } my $block_device = 'unknown'; # Get block device list. my $disc_block_devices = $self->get_block_devices(); my @controller_ld_block_devices = (); # Find all LD's block devices that belong to controller. for my $each_block_device ( @{ $disc_block_devices } ) { # Get vendor from /sys/block now in case we need it later. my $sys_block_vendor; my $sys_block_vendor_file = "/sys/block/$each_block_device/device/vendor"; if ( -e $sys_block_vendor_file ) { $sys_block_vendor = do { local $/ = undef; open my $fh, "<", $sys_block_vendor_file or $self->logger({ cat => 'c', msg => "Unable to open [$sys_block_vendor_file]: $!" }); <$fh>; }; } if ( $opts->{device} eq 'adaptec' ) { for my $line ( split /^/, `smartctl -i /dev/$each_block_device` ) { # Adaptec 7000 series controllers don't populate Adaptec as the # vendor. Instead, they populate this field with the model. The sample # model I saw was 'ASR7160', so i'm using ASR regex to attempt # (admittedly hackishly) to catch more Adaptec controllers. if ( $line =~ /(Device|Vendor)\s*:\s+(Adaptec|ASR\S+)/i ) { # We found an Adaptec LD block device; add it. push(@controller_ld_block_devices, $each_block_device); last; } elsif ( $sys_block_vendor && $sys_block_vendor =~ /^Adaptec|^ASR/i ) { push(@controller_ld_block_devices, $each_block_device); last; } } } elsif ( $opts->{device} eq 'megaraidsas' ) { for my $line ( split /^/, `smartctl -i /dev/$each_block_device` ) { if ( $line =~ /(Device|Vendor)\s*:\s+LSI|megaraid|AVAGO/i ) { # We found an LSI LD block device; add it. push(@controller_ld_block_devices, $each_block_device); last; } elsif ( $sys_block_vendor && $sys_block_vendor =~ /^LSI/i ) { push(@controller_ld_block_devices, $each_block_device); last; } } } else { $self->logger({ cat => 'c', msg => "find_ld_block_device() Don't know what to do with device [$opts->{device}] !" }); } } # Filter out any LD's that do not belong to the given controller. my $filtered_ld_block_devices_ref = $self->filter_ld_block_devices_by_controller({ ld_block_devices => \@controller_ld_block_devices, controller => $opts->{controller}, controllers => $opts->{controllers} }); my @filtered_ld_block_devices = @{ $filtered_ld_block_devices_ref }; my $lds_present = @filtered_ld_block_devices; if ( $lds_present == 1 ) { # One LD with a block device, so no need for further examination. $block_device = "/dev/$filtered_ld_block_devices[0]"; $self->logger({ cat => 'i', msg => "Matched [$block_device] to LD [$opts->{ld_num}] on controller [$opts->{controller}]; only LD present with a block device." }); } else { # Two or more LD's with a block device found. my $ld_size_match = 0; my $numwrites_match = 0; my $pci_pri_match = 0; #################### ## "Size match" ## #################### # Get the size of $block_device , and each LD # on controller. If sizes are different, match based on size. If not, proceed to "numWrites". # Our + || - range to match. my $size_match_mb_range = '100'; my %ld_sizes = (); my $match_comp = 0; if ( $opts->{device} eq 'adaptec' ) { for my $each_ld ( @{ $opts->{lds_array} } ) { for my $line ( split /^/, `$opts->{icmd} getconfig $opts->{controller} LD $each_ld` ) { if ( $line =~ /^\s+Size(\s+)?:\s+(\d+)\s+(\S+)/i ) { $ld_sizes{ $each_ld } = $self->convert_to_mb({ unit => $3, value => $2 }); $match_comp = $ld_sizes{ $each_ld }; } } } } elsif ( $opts->{device} eq 'megaraidsas' ) { for my $each_ld ( @{ $opts->{lds_array} } ) { for my $line ( split /^/, `$opts->{icmd} -LDInfo -L$each_ld -a$opts->{controller}` ) { if ( $line =~ /^Size(\s+)?:\s+(\S+)\s+(\S+)/i ) { $ld_sizes{ $each_ld } = $self->convert_to_mb({ unit => $3, value => $2 }); $match_comp = $ld_sizes{ $each_ld }; } } } } # Get number of same values in hash. my $num_matches_values_ld_sizes = grep {$_==$match_comp} values %ld_sizes; # Get number of keys in hash. my $num_keys_ld_sizes = keys(%ld_sizes); # If same values in hash == number of keys in hash, all LD sizes are the same. Bail! if ( $num_keys_ld_sizes == $num_matches_values_ld_sizes ) { $self->logger({ cat => 'i', msg => "Two or more same size LD's detected. Moving on to numWrites algorithm..." }); } else { # Two or more same size LD's. Match on size. for my $each_ld ( @{ $opts->{lds_array} } ) { last if ( $ld_size_match ); # Define high and low range. my $high_end = $ld_sizes{ $each_ld } + $size_match_mb_range; my $low_end = $ld_sizes{ $each_ld } - $size_match_mb_range; for my $controller_ld_block_device ( @filtered_ld_block_devices ) { # Get size of block device as reported by OS. my $os_block_device_size_file = "/sys/block/$controller_ld_block_device/size"; if ( ! -e $os_block_device_size_file ) { $self->logger({ cat => 'c', msg => "find_ld_block_device() device size file [$os_block_device_size_file] does not exist!" }); } my $block_device_size_os_level = 0; my $block_device_size_os_level_MB = 0; local $/ = undef; open FILE, $os_block_device_size_file || $self->logger({ cat => 'c', msg => "Couldn't open file [$os_block_device_size_file]: $!"}); $block_device_size_os_level = <FILE>; close FILE; # Get size in MB. $block_device_size_os_level_MB = $self->convert_to_mb({ unit => 'block', value => $block_device_size_os_level }); # If OS reported block device is within range of vendor tool reported size.. if ( grep {$_ == $ld_sizes{ $opts->{ld_num} }} $low_end..$high_end ) { # If OS reported block device size (in MB) is within the allowed range of the vendor tool reported size (in MB), it's a match. if ( grep {$_ == $block_device_size_os_level_MB} $low_end..$high_end ) { # Found our match, update block_device. $block_device = $controller_ld_block_device; $ld_size_match = 1; last; } } } } } if ( $ld_size_match ) { $self->logger({ cat => 'i', msg => "Matched [$block_device] to LD [$opts->{ld_num}] on controller [$opts->{controller}] via LD Size." }); return $block_device; } #################### ## "numWrites" ## #################### # Check /sys/block/$block_device/stat , field 5. If it matches numwrites for the LD in the vendor tool, match based on this. # Our + || - match range. my $numwrites_range = '10'; my %lds_numwrites = (); if ( $opts->{device} eq 'adaptec' ) { for my $each_ld ( @{ $opts->{lds_array} } ) { my $is_ld_status = 0; my $is_ld_status_correct_ld = 0; for my $line ( split /^/, `$opts->{icmd} getlogs $opts->{controller} stats tabular` ) { if ( $line =~ /^\s+logicaldrivestats/i ) { $is_ld_status = 1; } elsif ( $line =~ /^\s+interCmdTime/i ) { $is_ld_status = 0; $is_ld_status_correct_ld = 0; } if ( $is_ld_status ) { if ( $line =~ /^\s+id\s+\S+\s+(\d+)/i ) { my $disc_ld_writes = $1; if ( $disc_ld_writes == $each_ld ) { $is_ld_status_correct_ld = 1; } } } if ( $is_ld_status_correct_ld ) { if ( $line =~ /^\s+numWrites\s+\S+\s+(\d+)/i ) { $lds_numwrites{ $each_ld } = $1; } } } } } elsif ( $opts->{device} eq 'megaraidsas' ) { # Unfortunately, MegaCLI doesn't have any machanism for showing numwrites on a LD. } # Determine which controller block_device is within our numWrites range. for my $each_ld ( @{ $opts->{lds_array} } ) { last if ( $opts->{device} eq 'megaraidsas' || $numwrites_match ); # Define high and low range. my $high_end = $lds_numwrites{ $each_ld } + $numwrites_range; my $low_end = $lds_numwrites{ $each_ld } - $numwrites_range; for my $controller_ld_block_device ( @filtered_ld_block_devices ) { # Get numwrites as reported by OS. my $os_block_device_numwrites_file = "/sys/block/$controller_ld_block_device/stat"; my $os_block_device_numwrites_raw; my $os_block_device_numwrites = 0; if ( ! -e $os_block_device_numwrites_file ) { $self->logger({ cat => 'c', msg => "find_ld_block_device() stat file [$os_block_device_numwrites_file] doesn't exist !" }); } local $/ = undef; open FILE, $os_block_device_numwrites_file || $self->logger({ cat => 'c', msg => "Couldn't open file [$os_block_device_numwrites_file]: $!"}); $os_block_device_numwrites_raw = <FILE>; close FILE; if ( $os_block_device_numwrites_raw =~ /^\s+\d+\s+\d+\s+\d+\s+\d+\s+(\d+)/ ) { $os_block_device_numwrites = $1; } # See if numwrites as reported by OS is within range of vendor tool reported numwrites. if ( $each_ld == $opts->{ld_num} ) { if ( grep {$_ == $os_block_device_numwrites} $low_end..$high_end ) { # Found our match, update block_device. $block_device = $controller_ld_block_device; $numwrites_match = 1; last; } } } } if ( $numwrites_match ) { $self->logger({ cat => 'i', msg => "Matched [$block_device] to LD [$opts->{ld_num}] on controller [$opts->{controller}] via numWrites." }); return $block_device; } #################### ## PCI "Priority" ## #################### # Get the pci bus, channel, and location of all block devices that are of the same type as the controller. Aggregate those into an integer ("priority"), then # compare the LD index to the "priority". The one with the lowest numeric "priority" is the lowest numbered LD etc. my %pci_priority_map = (); for my $each_ld_block_device ( @filtered_ld_block_devices ) { # Get and assign PCI "priority" of block device. $pci_priority_map{ $each_ld_block_device } = $self->get_pci_priority({ block_device => $each_ld_block_device }); } # Sort pci priorities from lowest to highest. my @sorted_block_devices = (); foreach my $sorted_block_device ( sort {$pci_priority_map{$a} <=> $pci_priority_map{$b}} keys %pci_priority_map ) { push(@sorted_block_devices, $sorted_block_device); } if ( defined($sorted_block_devices[$opts->{ld_num}]) ) { $block_device = "/dev/$sorted_block_devices[$opts->{ld_num}]"; $pci_pri_match = 1; } else { $self->logger({ cat => 'c', msg => "find_ld_block_device() Can't determine block device for ld_num [$opts->{ld_num}] !" }); } if ( $pci_pri_match ) { $self->logger({ cat => 'i', msg => "Matched [$block_device] to LD [$opts->{ld_num}] on controller [$opts->{controller}] via PCI Priority." }); return $block_device; } if ( ! $ld_size_match && ! $numwrites_match && ! $pci_pri_match ) { $self->logger({ cat => 'c', msg => "Failed to find block device for LD [$opts->{ld_num}] with ld size match, numwrites, and PCI priority !" }); } } return $block_device; } sub get_pci_priority { my $self = shift; my $opts = shift; if ( ! $opts->{block_device} ) { $self->logger({ cat => 'c', msg => "get_pci_priority() not given block_device!" }); } my $pci_priority = 0; # Get all block devices. my $all_bdev_ctrls = $self->get_sys_ctrls_with_bdevs(); for my $device_path ( keys %{ $all_bdev_ctrls } ) { if ( $all_bdev_ctrls->{ $device_path } =~ /$opts->{block_device}/i ) { my $pci_dev_str = $device_path; # Remove non-numbers. $pci_dev_str =~ s/[^\d+]//g; # Add all numbers up, to determine pci_priority. while ( $pci_dev_str =~ /(.)/g ) { $pci_priority = $pci_priority + $1; } } } return $pci_priority; } sub filter_ld_block_devices_by_controller { my $self = shift; my $opts = shift; if ( ! defined($opts->{controller}) || ! defined($opts->{controllers}) || ! defined($opts->{ld_block_devices}) ) { $self->logger({ cat => 'c', msg => "filter_ld_block_devices_by_controller() missing required args!" }); } my $filtered_ld_block_devices = 0; my $lds_by_controller = {}; # Get all block devices. my $all_bdev_ctrls = $self->get_sys_ctrls_with_bdevs(); for my $each_block_device ( @{ $opts->{ld_block_devices} } ) { # Find the entry that corresponds with each_block_device for my $device_path ( keys %{ $all_bdev_ctrls } ) { if ( $all_bdev_ctrls->{ $device_path } =~ /$each_block_device/i ) { # Example, of the below, extracting [0000:00:02.0/0000:02:00.0/host0/target0] # /sys/devices/pci0000:00/0000:00:02.0/0000:02:00.0/host0/target0:0:2/0:0:2:0/block # /sys/devices/pci0000:00/0000:00:02.2/0000:04:00.0/host0/target0:2:0/0:2:0:0/block # /sys/devices/pci0000:00/0000:00:03.0/0000:05:00.0/host1/target1:2:0/1:2:0:0/block # /sys/devices/pci0000:00/0000:00:02.2/0000:04:00.0/host0/target0:2:1/0:2:1:0/block # /sys/devices/pci0000:3a/0000:3a:00.0/0000:3b:00.0/host0/target0:2:0/0:2:0:0/block if ( $device_path =~ /\/sys\/devices\/pci\d+:\S+?\/(\S+\/host\d+\/target\d+):/i ) { my $pci_host_addr = $1; $pci_host_addr =~ s/\D//g; my $score = 0; # Add all numbers up while ( $pci_host_addr =~ /(.)/g ) { $score = $score + $1; } push(@{ $lds_by_controller->{ $score } }, $each_block_device); } else { $self->logger({ cat => 'c', msg => "device_path [$device_path] unknown format!" }); } } } } # Some controllers, like Adaptecs, number controllers starting at 1, not 0, # like MegaraidSAS controllers do. Account for this by checking if the # lowest numbered controller is 1 or 0. my @sorted_controllers = sort { $a <=> $b } @{ $opts->{controllers} }; my $accessor; if ( $sorted_controllers[0] == 0 ) { $accessor = $opts->{controller}; } elsif ( $sorted_controllers[0] == 1 ) { $accessor = $opts->{controller} - 1; $self->logger({ cat => 'i', msg => "Lowest controller numbered 1, decremented passed controller to compensate." }); } else { $self->logger({ cat => 'c', msg => "Lowest numbered controller doesn't start with 0 or 1; I don't know how to handle this!" }); } # Now that our hashref is keyed up by PCI priority, we can select its block # devices by controller number. $filtered_ld_block_devices = $lds_by_controller->{ ( sort {$a <=> $b} keys %{ $lds_by_controller } )[$accessor] }; if ( ! $filtered_ld_block_devices ) { $self->logger({ cat => 'c', msg => "filter_ld_block_devices_by_controller() Cannot find block devices for controller [$opts->{controller}] !" }); } return $filtered_ld_block_devices; } sub isPvopsXenDom0 { my $self = shift; return 0 unless (-e '/sys/hypervisor/type'); open FILE, "</sys/hypervisor/type" or $self->logger({cat => 'c', msg => "Error opening /sys/hypervisor/type: $!"}); my $hypervisor_type = do { local $/; <FILE> }; close(FILE) or $self->logger({cat => 'c', msg => "Error closing /sys/hypervisor/type: $!"}); return 0 if ($hypervisor_type !~ /xen/); open FILE, "</sys/hypervisor/uuid" or $self->logger({cat => 'c', msg => "Error opening /sys/hypervisor/uuid: $!"}); my $hyp_uuid = do { local $/; <FILE> }; close(FILE) or $self->logger({cat => 'c', msg => "Error closing /sys/hypervisor/uuid: $!"}); # uuid is always 00000000-0000-0000-0000-000000000000 if its a dom0 return 0 if ($hyp_uuid !~ /00000000-0000-0000-0000-000000000000/); if (`modinfo xen_blkback 2>/dev/null` =~ /\/lib\/modules\//) { $self->logger({cat => 'i', msg => "Xen pvops based dom0 detected, xen_blkback is loaded"}); return 1; } return 0; } sub get_sys_ctrls_with_bdevs { my $self = shift; my $opts = shift; # pvops Xen dom0s do not populate a block file in /sys/devices.. if ($self->isPvopsXenDom0) { # fudging data for now, since only use case of this is legacy xen zone and we dont need # sonar.info to be strictly correct about this part on those. my $fudged_data; my $fudged_template_pci = '/sys/devices/pci0000:00/0000:00:1c.0/0000:05:00.0/host0/target0:0:0/0:0:0:INCREMENT/block'; my $disc_block_devices = $self->get_block_devices(); my $device_cnt = 0; for my $block_device (@{$disc_block_devices}) { (my $my_pci_addr = $fudged_template_pci) =~ s/INCREMENT/$device_cnt/g; $fudged_data->{ $my_pci_addr } = $block_device; $device_cnt++; } return $fudged_data; } unless ( -d '/sys/devices' ) { $self->logger({ cat => 'c', msg => "/sys/devices/ does not exist, cannot proceed!" }); } # Xen (on Cent5 at least) shows devices like this: # /sys/devices/pci0000:00/0000:00:1c.0/0000:05:00.0/host0/target0:0:0/0:0:0:0/block:sda # Where a non-Xen does this: # /sys/devices/pci0000:00/0000:00:1c.0/0000:05:00.0/host0/target0:0:0/0:0:0:0/block # Thus we get to look for both. our @all_bdev_ctrls = (); sub wanted_xen { /^block:.*\z/s && push(@all_bdev_ctrls, $name); } sub wanted { /^block\z/s && push(@all_bdev_ctrls, $name); } File::Find::find({ wanted => \&wanted_xen }, '/sys/devices/' ); File::Find::find({ wanted => \&wanted }, '/sys/devices/' ); my $data; for my $device_path ( @all_bdev_ctrls ) { if ( $device_path =~ /\/sys\/devices\/\S+\d+:\S+\/\d+:(\S+:\S+\.\d+)/ ) { # On older distros like CentOS4 it be like this: # /sys/devices/pci0000:00/0000:00:1f.2/host1/target1:0:0/1:0:0:0/block -> ../../../../../../block/sda # On more modern ones it will be like this: # root@host # ls -l /sys/devices/pci0000:00/0000:00:1f.2/host2/target2:0:0/2:0:0:0/block # drwxr-xr-x 10 root root 0 Sep 10 13:30 sda # root@host # if ( -l $device_path ) { # [root@host ~]# readlink -f /sys/devices/pci0000:00/0000:00:1f.2/host1/target1:0:0/1:0:0:0/block # /sys/block/sda # [root@host ~]# my $readlinked = `readlink -f $device_path`; if ( $readlinked =~ /\/sys\/block\/(\S+)/i ) { $data->{ $device_path } = $1; } else { $self->logger({ cat => 'c', msg => "Device [$device_path] readlinks into unknown format!" }); } } elsif ( -d $device_path ) { my @device_path_entries = glob("$device_path/*"); if ( defined($device_path_entries[0]) ) { unless ( -e $device_path_entries[0] ) { $self->logger({ cat => 'c', msg => "Missing [$device_path_entries[0]]" }); } my($dev_short) = $device_path_entries[0] =~ /.*\/(.*)/; my $device_tmp = "/dev/$dev_short"; unless ( -e $device_tmp ) { $self->logger({ cat => 'c', msg => "Extracted device [$device_tmp] non-existent!" }); } $data->{ $device_path } = $dev_short; } else { $self->logger({ cat => 'c', msg => "No entries for [$device_path]!" }); } } else { $self->logger({ cat => 'c', msg => "Device [$device_path] is of unknown type!" }); } } else { $self->logger({ cat => 'i', msg => "Skipping non-physical device [$device_path]" }); } } return $data; } sub find_suitable_block_device { my $self = shift; my $opts = shift; if ( ! defined($opts->{controller}) || ! defined($opts->{controllers}) || ! defined($opts->{candidate_block_devs}) ) { $self->logger({ cat => 'c', msg => "find_suitable_block_device() Missing required args!" }); } my $suitable_block_device; my @block_device_candidates = @{ $opts->{candidate_block_devs} }; # Determine if any LDs are on different controllers. my %block_devices_by_pcihost = (); my $pcihost_id = 0; my $all_bdev_ctrls = $self->get_sys_ctrls_with_bdevs(); for my $each_cand_block_device ( @block_device_candidates ) { if ( $each_cand_block_device =~ /\/dev\/(\S+)/ ) { $each_cand_block_device = $1; } for my $device_path ( keys %{ $all_bdev_ctrls } ) { if ( $all_bdev_ctrls->{ $device_path } =~ /$each_cand_block_device/i ) { # /sys/devices/pci0000:00/0000:00:1f.2/host2/target2:0:0/2:0:0:0/block # /sys/devices/pci0000:00/0000:00:02.0/0000:02:00.0/host0/target0:0:2/0:0:2:0/block if ( $device_path =~ /\/sys\/devices\/pci\d+:\d+\/(\S+\/host\d+\/target\d+):/i ) { my $pcihost = $1; $pcihost =~ s/\D//g; # Add pcihost numbers up. my @chars = $pcihost =~ /./sg; $pcihost_id = 0; for my $num ( @chars ) { $pcihost_id = $pcihost_id + $num; } $block_devices_by_pcihost{ $pcihost_id } = $each_cand_block_device; } } } } # If there is more than one key, there is more than one controller. my $key_count = keys %block_devices_by_pcihost; if ( $key_count == 1 ) { $suitable_block_device = $block_devices_by_pcihost{ $pcihost_id }; } else { # The lowest numbered key is the lowest numbered controller. # Sort our controllers array from lowest to highest. my @low_to_high_cntrls = sort { $a <=> $b } @{ $opts->{controllers} }; my $passed_controller_score = 0; for my $sorted_cntrl ( @low_to_high_cntrls ) { $passed_controller_score++; last if ( $sorted_cntrl == $opts->{controller} ); } my $loop_pass_sorted_devs_pcihost = 1; for my $key ( sort { $a <=> $b } keys %block_devices_by_pcihost ) { if ( $loop_pass_sorted_devs_pcihost == $passed_controller_score ) { $suitable_block_device = $block_devices_by_pcihost{ $key }; } $loop_pass_sorted_devs_pcihost++; } unless ( defined($suitable_block_device) ) { $self->logger({ cat => 'c', msg => "find_suitable_block_device() Unable to determine suitable block device for controller [$opts->{controller}]." }); } } return $suitable_block_device; } sub get_device_id { my $self = shift; my $opts = shift; my @x = split (/\//, $opts->{device_path}); if ( $x[4] =~ /^\d+:(\S+)/ ) { return $1; } else { $self->logger({ cat => 'c', msg => "Unable to determine device ID from device path [$opts->{device_path}]" }); } } 1;
Copyright ©2k19 -
Hexid
|
Tex7ure