package Linktest;
use strict;

use vars qw(%PROMPTS $SUCCESS $FOUND_SUSPECT_FRU $PROBLEM_UNDETECTED $ERROR  $FOUND_FRU $COMPLETED $ABORTED $MCDATA_TEST $YESNO $MULTIPLE_FRUS_FOUND $GUI $Qfile);

$SUCCESS            = 1;
$FOUND_SUSPECT_FRU  = 2;
$MULTIPLE_FRUS_FOUND= 3;
$PROBLEM_UNDETECTED = 99;

# exit status. These align w/the StorADE test structure.
$ERROR              = -1;
$COMPLETED          = 0;
$ABORTED            = 143;
$FOUND_FRU          = 140;

$MCDATA_TEST         = 0;
$YESNO              = "y=Yes|n=No";



%PROMPTS = 
  (
    restore_orignal_components => "\nRestore Orignal Components:\n",
    qlc_auto_loopback => "\nDetected port 0 of a Crystal+ card. This port will automatically go into an internal loopback mode if no connection is present. Due to the port going into automatic loopback mode, Linktest is unable to test this port.\n",
    no_valid_path => "\nNo host path was passed in. This is not a supported path\n",
    multiple_suspects => "\nlinktest could not isolate to a single failing FRU\nTry a single recommended action listed below:\n",
    insert_port_loopback => "Insert FC Loopback Cable into %s: %s, port: %s",
    remove_port_loopback => "Remove FC Loopback Cable from %s: %s, port: %s",
    detected_single_isl => "Detected single ISL (Single Point of Failure) on %s: %s, port: %s",
    ra_detected_single_isl => "Recommended action: Add ISL to zone containing %s: %s, port: %s",
    #detected_single_path_to_sw_ports => "Only one communication path to switch ports. This will prevent linktest from isolating FRUs in the remote %s: %s, port: %s",
    #ra_add_enet_connection => "Recommended action: Add enet connect to %s: %s and redisover and retest",
    #is_enet => "Is there an ethernet communication path to %s: %s, port: %s",
    suspect_switch => "Suspect FC %s: %s",
    ra_suspect_switch => "Recommended action: Replace FC %s: %s",
    suspect_port => "Suspect FC %s: %s, port: %s",
    ra_suspect_port => "Recommended action: reconfigure %s: %s using a port other than port: %s",
    suspect_replaced_gbic => "Suspect REPLACED FC GBIC or SFP in %s: %s, port: %s",
    suspect_controller => "Suspect device controller",
    ra_suspect_replaced_gbic => "Recommended action: Retry linktest with known good FC GBIC or SFP in %s: %s, port: %s",
    functional_port => "FC %s: %s, and port: %s are Functional",
    remove_port_cable => "Remove FC Cable from %s: %s, port: %s",
    replace_port_cable => "Insert a NEW FC Cable into %s: %s, port: %s",
    restore_port_cable => "Restore ORIGINAL FC Cable into %s: %s, port: %s",
    suspect_port_cable => "Suspect ORIGINAL FC Cable from %s: %s, port: %s",
    functional_port_cable => "FC Cable in %s: %s, port: %s is Functional",
    remove_port_gbic => "Remove FC GBIC or SFP from %s: %s, port: %s",

    replace_port_sfp => "Insert a NEW SFP into %s: %s, port: %s",
    remove_port_sfp  => "Remove SFP from %s: %s, port: %s",


    replace_port_gbic => "Insert a NEW FC GBIC or SFP into %s: %s, port: %s",
    restore_device_sfp => "Restore ORIGINAL SFP into %s: %s, port: %s",


    restore_port_gbic => "Restore ORIGINAL FC GBIC or SFP into %s: %s, port: %s",
    restore_device_ori_sfp => "Restore ORIGINAL SFP into %s: %s, port: %s",

    suspect_port_gbic => "Suspect ORIGINAL FC GBIC or SFP in %s: %s, port: %s",
    suspect_device_sfp => "Suspect ORIGINAL SFP in %s: %s, port: %s",

    suspect_inrange_switch => "Suspect Inrange Switch in %s: %s, port: %s.",
    ra_replace_inrange_switch => "Recommended action: Replace Inrange Switch.",



    functional_port_gbic => "FC GBIC or SFP in %s: %s, port: %s is Functional",
    insert_device_loopback => "Insert FC Loopback Cable into %s: %s, port: %s",
    remove_device_loopback => "Remove FC Loopback Cable from %s: %s, port: %s",
    functional_device => "%s: %s, port: %s is Functional",
    remove_device_cable => "Remove FC Cable from %s: %s, port: %s",
    replace_device_cable => "Insert a NEW FC Cable into %s: %s, port: %s",
    restore_device_cable => "Restore ORIGINAL FC Cable into %s: %s, port: %s",
    suspect_SE2_device  => "Suspect Sun StorEdge Solution : %s: %s, port: %s",
    suspect_bad_SIO => "Suspect bad SIO/SRC card in the Sun StorEdge Solution : %s: %s, port: %s",
    ra_verify_SIO => "Recommended action: Replace SIO/SRC pair %s: %s port: %s using SE6920 Service Advisor SIO/SRC replacement procedures",

    ra_run_SE2_diagnostics  => "Recommended action: Run isolation diagnostics on %s: %s",

    suspect_device_cable  => "Suspect ORIGINAL FC Cable from %s: %s, port: %s",
    functional_device_cable => "FC Cable in %s: %s, port: %s is Functional",
    remove_device_gbic => "Remove FC GBIC or SFP from %s: %s, port: %s",
    remove_device_sfp => "Remove SFP from %s: %s, port: %s",
    replace_device_sfp => "Insert a SFP into %s: %s, port: %s",

    replace_device_gbic => "Insert a NEW FC GBIC or SFP into %s: %s, port: %s",
    replace_dev_gbic => "Insert a NEW FC GBIC into %s: %s, port: %s",
    restore_device_gbic => "Restore ORIGINAL FC GBIC or SFP into %s: %s, port: %s",
    restore_dev_gbic => "Restore ORIGINAL FC GBIC into %s: %s, port: %s",
    suspect_device_gbic => "Suspect ORIGINAL FC GBIC or SFP from %s: %s, port: %s",
    suspect_dev_gbic => "Suspect ORIGINAL FC GBIC from %s: %s, port: %s",


    functional_device_gbic => "FC GBIC or SFP in %s: %s, port: %s is Functional",
    remove_device_mia => "Remove MIA from %s: %s, port: %s",
    replace_device_mia => "Insert a NEW MIA into %s: %s, port: %s",
    restore_device_mia => "Restore ORIGINAL MIA into %s: %s, port: %s",
    suspect_device_mia => "Suspect ORIGINAL MIA from %s: %s, port: %s",
    functional_device_mia => "MIA in %s: %s, port: %s is Functional",
    remove_device_coupler => "Remove FC Coupler from %s: %s, port: %s",
    replace_device_coupler => "Insert a NEW FC Coupler into %s: %s, port: %s",
    restore_device_coupler => "Restore ORIGINAL FC Coupler into %s: %s, port: %s",
    suspect_device_coupler => "Suspect ORIGINAL FC Coupler from %s: %s, port: %s",
    functional_device_coupler => "FC Coupler in %s: %s, port: %s is Functional",
    remove_device_adapter => "Remove FC Adapter cable from %s: %s, port: %s",
    replace_device_adapter => "Insert a NEW FC Adapter cable into %s: %s, port: %s",
    restore_device_adapter => "Restore ORIGINAL FC Adapter cable into %s: %s, port: %s",
    suspect_device_adapter => "Suspect ORIGINAL FC Adapter cable from %s: %s, port: %s",
    suspect_device => "Suspect the device %s: %s, port: %s",
    ra_detected_bad_device => "Recommended action: Use device specific isolation tools on %s: %s, port: %s ",

    functional_device_adapter => "FC Adapter cable in %s: %s, port: %s is Functional",
    remove_hba_cable    => "Remove FC Cable from %s: %s",
    replace_hba_cable   => "Insert a NEW FC Cable from %s: %s",
    restore_hba_cable   => "Restore ORIGINAL FC Cable into %s: %s",
    suspect_hba_cable   => "Suspect ORIGINAL FC Cable from %s: %s",
    functional_hba_cable => "FC Cable in %s: %s is Functional",
    remove_hba_gbic     => "Remove FC GBIC or SFP from %s: %s",
    replace_hba_gbic    => "Insert a NEW FC GBIC or SFP into %s: %s",
    restore_hba_gbic    => "Restore ORIGINAL FC GBIC or SFP into %s: %s",
    suspect_hba_gbic    => "Suspect ORIGINAL FC GBIC or SFP from %s: %s",
    functional_hba_gbic => "FC GBIC or SFP in %s: %s is Functional",
    insert_hba_loopback => "Insert FC Loopback Cable into %s: %s",
    remove_hba_loopback => "Remove FC Loopback Cable from %s: %s",
    suspect_hba         => "Suspect %s: %s",
    functional_hba      => "ORIGINAL %s: %s is Functional",
    suspect_a5k_drive   => "A5ktest isolated to drive, Check error messages for specific drive",
    suspect_3310        => "Suspect %s: %s",
    ra_a5ktest          => "Recommended action: execute functional test a5ktest on %s: %s",
    ra_hbatest          => "Recommended action: execute functional HBA test on %s: %s",
    ra_daktest          => "Recommended action: execute functional test daktest on %s: %s",
    ra_fcdisktest      => "Recommended action: execute functional test fcdisktest on %s: %s",
    ra_a3500fctest      => "Recommended action: execute functional test a3500fctest on %s: %s",
    ra_a3500rm6         => "Recommended action: execute rm6 healthcheck on %s: %s",
    ra_fctapetest       => "Recommended action: execute functional test fctapetest on %s: %s",
    ra_3310_tools       => "Recommended action: execute device isolation tools on %s: %s",
    ra_device_tools     => "Recommended action: execute device isolation tools on %s: %s",
    ra_t3_ofdg          => "Recommended action: execute functional test t3ofdg on %s: %s",
    ra_t3test           => "Recommended action: execute functional test t3test on %s: %s",
    ra_switchtest       => "Recommended action: execute functional test switchtest on %s: %s",
    ra_brocadetest      => "Recommended action: execute functional test brocadetest on %s: %s",
    ra_mcdatatest       => "Recommended action: execute functional test mcdatatest on %s: %s",
    ra_switch2test      => "Recommended action: execute functional test switch2test on %s: %s",
    ra_all_patterns     => "Recommended action: select -all- patterns and restart linktest on %s: %s",
    no_password         => "Password required for this device %s: %s",
    ra_password         => "Recommended action: enter password in Devices  >  Device Details > Actions > Monitoring and Setup Information for device %s: %s",
    invalid_password    => "Invalid password for this device %s: %s",
    ra_invalid_password => "Recommended action: verify password in Devices  >  Device Details > Actions > Monitoring and Setup Information for device %s: %s",
    port_offline        => "Port state is -offline-: %s: %s",
    ra_port_offline     => "Recommended action: turn port online with appropriate management s/w for device %s: %s",
    port_admin_offline    => "Port state is -admin offline-: %s: %s",
    ra_port_admin_offline => "Recommended action: turn port online with appropriate management s/w for device %s: %s",
    port_disabled       => "Port state is -disabled-: %s: %s",
    ra_port_disabled    => "Recommended action: enable port online with appropriate management s/w for device %s: %s",
    no_communication    => "Could not communicate with: %s: %s",
    ra_communication    => "Recommended action: verify e-net connection and power or replace device %s: %s",
    no_instrumentation => "Intstrumentation Report required for device %s: %s",
    ra_instrumentation => "Recommended action: Monitor device %s: %s and select -Run Agent- from -System Utilities-",
  );


#################################################
#   PROMPT MODULE
#
#   usage:  prompt('functional_hba', $node_a);
#
#################################################

sub swap {
  my($class, $a, $b) = @_;
  my $temp = $$a;
  $$a = $$b;
  $$b = $temp;
}

#  get_response("the question", "y=yes|n=no");
#  add Scheduler->sync() to this routine (bug 4619207)

sub get_response {
  my ($class, $question, $opt) = @_;
  my $response;

  if ($Linktest::GUI) {
    print $question . " ?\n";
    $response = Scheduler->QandA($Linktest::Qfile, $opt, 60*60); # wait 60 mins max
    $response = "n" if (!defined($response));
    return( $response );
    
  } else {
    $opt =~ s/\|/, /g;
    print "$question [$opt]";
    $response = <STDIN>;
    chop( $response );
    return( $response );
  }
}


sub retest_msg
{
  print( "\n" );
  print( "Retest to verify FRU replacement.\n" );
}

sub ra_exit
{
  print( "\n" );
  print( "Perform a recommended action and restart linktest.\n");
  exit( $ERROR );
}

sub daktari_multiple_frus_msg
{
  print( "\n" );
  print( "V880 Internal disk HBA to Enclosure data path\n" );
  print( "does not have any hot swapable FRUs.\n" );
  print( "linktest could not isolate to a single hot swap FRU.\n" );
  print( "Attempt to isolate using the appropriate HBA Diagnostic." );
  print( "Attempt to isolate using the V880 Disk Diagnostic." );
  print( "Attempt to isolate using the V880 SES Diagnostic." );
  print( "Reference the appropriate Trouble Shooting Guide for Manual FRU Isolation and addititional information.\n" );
  print( "Shutdown host; Replace a component; Boot Host; restart linktest.\n");
}

sub multiple_frus_msg
{
  print( "\n" );
  print( "linktest could not isolate to a single hot swap FRU.\n" );
  print( "Perform a recommended action and restart linktest.\n");
}

sub device_unsupported_msg
{
  print( "Device type unknown or error detecting device.\n" );
  print( "Isolation code TBD: report to developer!\n" );
  print( "Failing FRU not isolated!\n" );
}

sub tbd_msg
{
  print( "Isolation code TBD: report to developer!\n" );
  print( "Failing FRU not isolated!\n" );
}

sub program_error
{
  print( "Program Error: Isolation terminated pre-maturely!\n" );
  print( "Failing FRU not isolated!\n" );
  exit( $ERROR );
}

sub premature_exit
{
  print( "Isolation terminated pre-maturely!\n" );
  print( "Probable cause: User Request\n" );
  print( "Probable cause: Timed out waiting for user input\n" );
  print( "Failing FRU not isolated!\n" );
  exit( $ABORTED );
}

sub skip_io_device
{
  my($class, $device) = @_;

  print( "\nSkipping $device.\n" );
  print( "Required FC loopback test capabilities are not supported by this device.\n" );
}

sub t3_f_port_detected
{
  my($class, $dev_a, $dev_b) = @_;

  print( "linktest does not support testing of $dev_a F port connected to $dev_b.\n" );
  print( "Required FC loopback test capabilities are not supported by the T3 in Fabric mode.\n" );
  print(  "Reference the appropriate Trouble Shooting Guide for Manual FRU Isolation and addititional information.\n" );
  exit( $ERROR );
}

sub brocade_1gb_f_port_detected
{
  my($clas, $dev_a, $dev_b) = @_;

  print( "linktest does not support testing of $dev_a F port connected to $dev_b.\n" );
  print( "Required FC loopback test capabilities are not supported by the 1GB Brocade switch when the port is in Fabric mode.\n" );
}

sub untestable_link
{
  my($class, $dev_a, $dev_b) = @_;

  print( "Test terminated!\n" );
  print( "linktest does not support testing of $dev_a connected to $dev_b.\n" );
  print( "Required FC loopback test capabilities are not supported by one or both devices.\n" );
  print(  "Reference the appropriate Trouble Shooting Guide for Manual FRU Isolation and addititional information.\n" );
  exit( $ERROR );
}

sub untestable_storage_to_storage
{
  my($class, $dev_a, $dev_b) = @_;

  print( "Test terminated!\n" );
  print( "linktest does not support testing of storage device $dev_a connected to storage $dev_b.\n" );
  print(  "Reference the appropriate Trouble Shooting Guide for Manual FRU Isolation and addititional information.\n" );
  exit( $ERROR );
}
sub ask_continue
{
  my $answer = Linktest->get_response( "Continue Isolation", $YESNO);
  print ("\n");
  return $answer;
}

sub ask_t3b_direct
{
  my $answer = Linktest->get_response( "Does this array have a direct cable connection", $YESNO);
  return $answer;
}


sub error {
  my($class, $err) = @_;

  open(OO, ">>" . System->get_home() . "/log/linktest.log");
  print OO $err . "\n";
  close(OO);
}
  
sub start_linktest_msg {
  my($class,  $node_a, $node_b ) = @_;

  my $device = $node_a->{type};

  print( "linktest started on FC interconnect: $device to $node_b->{type}\n" );

}

sub stop_linktest_msg {
  my($class,  $node_a, $node_b ) = @_;

  print( "linktest completed on FC interconnect: $node_a->{type} to $node_b->{type}\n" );
}

sub failed_linktest_msg {
  my($class, $node_a, $node_b ) = @_;

  print( "linktest failed on FC interconnect: $node_a->{type} to $node_b->{type}\n" );
}



sub usage {
  Linktest->tbd_msg(); 
  print "Usage: linktest -a node_a -b node_b\n";
  print " linktest -a \"type=t3|logical=/dev/...|ip=1.1.1.1|port=0|portWWN=wwn|monHost=rasd2\" \n";
  print "          -a \"type=switch|ip=1.1.1.1|port=2|fcaddr=108600|WWN=111|monHost=rasd2\"\n";
  print "          -a \"type=vicom|ip=1.1.1.1|login=v1|port=0|WWN=111|portWWN=111|monHost=rasd2\"\n";
  print "          -a \"type=hba|registerName=qlc0|path=/device...|ip=datahost.central\" \n";
  print "\n";
}

sub debug_header {
  my($class, $node_a, $node_b, $function_name ) = @_;

  if ( System->get_debug() == 1) {
    print "Function: $function_name\n";
    print "Node A: " . $node_a->dumper() . "\n";
    print "Node B: " . $node_b->dumper() . "\n";
  }
}

sub parse {
  my($class, $node_as, $node_bs) = @_;
  my @args;
  my(%node_a, %node_b);

  @args = split(/\|/, $node_as);
  foreach my $v (@args) {
    my($name, $value) = split(/=/, $v);
    $node_a{$name} = $value;
  }

  @args = split(/\|/, $node_bs);
  foreach my $v (@args) {
    my($name, $value) = split(/=/, $v);
    $node_b{$name} = $value;
  }
  return (\%node_a, \%node_b);
}



# '_name' => 'device3',
#'name' => '172.20.67.167',
#'type' => 'brocade',
#'ipno' => '172.20.67.167',
#'class' => 'switch.brocade',
#'ip' => '172.20.67.167',
#'key' => '1000006069201efc',
#'wwn' => '1000006069201efc',
#'active' => 'Y',
#'telnet' => '%70%61%73%73%77%6f%72%64',
#'host' => ''

# '_name' => 'device2',
# 'name' => 'Leno',
# 'type' => 'a5k',
# 'class' => 'storage.a5k',
# 'key' => '5080020000032510',
# 'hba' => 'S',
# 'wwn' => '5080020000032510',
# 'active' => 'Y',
# 'host' => ''

#'_name' => 'device1',
#'name' => '172.20.67.168',
#'type' => 't3',
#'ctrl_model' => '375-0084-02-JJ11',
#'ipno' => '172.20.67.168',
#'class' => 'storage.t3',
#'ip' => '172.20.67.168',
#'key' => 'slr-mi.370-3990-01-e-f0.016101',
#'wwn' => '50020f2300003ee5',
#'active' => 'Y',
#'telnet' => 'diags',
#'wwn2' => '50020f2300003d2c',
#'host' => ''




1;
