????

Your IP : 216.73.216.174


Current Path : /lib/raider/Raider/Info/
Upload File :
Current File : //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;