/
usr
/
lib
/
raider
/
Raider
/
Info
/
File Upload :
llllll
Current File: //usr/lib/raider/Raider/Info/satascsiata.pm
use strict; use warnings; package Raider::Info::satascsiata; use base qw( Raider::Info ); use Raider::Notification::Email; use Cwd (); use JSON::Tiny qw(decode_json encode_json); =head1 NAME Raider::Info::satascsiata - satascsiata specific instructions for get-info =head1 DESCRIPTION satascsiata specific methods to gather information required for populating the .info file. =head1 USAGE use Raider::Info::satascsiata; my $noraidInfoObj = Raider::Info::satascsiata->new(); =head1 METHODS =head2 get_info() Initiates all needed steps to generate a finished satascsiata info file. =head2 get_pci_id(\%args) Returns the PCI ID of device. =head2 get_disk_info(\%args) Returns the info of a disk. =head2 get_disks() Return the disk devices we are working with. =head2 get_satascsiata() Return non raided disks. =head2 is_hdd(\%args) Returns 1 if device is a physical disk, 0 if not. =head2 get_non_raid_devices() Return non raided ID's. =cut sub get_info { my $self = shift; my $opts = shift; my $found_numdrives_arr = $self->get_satascsiata(); my $found_numdrives = scalar(@{ $found_numdrives_arr }); my ($disks_struct,$unidentifiable_disks,$unidentifiable_disks_usb) = $self->get_disks(); my $d_struct_usb_storage = { }; # Onboard controller identification. my $onboard_controller_id; my $id_int_key; my @onboard_controller_ids_raw = (); for my $line ( split /^/, `lspci -n` ) { # IDE/SATA AHCI if ( $line =~ /^\S+\s+0101:\s+(\S+)/ || $line =~ /^\S+\s+Class\s+0101:\s+(\S+)/i ) { $id_int_key = $1; push(@onboard_controller_ids_raw, $id_int_key); } # SATA elsif ( $line =~ /^\S+\s+0106:\s+(\S+)/ || $line =~ /^\S+\s+Class\s+0106:\s+(\S+)/i) { $id_int_key = $1; push(@onboard_controller_ids_raw, $id_int_key); } } # Remove duplicates my %seen = (); my @onboard_controller_ids = grep { ! $seen{$_} ++ } @onboard_controller_ids_raw; my $d_struct_controllers; for my $onboard_cntrl_id ( @onboard_controller_ids ) { $d_struct_controllers->{ $opts->{cntrl_cnt} } = { identifier => "Onboard:::$onboard_cntrl_id", logical_disks => {} }; $onboard_controller_id = $opts->{cntrl_cnt}; $opts->{cntrl_cnt}++; } my $d_struct_disks = { }; my $storm_d_struct = { controllers => { }, }; $storm_d_struct->{'controllers'}->{ '0' } = { disks => { } }; $storm_d_struct->{'controllers'}->{'0'}->{'numarrays'} = '0'; $storm_d_struct->{'controllers'}->{'0'}->{'numdrives'} = $found_numdrives; $storm_d_struct->{'controllers'}->{'0'}->{'make'} = 'N/A'; $storm_d_struct->{'controllers'}->{'0'}->{'model'} = 'N/A'; $storm_d_struct->{'controllers'}->{'0'}->{'firmware'} = 'N/A'; my @usb_disks = (); my @onboard_controller_disks = (); for my $disk ( @{ $disks_struct->{disk_cnt} } ) { my $disk_id; if ( defined("$disks_struct->{disk_info}->{ $disk }->{model}\:::$disks_struct->{disk_info}->{ $disk }->{serial}") ) { $disk_id = "$disks_struct->{disk_info}->{ $disk }->{model}\:::$disks_struct->{disk_info}->{ $disk }->{serial}"; } if ( $disks_struct->{disk_info}->{ $disk }->{udev_id_bus} =~ /usb/i ) { push(@usb_disks, $disk_id); } else { push(@onboard_controller_disks, $disk_id); } if ( defined($disk_id) ) { $d_struct_disks->{ $opts->{disk_cnt} } = { identifier => $disk_id, size_mb => $disks_struct->{disk_info}->{ $disk }->{size_mb}, size_unparsed => $disks_struct->{disk_info}->{ $disk }->{size_unparsed}, serial => $disks_struct->{disk_info}->{ $disk }->{serial}, model => $disks_struct->{disk_info}->{ $disk }->{model}, type => $disks_struct->{disk_info}->{ $disk }->{disk_type}, firmware => $disks_struct->{disk_info}->{ $disk }->{firmware}, state => $disks_struct->{disk_info}->{ $disk }->{state}, block_device => $disks_struct->{disk_info}->{ $disk }->{block_device}, udev_id_bus => $disks_struct->{disk_info}->{ $disk }->{udev_id_bus} }; if ( defined($disks_struct->{disk_info}->{ $disk }->{smart_attributes}) ) { $d_struct_disks->{ $opts->{disk_cnt} }->{'smart_attributes'} = $disks_struct->{disk_info}->{ $disk }->{smart_attributes}; } $opts->{disk_cnt}++; } $storm_d_struct->{'controllers'}->{'0'}->{'disks'}->{ $disk }->{'type'} = $disks_struct->{disk_info}->{ $disk }->{disk_type}; }; if ( defined($onboard_controller_id) ) { $d_struct_controllers->{ $onboard_controller_id }->{'unidentifiable_disks'} = $unidentifiable_disks; my $each_onboard_cntrl_pd_count = 0; for my $each_onboard_cntrl_pd ( @onboard_controller_disks ) { my $accessor_key; foreach my $key ( keys %{ $d_struct_disks } ) { if ( $d_struct_disks->{$key}->{identifier} =~ /$each_onboard_cntrl_pd/ ) { $accessor_key = $key; } } $d_struct_controllers->{ $onboard_controller_id }->{'logical_disks'}->{ $each_onboard_cntrl_pd_count }->{'size_mb'} = $d_struct_disks->{ $accessor_key }->{'size_mb'}; $d_struct_controllers->{ $onboard_controller_id }->{'logical_disks'}->{ $each_onboard_cntrl_pd_count }->{'block_device'} = $d_struct_disks->{ $accessor_key }->{'block_device'}; $d_struct_controllers->{ $onboard_controller_id }->{'logical_disks'}->{ $each_onboard_cntrl_pd_count }->{'physical_disks'} = [ $each_onboard_cntrl_pd ]; $each_onboard_cntrl_pd_count++; } } # Increment passed controller counter for onboard USB if needed. if ( defined($d_struct_controllers->{ $opts->{cntrl_cnt} }->{'identifier'}) ) { $opts->{cntrl_cnt}++; } $d_struct_usb_storage->{ $opts->{cntrl_cnt} }->{'identifier'} = 'Onboard:::usb_storage'; $d_struct_usb_storage->{ $opts->{cntrl_cnt} }->{'unidentifiable_disks'} = $unidentifiable_disks_usb; my $each_onboard_usb_pd_count = 0; for my $each_onboard_usb_pd ( @usb_disks ) { my $accessor_key; foreach my $key ( keys %{ $d_struct_disks } ) { if ( $d_struct_disks->{$key}->{identifier} =~ /$each_onboard_usb_pd/ ) { $accessor_key = $key; } } $d_struct_usb_storage->{ $opts->{cntrl_cnt} }->{'logical_disks'}->{ $each_onboard_usb_pd_count }->{'size_mb'} = $d_struct_disks->{ $accessor_key }->{'size_mb'}; $d_struct_usb_storage->{ $opts->{cntrl_cnt} }->{'logical_disks'}->{ $each_onboard_usb_pd_count }->{'block_device'} = $d_struct_disks->{ $accessor_key }->{'block_device'}; $d_struct_usb_storage->{ $opts->{cntrl_cnt} }->{'logical_disks'}->{ $each_onboard_usb_pd_count }->{'physical_disks'} = [ $each_onboard_usb_pd ]; $each_onboard_usb_pd_count++; } $self->write_json({data => $storm_d_struct, device => 'satascsiata' }); return ($d_struct_usb_storage,$d_struct_controllers,$d_struct_disks,$opts->{cntrl_cnt},$opts->{disk_cnt}); } sub get_pci_id { my $self = shift; my $opts = shift; my $pci_id; my $is_ide_short_circuit = 0; my $all_bdev_ctrls = $self->get_nonraid_devs_to_path(); while ( my($device, $device_path) = each %{ $all_bdev_ctrls } ) { if ( $device =~ /$opts->{device}/ ) { if ( $device_path =~ /ide-/ ) { $is_ide_short_circuit = 1; } else { if ( $device_path =~ /\/sys\/devices\/[A-z]+\d+:\d+\/\d+:(\S+:\S+\.\d+)\// ) { $pci_id = $self->get_device_id({ device_path => $device_path }); } else { $self->logger({ cat => 'c', msg => "Failed to extract pci_id from [$device_path]" }); } } } }; return 'IDE' if ( $is_ide_short_circuit ); my $pci_id_desc; for my $line ( split /^/, `lspci` ) { if ( $pci_id && $line =~ /$pci_id/ ) { $line =~ /$pci_id\s+(.*)/; $pci_id_desc = $1; } } return $pci_id_desc; } sub get_disk_info { my $self = shift; my $opts = shift; my $device_name = substr $opts->{device}, 5, 3; my ($contains,$pci_id); my $size_mb = 0; my $size_unparsed = 'unknown'; my $dtype = 'unknown'; my $model = 'unknown'; my $firmware = 'unknown'; my $serial = 'unknown'; my $state = 'unsupported'; # # disk type ############################################################################################### ## ## SSD ## if ( -e "/sys/block/$device_name/device/vendor" ) { open FILE, "</sys/block/$device_name/device/vendor"; $contains = do { local $/; <FILE> }; close(FILE); if ( $contains =~ /SSD|KINGSTON|INTEL|CRUCIAL/i ) { $dtype = 'SSD'; } } for my $line ( split /^/, `sginfo -z /dev/$device_name` ) { if ( $line =~ /Product:/i ) { if ( $line =~ /^M4-CT|SSD|^CT|^SKC|kingston|^C300-|crucial|intel/i ) { $dtype = 'SSD'; } } } ## ## ZDRIVE ## if ( -e "/sys/block/$device_name/device/vendor" ) { $contains = `grep -q "OCZ" /sys/block/$device_name/device/vendor`; if ( $? == 0 ) { $dtype = 'ZDRIVE'; } } $pci_id = $self->get_pci_id({ device => "$opts->{device}" }); if ( $pci_id =~ /SATA/i ) { $dtype = 'SATA'; } elsif ( $pci_id =~ /SCSI/i ) { $dtype = 'SCSI'; } elsif ( $pci_id =~ /IDE/i ) { $dtype = 'PATA'; } elsif ( $pci_id =~ /SAS/i ) { $dtype = 'SAS'; } my $hdparm_info; if ( $dtype eq 'PATA' ) { $hdparm_info = `hdparm -i $opts->{device}`; } else { $hdparm_info = `hdparm -I $opts->{device}`; } my $smartctl_info = `smartctl -i $opts->{device} -T permissive`; if ( $hdparm_info =~ /Nominal\s+Media\s+Rotation\s+Rate:\s+Solid\s+State\s+Device/i ) { $dtype = 'SSD'; } # # disk size ############################################################################################### my $sectors = 0; my $hdparm_output_geo = `hdparm -g $opts->{device}`; if ( $hdparm_output_geo =~ /geometry\s+=\s+\S+\s+sectors\s+=\s+(\d+)/i ) { $sectors = $1; } if ( $hdparm_output_geo =~ /(geometry\s+=\s+\S+\s+sectors\s+=.+)/i ) { $size_unparsed = $1; } # 512 bytes per sector.. my $in_bytes = $sectors * 512; my $in_kb = $in_bytes / 1024; $size_mb = $in_kb / 1024; # # disk model ############################################################################################### if ( $hdparm_info =~ /Model\s+Number(\s+)?:(.+)/i ) { $model = $2; $model =~ tr/ //ds; } elsif ( $smartctl_info =~ /Device\sModel(\s+)?:(.+)/i ) { $model = $2; $model =~ tr/ //ds; } elsif ( $smartctl_info =~ /Device(\s+)?:(.+)/i ) { $model = $2; $model =~ tr/ //ds; } # # disk firmware ############################################################################################### if ( $hdparm_info =~ /Firmware\s+Revision(\s+)?:(.+)/i ) { $firmware = $2; $firmware =~ tr/ //ds; } elsif ( $smartctl_info =~ /Firmware\s+Version(\s+)?:(.+)/i ) { $firmware = $2; $firmware =~ tr/ //ds; } # # disk serial ############################################################################################### if ( $hdparm_info =~ /Serial\s+Number(\s+)?:(.+)/i ) { $serial = $2; $serial =~ tr/ //ds; } elsif ( $smartctl_info =~ /Serial\s+Number(\s+)?:(.+)/i ) { $serial = $2; $serial =~ tr/ //ds; } # # UDEV ############################################################################################### # Directly querying the disk for model/serial works well when the disk responds. However, since we ideally want to be able to uniquely # identify a disk, and our current identification system used by Sonar relies on $model-$serial identification, query udev db for # model/serial as well. This covers the scenario where a disk may be failed and not responding to requests, but the serial/model still # lives in the udev db. Obviously this isn't perfect , but covers the scenario where a disk fails after the udev db was generated, so that # we can still get the failed disks model/serial. # # -- ssullivan May 15, 2013 my $udev_info; if ( $serial eq 'unknown' || $model eq 'unknown' ) { $udev_info = $self->get_udev_block_device_info({ block_device_name => $device_name }); # When querying disk directly for model-serial; use the udev info only on failure. if ( ref($udev_info) eq 'HASH' && defined($udev_info->{model}) && defined($udev_info->{id_serial}) ) { $model = $udev_info->{model}; $serial = $udev_info->{id_serial}; } } else { $udev_info = $self->get_udev_block_device_info({ block_device_name => $device_name }); } # SMART health status doesn't seem to work with flash drives, so fudge it. if ( $model =~ /Flash_Disk/i ) { $dtype = 'Flash_Disk'; $state = 'Present'; } elsif ( ref($udev_info) eq 'HASH' && $udev_info->{disk_bus} =~ /usb/i ) { $dtype = 'USB_ATA'; } # We don't want the block device of a RAID LD to show up. elsif ( ref($udev_info) eq 'HASH' && $udev_info->{id_vendor} =~ /^lsi|^adaptec|^3ware/i ) { return 'skip'; } # # disk state & SMART attributes ############################################################################################### my $smart_attribs = 'unsupported'; if ( `smartctl -H $opts->{device} -T permissive -s on` =~ /SMART\s+overall-health\s+self-assessment\s+test\s+result(\s+)?:\s+(.+)/i ) { $state = $2; } # SMART attributes if ( ref($udev_info) eq 'HASH' && $udev_info->{disk_bus} =~ /usb/i ) { $smart_attribs = $self->get_smart_attribs({ device => $opts->{device}, bus => 'usb' }); } else { $smart_attribs = $self->get_smart_attribs({ device => $opts->{device} }); } $smart_attribs = 'unsupported' if ( defined($smart_attribs->{error}) ); if ( ref($udev_info) eq 'HASH' ) { return ($dtype,$size_mb,$model,$firmware,$serial,$state,$smart_attribs,$size_unparsed,$udev_info->{disk_bus}); } else { return ($dtype,$size_mb,$model,$firmware,$serial,$state,$smart_attribs,$size_unparsed,'unknown'); } } sub get_disks { my $self = shift; my $found_numdrives_arr = $self->get_satascsiata(); my $dnum = 0; my $struct = { disk_cnt => [], disk_info => {} }; my $unidentifiable_disks = 0; my $unidentifiable_disks_usb = 0; if ( @{ $found_numdrives_arr } ) { for my $each_disk ( @{ $found_numdrives_arr } ) { my ($disk_type,$size_mb,$model,$firmware,$serial,$state,$smart_attribs,$size_unparsed,$udev_id_bus) = $self->get_disk_info({ device => $each_disk }); next if ( $disk_type eq 'skip' ); if ( $model eq 'unknown' || $serial eq 'unknown' ) { if ( $udev_id_bus =~ /usb/i ) { $unidentifiable_disks_usb++; } else { $unidentifiable_disks++; } next; } push(@{ $struct->{disk_cnt} }, $dnum); $struct->{disk_info}->{ $dnum } = { disk_type => $disk_type, size_mb => $size_mb, size_unparsed => $size_unparsed, model => $model, firmware => $firmware, serial => $serial, state => $state, block_device => $each_disk, udev_id_bus => $udev_id_bus }; if ( $smart_attribs ne 'unsupported' ) { $struct->{disk_info}->{ $dnum }->{'smart_attributes'} = $smart_attribs; } $dnum++; } } return ($struct,$unidentifiable_disks,$unidentifiable_disks_usb); } sub get_nonraid_devs_to_path { my $self = shift; my $opts = shift; my $data; my $lspci_output = `lspci`; for our $device ( @{ $self->get_non_raid_devices() } ) { # Filter out FusionMPTSAS2 devices from this. We want them to show up as # attached to their specific controller, not as directly connected through # the motherboard. if ( $lspci_output =~ /$device\s+.+Fusion-MPT\s+SAS-2/i ) { $self->logger({ cat => 'i', msg => "Filtering out [$device] from Info/satascsiata as it's a FusionMPTSAS2 controller" }); next; } my $all_bdev_ctrls = $self->get_sys_ctrls_with_bdevs(); for my $device_path ( keys %{ $all_bdev_ctrls } ) { # If this block device does not belong to our current controller, go to # next loop pass. next unless ( $device_path =~ /$device/ ); $data->{ "/dev/$all_bdev_ctrls->{ $device_path }" } = $device_path; } } return $data; } sub get_satascsiata { my $self = shift; my $opts = shift; my @found_numdrives_arr; my $d_struct = $self->get_nonraid_devs_to_path(); while ( my($device, $device_path) = each %{ $d_struct } ) { if ( $self->is_hdd({ device => $device }) ) { push(@found_numdrives_arr, $device); } } return \@found_numdrives_arr; } sub is_hdd { my $self = shift; my $opts = shift; # Returns: # 0 -- if device is not a physical disk # 1 -- if device is a physical disk my $device_short; if ( $opts->{device} =~ /\/dev\/(\S+)/ ) { $device_short = $1; } else { $self->logger({ cat => 'c', msg => "is_hdd() [$opts->{device}] is of invalid type!" }); } # Filter out CDROM device # Some old hosts, like Cent5s, will sometimes show the CDROM as hda. # Account for this situation. if ( -e '/proc/sys/dev/cdrom/info' ) { open(my $fh, '/proc/sys/dev/cdrom/info' ) or $self->logger({ cat => 'c', msg => "Unable to open [/proc/sys/dev/cdrom/info]: $!" }); while (my $line = <$fh>) { if ( $line =~ /^drive\s+name:\s+(\S+)/ ) { if ( $1 eq $device_short ) { return 0; } } } } return 0 if ( $device_short =~ /^sr[0-9]+/ ); # If the device name is of format fd[0-9]+ throw it out, as assume its a # floppy device. return 0 if ( $device_short =~ /^fd[0-9]+/ ); # No entry in /sys/block ? Reject. return 0 unless ( -d "/sys/block/$device_short" ); # Somehow doesn't exist? Reject. return 0 unless ( -e $opts->{device} ); # If the size of the device is reported as 0, reject. if ( -e "/sys/block/$device_short/size" ) { my $device_size; open(my $fh, "/sys/block/$device_short/size") or $self->logger({ cat => 'c', msg => "Unable to open [/sys/block/$device_short/size]: $!" }); while (my $line = <$fh>) { chomp $line; $device_size = $line; } # 0 size, assume not a block device. Among likely other things, this gets # rid of loop devices from detecting as a physical disk. if ( $device_size == 0 ) { return 0; } } else { # No size? reject. return 0; } # With 2.6.32 (Cent6) and newer, there is a /sys/devices/virtual/block/ # directory. Virtual devices like loop, dm, and md, devices show up in here # (which also show up in /sys/block). If /sys/devices/virtual/block/ exists, # filter out (reject) devices that show up here. if ( -d '/sys/devices/virtual/block' ) { return 0 if ( -e "/sys/devices/virtual/block/$device_short" ); } else { return 0 if ( $device_short =~ /^loop[0-9]+/ ); return 0 if ( $device_short =~ /^md[0-9]+/ ); return 0 if ( $device_short =~ /^dm-[0-9]+/ ); return 0 if ( $device_short =~ /^ram[0-9]+/ ); } # Filter out things we gleam from basic SMART info that give this away as # not a physical disk. for my $line ( split /^/, `smartctl -i $opts->{device}` ) { # Filter out Adaptec RAID LDs if ( $line =~ /(Device|Vendor)\s*:\s+(Adaptec|ASR\S+)/i ) { return 0; } # Filter out LSI RAID LDs elsif ( $line =~ /(Device|Vendor)\s*:\s+LSI|megaraid/i ) { return 0; } # Filter out 3ware RAID LDs elsif ( $line =~ /(Device|Vendor)\s*:\s+3ware/i ) { return 0; } # iSCSI disk elsif ( $line =~ /(Device|Vendor)\s*:\s+IET/i ) { return 0; } # iSCSI disk elsif ( $line =~ /(Product)\s*:\s+VIRTUAL-DISK/i ) { return 0; } } # If we made it this far, assume its a physical disk. return 1; } sub get_non_raid_devices { my $self = shift; my $all_bdev_ctrls = $self->get_sys_ctrls_with_bdevs(); my @nonraid_bdev_ctrls = (); my $lspci_output = `lspci`; # in this path: # /sys/devices/pci0000:00/0000:00:01.1/0000:02:00.0/host0/target0:2:0/0:2:0:0/block # 00:01.1 is the address of the PCI slot: # 00:01.1 PCI bridge: Intel Corporation Xeon E3-1200 v3/4th Gen Core Processor PCI Express x8 Controller (rev 06) # 02:00.0 is the address of the raid controller: # 02:00.0 RAID bus controller: Broadcom / LSI MegaRAID Tri-Mode SAS3516 (rev 01) # thus in these scenarios, we would need to look and see if this block device maps to 02:00.0 # in this path: # /sys/devices/pci0000:00/0000:00:1f.2/ata1/host1/target1:0:0/1:0:0:0/block # 00:1f.2 is the address of a SATA port on the motherboard: # 00:1f.2 SATA controller: Intel Corporation 8 Series/C220 Series Chipset Family 6-port SATA Controller 1 [AHCI mode] (rev 05) # Since its directly attached, there is no address after that. Thus in these scenarios, we need to look # and see if this block device maps to 00:1f.2 for my $bdev_ctrl_path ( keys %{ $all_bdev_ctrls } ) { # given this path /sys/devices/pci0000:00/0000:00:1f.2/host1/target1:0:0/1:0:0:0/block # would try to match on 00:1f.2 my $ctrl_id; if ( $bdev_ctrl_path =~ /\/sys\/devices\/\S+\d+:\d+\/\d+:(\d+:\S+\.\d+)/ ) { # if theres a controller connected to the base address, $1 will look like this: # 00:01.1/0000:02:00.0 # So we check for the presence of /, and treat it as true/false my ($has_ctrl) = $1 =~ /\// ? 1 : 0; unless ($has_ctrl) { $ctrl_id = $self->get_device_id({ device_path => $bdev_ctrl_path });; } } if ($ctrl_id) { for my $line ( split /^/, $lspci_output ) { # If our block device is _not_ raid.. if ( $line =~ /^$ctrl_id/i && $line !~ /RAID/i ) { # Perl 5.8.8 doesn't support the below commented code.. #if ( $ctrl_id ~~ @nonraid_bdev_ctrls ) { my %nonraid_bdev_ctrls_bash = map { $_ => 1 } @nonraid_bdev_ctrls; if ( exists($nonraid_bdev_ctrls_bash{$ctrl_id}) ) { # Dont duplicate it. next; } else { push(@nonraid_bdev_ctrls, $ctrl_id); } } } } else { $self->logger({ cat => 'w', msg => "get_non_raid_devices() Skipping device [$bdev_ctrl_path]" }); } } return \@nonraid_bdev_ctrls; } 1;
Copyright ©2k19 -
Hexid
|
Tex7ure