package Linktest::Node;
use Data::Dumper;
use strict;

# node_a:  {
#          'bitMode' => 32|64,
#          'monHost' => 'ccadieux.central.sun.com',
#          'portWWN' => 'wwn',
#          'ip' => '1.1.1.1',
#          'type' => 't3',
#          'port' => '0'
#        };

#node_b:  {
#          'monHost' => 'rasd2',
#          'ip' => '1.1.1.1',
#          'type' => 'switch',
#          'WWN' => 111,
#          'fcaddr' => 108600,
#          'port' => 2
#        };


sub create {
  my($class, $node, $config_node, $topo_node)  = @_;

  my $obj = $node;
  my $type = uc($node->{type});
  $obj->{_config_node} = $config_node;
  $obj->{_topo_node}   = $topo_node;

  my $cat = "Linktest::$type";
  if (!-f System->get_home() . "/lib/Linktest/$type.pm") {
     print "Error: No Linktest/$type.pm class found(from Linktest/Node.pm)\n";
     exit(1);
  }
 
  bless($obj, $cat);
  return $obj;
}
#
# $config = $nodeA->config();   #get the rasagent.conf info.

sub config {
  my($node) = @_;
  return $node->{_config_node};
}

#
# $config = $nodeA->topo();   #get the node from the topology database

sub topo {
  my($node) = @_;
  return $node->{_topo_node};
}

# allow aditional port informaton to be displayed
sub add_port_info{
  my($node, $port) = @_;
  return undef;
}

sub prompt {
  my($node, $tag) = @_;
  my $fcaddr;

  my $config_dev = $node->config();
  
  my($device, $port) = $node->prompt_info();

  $port += 0;
  if ( $config_dev->{name} ) {
    $device .= " (" . $config_dev->{name} . ")";
  }
  my $p = $Linktest::PROMPTS{$tag};

  $port .= $node->add_port_info($port);

  if ($p) {
     $p = $p . "\n";
     print( sprintf( $p, $node->{type}, $device, $port ) );
  } else {
     print( "Internal error: Cannot find prompt $tag\n" );
     Linktest->error( "Cannot find prompt $tag" );
  }

}
  

sub info {
  my($obj, $id) =  @_;
  return $obj->{$id};
}

sub dumper {
  my($obj) = @_;

  my $out ;
  foreach my $el (keys %$obj) {
    next if (substr($el,0,1) eq "_");
    $out .= "     $el\t=> $obj->{$el}\n";
  }
  $Data::Dumper::Indent = 1;
  $out .= "  Config= " . substr(Dumper($obj->config()), 13);

  return $out;
}

sub id {
  my($obj) = @_;

  my $id = "$obj->{type}:$obj->{name}";
  $id .= "/ip=$obj->{ipno}" if ($obj->{ipno});
  $id .= "/wwn=$obj->{WWN}" if ($obj->{WWN});
  $id .= " $obj->{path}" if ($obj->{path});
  return $id;
}

sub portTest {0}

# IO test means that this device can be test by
# sending IO against it. This is not the best
# type of isolation because you have to use
# the entire path to find errors but it is a
# last resort test.
sub IOtest {0};

sub class {
  my($obj) = @_;
  print "Node " . $obj->id() . " has no class method!\n";
  return 0;
}

sub password {
  my($node) = @_;

  my $dev = $node->config();
  if (!exists($dev->{telnet})) {
     return undef;
  } else {
     return Util->decode($dev->{telnet});
  }
}


sub needPassword {0}

sub diag_defaults {
  my($node) = @_;
  my($option_user_pattern);

  my $linkEnv     = System->get_linkEnv();
  my $verbose     = "-v" if ($linkEnv->{verbose2});
  my $wb_patterns = $linkEnv->{option_pattern_type};

  if ( $linkEnv->{option_pattern_type} eq "user" ) {
    $option_user_pattern = "|userpattern=$linkEnv->{option_pattern}";
  }
  return ($verbose, $wb_patterns, $option_user_pattern, 
          $linkEnv->{option_pattern_type}, $linkEnv);
}

sub printit {
  my($sofar) = @_;
  print $sofar;
}

sub stop_test_msg {
  my($node, $test, $out, $verbose ) = @_;


  print( "$test " );
  if ( $out->{rc} == 0 ) {
    print( "completed successfully\n" );
  } elsif ( $out->{rc} & 128 ) {
    print( "test core dumped\n" );
  } elsif ( $out->{rc} & 127 ) {
    print( "test died signal ( $out->{rc} & 127 )\n" );
  } else {
    print( "failed\n" );
  }

  if ( $verbose ) {
    my $adjusted = $out->{rc} >> 8;

    print( "error code: $adjusted  System Error: $out->{rc}\n" );
  }
}

sub start_test_msg {
  my($node, $test, $options, $verbose, $estimated_time) = @_;

  print( "$test started on " . $node->id() . "\n" );
  print "options= $options\n" if (System->get_debug());

}




sub run_diagnostic {
  my($node, $test,  $sparcv9, $options, $estimated_time) = @_;

  my $linkEnv    = System->get_linkEnv();
  my $diagnostic = "/Diags/bin/$sparcv9$test $options";
  my $verbose    = $linkEnv->{verbose};

  $node->start_test_msg($test, $options, $verbose, 
                            $estimated_time );

  my $out = Scheduler->syncRun($node->{monHost}, $diagnostic, 
                            $linkEnv->{test_timeout}, 
                             { progressHandler => \&printit });

  if ( defined($out->{rc}) ) {
    $node->stop_test_msg($test, $out, $verbose );
    # must adjust rc code to remove the return signal code
    $out->{rc} = $out->{rc} >> 8;
  } else {
    print( "Program Error: Status not returned from remote test.\n" );
    Linktest->program_error();
  }

  # predefined error codes that isolate to specific actions

  if ($node->type2() eq "SW") {
    if ( $out->{rc} == 100 ) {
      # reserved for future use
      # port offline - DO NOT IMPLEMENT
      # $node->prompt('port_offline' );
      # $node->prompt('ra_port_offline' );
      # Linktest->ra_exit();

    } elsif ( $out->{rc} == 101 ) {
      # port admin offline (sun switch(s))
      $node->prompt('port_admin_offline' );
      $node->prompt('ra_port_admin_offline' );
      Linktest->ra_exit();

    } elsif ( $out->{rc} == 102 ) {
      # port disabled
      $node->prompt('port_disabled' );
      $node->prompt('ra_port_disabled' );
      Linktest->ra_exit();

    } elsif ( $out->{rc} == 103 ) {
      # reserved for future use
    } elsif ( $out->{rc} == 104 ) {
      # reserved for future use
    } elsif ( $out->{rc} == 105 ) {
      # cannot login to port invalid passwd (brcoade/sun2gb)
      $node->prompt('invalid_password' );
      $node->prompt('ra_invalid_password' );
      Linktest->ra_exit();

    } elsif ( $out->{rc} == 106 ) {
      # cannot communicate w/switch (brocade)
      $node->prompt('no_communication' );
      $node->prompt('ra_communication' );
      Linktest->ra_exit();

    }
  }
  return $out;
}


sub start_diagnostic {
  my($node) = @_;

  print "Error: start_diagnostic not available for this node " . $node->id() . "\n";
  return 0;
}





###############################################
#  SWITCH SPECIFIC, need to move in FCSWITCH
###############################################

sub portType {
  my($sw) = @_;
  my($port_type);
  my $to_node = $sw->topo();
  if (!$to_node) {
    $sw->prompt('no_instrumentation' );
    $sw->prompt('ra_instrumentation' );
    Linktest->program_error();
  }

  my $ports = $to_node->portInfo();
  my $port = $ports->[$sw->{port}];
  return $port->{sw_PortType};
}

sub port_node_isolation {
  my ($node, $other_node, $failed_node ) = @_;

  Linktest->debug_header( $node, $other_node, "port_node_isolation" );

  my ($answer, $status, $out);

  $node->prompt('remove_port_cable' );
  $node->prompt('insert_port_loopback' );
  $answer = Linktest->ask_continue();

  if ( $answer ne "n" ) {
    $out = $node->start_diagnostic();
  } else {
    $node->prompt('remove_port_loopback' );
    $node->prompt('restore_port_cable' );
    Linktest->premature_exit();
  }
  if ( $out->{rc} == 0 ) {
    # the test passed with a loopback, the switch port node is OK
    # test the next node, could be a cable problem
    $node->prompt('remove_port_loopback' );
    $node->prompt('restore_port_cable' );
    $node->prompt('functional_port' );
    if ( $failed_node ne "b" ) {
      $failed_node = "b";
      return $Linktest::SUCCESS;
    }

  } else {

    $node->prompt('remove_port_loopback' );
    $node->prompt('replace_port_gbic' );
    $node->prompt('insert_port_loopback' );
    $answer = Linktest->ask_continue();
    if ( $answer ne "n" ) {
      $out = $node->start_diagnostic();
    } else {
      $node->prompt('remove_port_loopback' );
      $node->prompt('restore_port_gbic' );
      $node->prompt('restore_port_cable' );
      Linktest->premature_exit();
    }
    if ( $out->{rc} == 0 ) {
      # the test passed with a new GBIC. The GBIC is the suspect.
      $node->prompt('remove_port_loopback' );
      $node->prompt('restore_port_cable' );
      $node->prompt('suspect_port_gbic' );
      Linktest->retest_msg();
      return $Linktest::FOUND_SUSPECT_FRU;
    } else {
      # the test failed with a new GBIC. The port and/or switch is suspect.

      $node->prompt('restore_orignal_components' );

     $node->prompt('remove_port_loopback' );
      $node->prompt('restore_port_cable' );
      $node->prompt('restore_port_gbic' );
      $node->prompt('multiple_suspects' );

      $node->prompt('suspect_switch' );
      $node->prompt('ra_suspect_switch' );

      $node->prompt('suspect_port' );
      $node->prompt('ra_suspect_port' );

      $node->prompt('suspect_replaced_gbic' );
      $node->prompt('ra_suspect_replaced_gbic' );

      Linktest->multiple_frus_msg();
      return $Linktest::MULTIPLE_FRUS_FOUND;
    }

  }

  if ( $failed_node eq "b" ) {
    # node a passed all loopback testing and node b passed loopback
    # try CABLE
    $node->prompt('replace_port_cable' );
    $answer = Linktest->ask_continue();
    if ( $answer ne "n" ) {
      if ( $node->{type} eq 'brocade' && $other_node->{type} eq 'hba' ) {
        $out = $other_node->start_diagnostic("E" );
      } else {
        $out = $node->start_diagnostic();
      }
    } else {
      $node->prompt('remove_port_cable' );
      $node->prompt('restore_port_cable' );
      Linktest->premature_exit();
    }
    if ( $out->{rc} == 0 ) {
      # the test passed with a new CABLE. The CABLE is the suspect.
      $node->prompt('suspect_port_cable' );
      Linktest->retest_msg();
      return $Linktest::FOUND_SUSPECT_FRU;
    } else {
      Linktest->premature_exit();
    }
  }

  Linktest->tbd_msg();
  return $Linktest::PROBLEM_UNDETECTED;
}

sub single_isl_port_node_isolation {
  my ($node, $other_node, $failed_node ) = @_;

  Linktest->debug_header( $node, $other_node, "single_isl_port_node_isolation" );

  my $answer;
  my $status;
  my $out;

  $node->prompt('remove_port_cable' );
  $node->prompt('insert_port_loopback' );
  $answer = Linktest->ask_continue();
  if ( $answer ne "n" ) {
    $out = start_diagnostic( $node );
  } else {
    $node->prompt('remove_port_loopback' );
    $node->prompt('restore_port_cable' );
    Linktest->premature_exit();
  }
  if ( $out->{rc} == 0 ) {
    # the test passed with a loopback, the switch port node is OK
    # test the next node, could be a cable problem
    $node->prompt('functional_port' );
  } else {
    $node->prompt('remove_port_loopback' );
    $node->prompt('replace_port_gbic' );
    $node->prompt('insert_port_loopback' );
    $answer = Linktest->ask_continue();
    if ( $answer ne "n" ) {
      $out = start_diagnostic( $node );
    } else {
      $node->prompt('remove_port_loopback' );
      $node->prompt('restore_port_gbic' );
      $node->prompt('restore_port_cable' );
      Linktest->premature_exit();
    }
    if ( $out->{rc} == 0 ) {
      # the test passed with a new GBIC. The GBIC is the suspect.
      $node->prompt('remove_port_loopback' );
      $node->prompt('restore_port_cable' );
      $node->prompt('suspect_port_gbic' );
      Linktest->retest_msg();
      return $Linktest::FOUND_SUSPECT_FRU;
    }
  }


  # try remote GBIC
  $node->prompt('remove_port_loopback' );
  $node->prompt('restore_port_cable' );
  $other_node->prompt('replace_port_gbic' );
  $other_node->prompt('restore_port_cable' );
  $answer = Linktest->ask_continue();
  if ( $answer ne "n" ) {
    $out = start_diagnostic( $node );
  } else {
    $other_node->prompt('restore_port_gbic' );
    $other_node->prompt('restore_port_cable' );
    Linktest->premature_exit();
  }
  if ( $out->{rc} == 0 ) {
    # the test passed with a new remote GBIC. The GBIC is the suspect.
    $other_node->prompt('suspect_port_gbic' );
    Linktest->retest_msg();
    return $Linktest::FOUND_SUSPECT_FRU;
  }

  # node a passed all loopback testing and node b passed loopback
  # try CABLE
  $other_node->prompt('replace_port_cable' );
  $answer = Linktest->ask_continue();
  if ( $answer ne "n" ) {
    $out = start_diagnostic( $node );
  } else {
    $other_node->prompt('remove_port_cable' );
    $other_node->prompt('restore_port_cable' );
    Linktest->premature_exit();
  }
  if ( $out->{rc} == 0 ) {
    # the test passed with a new CABLE. The CABLE is the suspect.
    $other_node->prompt('suspect_port_cable' );
    Linktest->retest_msg();
    return $Linktest::FOUND_SUSPECT_FRU;
  } else {

      # the test failed after trying all hot swap FRUs .
      # The port and/or switch is suspect.
      $node->prompt('restore_orignal_components' );

      $node->prompt('remove_port_cable' );
      $node->prompt('restore_port_cable' );

      $node->prompt('multiple_suspects' );

      $node->prompt('suspect_switch' );
      $node->prompt('ra_suspect_switch' );

      $node->prompt('suspect_port' );
      $node->prompt('ra_suspect_port' );

      $node->prompt('suspect_replaced_gbic' );
      $node->prompt('ra_suspect_replaced_gbic' );

      Linktest->multiple_frus_msg();
      return $Linktest::MULTIPLE_FRUS_FOUND;
  }

  Linktest->tbd_msg();
  return $Linktest::PROBLEM_UNDETECTED;

}





1;
