????

Your IP : 216.73.216.152


Current Path : /usr/lib/raider/Raider/Info/
Upload File :
Current File : //usr/lib/raider/Raider/Info/3ware.pm

use strict;
use warnings;

package Raider::Info::3ware;
use base qw( Raider::Info );

use Raider::Jobs::3ware;
use JSON::Tiny qw(decode_json encode_json);

=head1 NAME

Raider::Info::3ware - 3ware specific instructions for get-info

=head1 DESCRIPTION

3ware specific methods to gather information required for populating the .info
file.

=head1 USAGE

use Raider::Info::3ware;
my $info3ware = Raider::Info::3ware->new();

=head1 METHODS

=head2 get_info()

Initiates all needed steps to generate a finished 3ware info file.

=head2 get_disk_info(\%)

Return the info of a disk.

=head2 extract_LD_info(\%)

Return info on a LD.

=head2 get_controller_attribute(\%args)

Return the specified attribute.

=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/local/bin/tw_cli';

sub get_info {
  my $self = shift;
  my $opts = shift;
  my $jobs3ware = Raider::Jobs::3ware->new();
  $jobs3ware->icmd_in_path({ icmd => "$icmd" });
  my $controller_list_ref = $jobs3ware->get_controller_list();
  my @controller_list = @$controller_list_ref;

  my $d_struct_disks = { };
  my $d_struct_controllers = { };
  my $storm_d_struct = { controllers => { }, };

  unless ($self->host_supports_info()) {
    $self->logger({cat => 'w', msg => "this host doesnt support get-info collection for 3ware 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 $controller_num = $controller;
    $controller_num =~ s/\D//g;     # remove non-numeric characters.
    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_numdrives = $self->get_controller_attribute({  controller => "$controller", attrib => 'numdrives' });
    my $cntrl_numarrays = $self->get_controller_attribute({ controller => "$controller", attrib => 'numunits' });
    my $cntrl_serial = $self->get_controller_attribute({ controller => $controller, attrib => 'serial' });
    
    my $cntrl_memory;
    for my $line ( split /^/, `$icmd info $controller diag` ) {
      if ( $line =~ /^###\s+Memory\s+Installed(\s+)?:\s+(.+)/i ) {
        $cntrl_memory = $2;
      }
    }

    $d_struct_controllers->{ $opts->{cntrl_cnt} }->{'identifier'} = "$cntrl_model\:::$cntrl_serial";
    $d_struct_controllers->{ $opts->{cntrl_cnt} }->{'make'} = '3ware';
    $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;
    # Fudge this since none of our legacy 3ware 8000 series cards have BBU...
    $d_struct_controllers->{ $opts->{cntrl_cnt} }->{'bbu_present'} = 0;
    $d_struct_controllers->{ $opts->{cntrl_cnt} }->{'memory'} = $cntrl_memory;

    $storm_d_struct->{'controllers'}->{ $controller_num } = { disks => { } };
    $storm_d_struct->{'controllers'}->{ $controller_num }->{'make'} = '3ware';
    $storm_d_struct->{'controllers'}->{ $controller_num }->{'model'} = $cntrl_model;
    $storm_d_struct->{'controllers'}->{ $controller_num }->{'firmware'} = $cntrl_firmware;
    $storm_d_struct->{'controllers'}->{ $controller_num }->{'numdrives'} = $cntrl_numdrives;
    $storm_d_struct->{'controllers'}->{ $controller_num }->{'numarrays'} = $cntrl_numarrays;

    if ( ! defined($d_struct_map_file_new->{ "$cntrl_model\:::$cntrl_serial" }) ) {
      $d_struct_map_file_new = { "$cntrl_model\:::$cntrl_serial" => {} };
    }

    my $cntrl_info = `$icmd info $controller`;

    my @unit_list = ();
    for my $line ( split /^/, $cntrl_info ) {
      if ( $line =~ /^(u\d+)\s+RAID-\d+/i ) {
        push(@unit_list, $1);
      }
    }

    my @phys_disk_list = ();
    for my $line ( split /^/, $cntrl_info ) {
      if ( $line =~ /^(p\d+)\s+/i ) {
        if ( $line !~ /NOT-PRESENT/i ) {
          push(@phys_disk_list, $1);
        }
      }
    }

    for my $each_unit ( @unit_list ) {
      my $each_unit_stripped = $each_unit;
      $each_unit_stripped =~ s/\D//g;     # remove non-numeric characters.
      my @phys_disks_in_unit = ();
      my $unidentifiable_disks_unit = 0;

      # Get port(s) assigned to $each_unit.
      for my $line ( split /^/, $cntrl_info  ) {
        if ( $line =~ /^(p\d+)\s+\S+\s+$each_unit\s+/i ) {
          my $port = $1;
          my ($serial,$model);
          for my $line ( split /^/, `$icmd /$controller/$port show all` ) {
            if ( $line =~ /^\S+\s+Serial\s+=\s+(\S+)/i ) {
              $serial = $1;    
            }
            # If no serial, it's not identifiable.
            elsif ( $line =~ /^\S+\s+Serial\s+=.+/i ) {
              $unidentifiable_disks_unit++;
            }
            elsif ( $line =~ /^\S+\s+Model\s+=\s+(\S+)/i ) {
              $model = $1;
            }
          }
          if ( defined($model) && defined($serial) ) {
            push(@phys_disks_in_unit, "$model\:::$serial");
          }
        }
      }

      my $health_check = `$icmd info $controller $each_unit`;
      my $state = $jobs3ware->array_is_ok({ health_check => $health_check });
      my ($raid_level,$numdrives,$size,$block_device,$raid_state,$stripe_size,$size_unparsed) = $self->extract_LD_info({ controller => $controller, unit => $each_unit });
      # We don't identify 3ware LD's by serial like we do for LSI or Adaptec because 3ware 8000 series cards (which is all our current 3ware stock) don't support 
      # serial identification for a LD (9000 series appear to FYI). Instead, we just identify by unit number here.
      #   --ssullivan May 6th, 2013
      $d_struct_controllers->{ $opts->{cntrl_cnt} }->{'logical_disks'}->{ $each_unit_stripped } = { 
        identifier => "$cntrl_model\:::$cntrl_serial\:::$each_unit_stripped",
        numdrives => $numdrives,
        ld_num => $each_unit_stripped,
        type => 'RAID',
        stripe_size => $stripe_size,  
        raidlevel => $raid_level, 
        state => $raid_state,
        size_mb => $size,
        size_unparsed => $size_unparsed,
        block_device => $block_device
      };

      # Populate arrays disks..
      $d_struct_controllers->{ $opts->{cntrl_cnt} }->{'logical_disks'}->{ $each_unit_stripped }->{'unidentifiable_disks'} = $unidentifiable_disks_unit;
      $d_struct_controllers->{ $opts->{cntrl_cnt} }->{'logical_disks'}->{ $each_unit_stripped  }->{physical_disks} = \@phys_disks_in_unit;
    
      $storm_d_struct->{'controllers'}->{ $controller_num }->{'arrays'}->{ $each_unit_stripped } = { numdrives => $numdrives, raidlevel => $raid_level, state => $state };
    
    };
    my $unidentifiable_disks_cntrl_total_counter = 0;
    for my $each_phys_disk ( @phys_disk_list ) {
      my $disk_num = $each_phys_disk;
      $disk_num =~ s/[A-z]//g;
      my ($disk_type,$size_mb,$port,$model,$firmware,$serial,$state,$smart_attribs,
        $block_device,$device_id,$size_unparsed) = $self->get_disk_info({ controller => $controller, phys_disk => $each_phys_disk });


      if ( $model =~ /^\[No|unknown/i || $serial =~ /^\[No|unknown/i ) {
        $self->logger({ cat => 'i', msg => "PD [$disk_num] not responding to SMART inquiries." });
          
        if ( $self->map_file_exists({ device => '3ware_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'}/3ware_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" }->{ "0\:::$port" }) ) {
              $model = $d_struct_map_file->{ "$cntrl_model\:::$cntrl_serial" }->{ "0\:::$port" }->{model};
              $serial = $d_struct_map_file->{ "$cntrl_model\:::$cntrl_serial" }->{ "0\:::$port" }->{serial};
              if ( defined($d_struct_map_file->{ "$cntrl_model\:::$cntrl_serial" }->{ "0\:::$port" }->{disk_type}) ) {
                $disk_type = $d_struct_map_file->{ "$cntrl_model\:::$cntrl_serial" }->{ "0\:::$port" }->{disk_type};
              }
              $self->logger({ cat => 'i', msg => "3ware: Got model+serial from MAP file." }); 
            }
            else {
              $self->logger({ cat => 'w', msg => "No device entry found in MAP file for [0\:::$port]." });
            }    
          }

        }
        else {
          $self->logger({ cat => 'w', msg => "PD [$disk_num] 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" }{ "0\:::$port" } = {
          model => "$model",
          serial => "$serial",
          disk_type => "$disk_type"
        };
        
        # Our 3ware cards (8000 series) only have single channel, so hard code for now.
        $d_struct_disks->{ $opts->{disk_cnt} } = {
          identifier => "$model\:::$serial",
          type => $disk_type,
          size_mb => $size_mb,
          size_unparsed => $size_unparsed,
          port => $port,
          channel => '0',
          model => $model,
          firmware => $firmware,
          serial => $serial,
          state => $state,
          block_device => $block_device,
          device_id => $device_id
        };
        if ( $smart_attribs ne 'unsupported' ) {
          $d_struct_disks->{ $opts->{disk_cnt} }->{'smart_attributes'} = $smart_attribs;
        }

      }
      else {
        # Increment unidentifiable_disks counter.
        $unidentifiable_disks_cntrl_total_counter++;
      }

      $storm_d_struct->{'controllers'}->{ $controller_num }->{'disks'}->{ $disk_num } = { type => $disk_type };

      # Increment our passed disk counter.
      $opts->{disk_cnt}++;
    };
    # Populate unidentifiable_disks.
    $d_struct_controllers->{ $opts->{cntrl_cnt} }->{'unidentifiable_disks'} = $unidentifiable_disks_cntrl_total_counter;
  
    # Increment our passed controller counter.
    $opts->{cntrl_cnt}++;
  };

  $self->write_json({data => $storm_d_struct, device => '3ware'});

  if ( defined($d_struct_map_file_new) ) {
    $self->write_json({data => $d_struct_map_file_new, device => '3ware_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 $num_drives = 0;
  my $raid_level = 'unknown';
  my $block_device = 'unknown';
  my $raid_state = 'unknown';
  my $stripe_size = 'unknown';
  my $size_unparsed = 'unknown';
  my $size_value = 0;
  my $size_unit;
  my $size_mb = 0;

  for my $line ( split /^/, `$icmd info $opts->{controller} $opts->{unit}` ) {

    if ( $line =~ /^Unit\s+UnitType\s+Status\s+\S+\s+Port\s+Stripe\s+Size\((\S+)\)\s+Blocks/i ) {
      $size_unit = $1;
    }

    if ( $line =~ /^u\d+-\d+\s+DISK/i ) {
      $num_drives++;
    }

    if ( $line =~ /^u\d+\s+RAID-(\d+)\s+(\S+)\s+\S+\s+\S+\s+(\S+)\s+(\S+)/i ) {
      $raid_level = $1;
      $raid_state = $2;
      $stripe_size = $3;
      $size_value = $4;
    }

    if ( $line =~ /^u\d+\s+RAID-\d+\s+\S+\s+\S+\s+\S+\s+\S+\s+(\S+)/i ) {
      $size_unparsed = $1;
      # 3ware doesn't tack unit after value
      $size_unparsed .= " $size_unit";
    }

  }

  $size_mb = $self->convert_to_mb({ unit => $size_unit, value => $size_value });

  # Find block device of our LD
  my $numeric_unit;
  if ( $opts->{unit} =~ /u(\d+)/i ) {
    $numeric_unit = $1;
  }
  my $disc_block_devices_ref = $self->get_block_devices();
  for my $each_block_device ( @{ $disc_block_devices_ref } ) {
    my $smartctl_info_out = `smartctl -i /dev/$each_block_device`;
    if ( $smartctl_info_out =~ /3ware/ && $smartctl_info_out !~ /Device(\s+)?:\s+3ware\s+/ ) {
      $block_device = "/dev/$each_block_device";
    }
    elsif ( $smartctl_info_out =~ /Device(\s+)?:\s+3ware\s+Logical\s+Disk\s+$numeric_unit\s+/i ) {
      $block_device = "/dev/$each_block_device";
    }
  }

  return ($raid_level, $num_drives, $size_mb, $block_device, $raid_state, $stripe_size, $size_unparsed);
}

sub get_disk_info {
  my $self = shift;
  my $opts = shift;
  
  my $dtype = 'unknown';
  my $model = 'unknown';
  my $port = 'unknown';
  my $firmware = 'unknown';
  my $serial = 'unknown';
  my $state = 'unknown';
  my $size_unparsed = 'unknown';
  my $size_value = 0;
  my $size_unit;
  my $size_mb = 0;

  for my $line ( split /^/, `$icmd /$opts->{controller}/$opts->{phys_disk} show all` ) {

    if ( $line =~ /^\S+\s+Model\s+=(.+)/i ) {
      $model = $1;
      $model =~ tr/ //ds;
    }
    if ( $line =~ /^\S+\s+Status\s+=\s+(\S+)/i ) {
      $state = $1;
    }
    if ( $line =~ /^\S+\s+Serial\s+=(.+)/i ) {
      $serial = $1;
      $serial =~ tr/ //ds;
    }
    if ( $line =~ /^\S+\s+Firmware\s+Version\s+=(.+)/i ) {
      $firmware = $1;
      $firmware =~ tr/ //ds;
    }
    if ( $line =~ /^\S+\s+Capacity\s+=\s+(\S+)\s+(\S+)/i ) {
      $size_value = $1;
      $size_unit = $2;
    }
    if ( $line =~ /^\S+\s+Capacity\s+=(.+)/i ) {
      $size_unparsed = $1;
    }
    if ( $line =~ /^\/c\d+\/p(\d+)/i ) {
      $port = $1;
    }

  }

  if ( $model =~ /SSD|INTEL|M4-CT/ ) {
    $dtype = 'SSD';
  }
  else {
    $dtype = 'SATA';
  }

  $size_mb = $self->convert_to_mb({ value => $size_value, unit => $size_unit });

  my $smart_attribs = 'unsupported';
  # /dev/twa0 refers to the first 9000-series controller, /dev/twa1 refers to the second 9000 series controller, and so on. 
  # Likewise /dev/twe0 refers to the first 6/7/8000-series controller, /dev/twa1 refers to the second 6/7/8000 series 
  # controller, and so on.
  my $device_prefix;
  if ( $model =~ /^9\d\d\d/i ) {
    $device_prefix = '/dev/twa';
  }
  else {
    $device_prefix = '/dev/twe';
  }
  # Verify our device exists
  my $controller_numeric;
  if ( $opts->{controller} =~ /\S(\d+)/ ) {
    $controller_numeric = $1;
  }
  my $device_to_query = "$device_prefix$controller_numeric";
  if ( ! -e $device_to_query ) {
    $self->logger({ cat => 'c', msg => "Device to query ($device_to_query) doesn't exist!" });
  }
  my $smartctl_info = `smartctl -i $device_to_query -d 3ware,$port -T permissive`;
  for my $line ( split /^/, $smartctl_info ) {
    if ( $line =~ /Serial\s+number(\s+)?:\s+(\S+)/i ) {
      if ( $2 eq $serial ) {
        $smart_attribs = $self->get_smart_attribs({ 
          device => $device_to_query,
          d_flag => "3ware,$port"
        });
        $smart_attribs = 'unsupported' if ( defined($smart_attribs->{error}) );
      }
    }
  }


  # On 3ware, port is also device id
  my $device_id = $port;

  return ($dtype,$size_mb,$port,$model,$firmware,$serial,$state,$smart_attribs,$device_to_query,$device_id,$size_unparsed);
}

sub get_controller_attribute {
  my $self = shift;
  my $opts = shift;

  my $cntrl_attrib = `$icmd info $opts->{controller} $opts->{attrib}`;
  $cntrl_attrib =~ s/.*=//;
  $cntrl_attrib = $self->strip_whitespace({ string => $cntrl_attrib });

  # If empty, set to error.
  $cntrl_attrib = 'error' if ( $cntrl_attrib !~ /\S/ );

  return $cntrl_attrib;
}

sub host_supports_info {
  my ($self) = @_;

  return 1;
}

1;