#
# Copyright (c) 2001, 2008, Oracle and/or its affiliates. All rights reserved.
#
#  $Id: Register.pm /st_emagent_10.2.0.4.2db11.2/1 2008/10/10 10:35:33 yozhang Exp $ 
#
#
# NAME  
#	 Register.pm
#
# DESC 
#	 Register and invoke the subroutines 
#
#
# FUNCTIONS
# AUTOLOADER
#
# NOTES
#
#
# MODIFIED	(MM/DD/YY)
# yozhang  10/03/08 - Add file check
# spanchum 06/21/07 - Backport rajverma_bug-6113649 from main
# rrawat   03/26/07 - Backport rrawat_bug-4944947 from main
# rrawat   06/21/06 - Bug-4955208
# ajdsouza 02/07/05 - qualify error messages to be loaded to rep with ERROR:
# ajdsouza 01/26/05 - log_message to keep message_counter instead of 
#                     message text
# ajdsouza 12/02/04 - Run in test mode using 0 byte file
# ajdsouza 11/15/04 - Add id to nls message structure message_list
# ajdsouza 10/28/04 - 
# ajdsouza 09/07/04 - Use fallback osd,platform generic and platform neutral
#                     for function calls
#                     add ability to run script in capture and regression test mode
# ajdsouza 08/17/04 - 
# ajdsouza 08/11/04 - 
# ajdsouza 07/27/04 - 
# ajdsouza 06/25/04 - storage reporting sources 
# ajdsouza 04/09/04 - 
# ajdsouza 04/08/04 - storage perl modules 
#
#
package storage::Register;

require v5.6.1;

use Exporter;
use strict;
use warnings;
use locale;
use File::Spec::Functions;
use File::Path;
use Data::Dumper;
use storage::Utilities;
use storage::vendor::Veritas;
use storage::vendor::Emc;
use storage::vendor::Hitachi;
use storage::sRawmetrics;

our @ISA = qw(Exporter);
our @EXPORT = qw( get_os_storage_entity_identifier 
                  get_agentstate_dir 
                  get_target_name 
                  get_target_id 
                  get_agentstatetarget_dir
                  log_message 
                  log_error_message 
                );

BEGIN
{
  # Check if the execution mode is regression or capture
  $storage::Register::run_mode = $ENV{EM_STORAGE_RMODE}
   if $ENV{EM_STORAGE_RMODE}
    and not $storage::Register::run_mode;

  # Get the complete path of the file based on the mangled function name
  # is the test(name) sub directory provided by the environment variable
  $storage::Register::test_name = $ENV{EM_STORAGE_TEST_NAME}
   if $ENV{EM_STORAGE_TEST_NAME}
     and not $storage::Register::test_name;

  # if the test desc is provided by the env
  $storage::Register::test_desc = $ENV{EM_STORAGE_TEST_DESCRIPTION}
   if $ENV{EM_STORAGE_TEST_DESCRIPTION}
    and not $storage::Register::test_desc;

  $storage::Register::test_desc = "No description"
   unless $storage::Register::test_desc; 

  # in the test environment take the test ddirectory be be under
  # srchome
   if 
   ( 
     $ENV{SRCHOME}
      and not $storage::Register::test_directory
   )
   {
     $storage::Register::test_directory = $ENV{SRCHOME};
     for my $dir ( qw ( emagent test src emd tvmac ) )
     {
       $storage::Register::test_directory =
        catfile($storage::Register::test_directory,$dir);
     }

     # If its not a valid directory do not take it
     stat $storage::Register::test_directory;

     undef $storage::Register::test_directory
      unless 
      (
       -e $storage::Register::test_directory
        and -d $storage::Register::test_directory
      );

   }

  # default the test_directory to agent_state_dir if its null
  (
   $storage::Register::test_directory = get_agentstate_dir() 
    or warn " Failed to get directory to cache storage metrics on host \n"
     and return
   )
   unless $storage::Register::test_directory;
 
  # If the regression mode is not provided in the env, check
  # if the 0 byte file nmhsr.lck exists in test_directory
  # take care of portability issue when concatenating the directory path
  if ( not $storage::Register::run_mode )
  {

   my $checkrmodefile = catfile($storage::Register::test_directory,'nmhsr.lck');
  
   stat $checkrmodefile;

   $storage::Register::run_mode = 'REGRESSION'
    if -e $checkrmodefile;

  }


}

#-----------------------------------------------------------------------------------------
# Global package variable to hold sub name
our $AUTOLOAD;

# global to hold all config infomation
our %config;

# Global package variables for REGRESSION or CAPTURE mode execution
# to cache the execution mode REGRESSION or CAPTURE 
# test(name) directory 
# parent directory for location of test files
# counter for function invocation
our $run_mode;
our $test_directory;
our $test_desc;
our $test_name;
our %regression_fn_count;
our $read_fn_results_ref;
our %write_fn_results;

# Global to indicate the metric being excuted data|keys|alias|issu
our $metric_name;

# Global package variable to cache the list of all filesystems so can be used by other modules
# outside of Filesystem. This global is populated by Filesystem.pm
our @filesystemarray;

# Global package variable to cache the mount privileges by mountpoint
our %mount_privilege;

# Global package variable to cache the sat device id to the mountpoint
our %mountpoint_id_index;

# Global package variable to indentify the host target 
our $em_target_id;
our $em_target_name;

# Global package variable to hold execution earnigns and errors 
our %error_stack;

# Global package variable to log storage issues 
our @logged_messages;
our %logged_message_index;

# Global package variable to hold all nls strings
our %message_list;

# Global package variable to cache values on the host 
our $agent_state_dir;
our $agent_state_target_dir;

# Global package variable to cache file seperator
our $file_seperator;

# Global package variable to cache absolute path start
our $abs_path_start;

# global to hold all failed commands
our %failed_command;

#-----------------------------------------------------------------------------------------
# List of NLS messages for issues
#
# nls id format nls_st<e|a>_<xxxxx>
#
#-----------------------------------------------------------------------------------------
$message_list{ERROR_INST_PROCESSING} =
 {
   message => 'Instrumentation Error - Failed to instrument storage metrics for the host'
 };

$message_list{ERROR_INST_MAPPING} =
 {
   message => 'Instrumentation Error - Failed to map storage entity {0}, this may result in inaccuracies in the storage report',
   message_params => 
   { 
     1=> [ ( 'entity_type,type' , 'name' ) ] 
   }
 };

$message_list{ERROR_INST_INVALID_SIZE} =
 {
   message => 'Instrumentation Error - There is an error in the size, used and free byte values instrumented for storage entity {0}',
   message_params => 
   { 
     1=> [ ( 'entity_type,type' , 'name' ) ]
   }
 };

$message_list{ERROR_INST_NO_GID} =
 {
   message => 'Instrumentation Error - Failed to generate a globally unique identifier for {0}, this may result in inaccuracies in shared storage computation',
   message_params => 
   {
     1=> [ ( 'storage_layer,type', 'name' ) ]
   }
 };

$message_list{ERROR_INST_NO_TARGET_ID} =
 {
   message => 'Instrumentation Error - Failed to fetch the hostname or EM target_guid for the host target'
 };

$message_list{ACTION_INST_CHECK_AGENT_LOG}=
 {
   message => 'Check the error log for the detailed error message'
 };

$message_list{ACTION_INST_RESOLV_ISSUE} =
 {
   message => 'The following steps may help resolve this issue (a) Check the metric collection error log for detailed error message (b) Check for stale storage configuration on the host (c) Refresh storage metrics for this host. Contact Oracle Support if the issue persists'
 };
#------------------------------------------------------------------------------
# Register those subs where invoked sub names are not present in 
# storage perl modules sRawmetrics, sUtilities and Utilities.pm
#
#-------------------------------------------------------------------------------

my %subRegister;

$subRegister {getEmcDiskData}			= \&storage::vendor::Emc::getDiskinfo;
$subRegister {getEmcClariionDiskData}           = \&storage::vendor::Emc::getClariionDiskinfo;
$subRegister {getSunDiskData}			= \&storage::vendor::Sun::getDiskinfo;
$subRegister {getHitachiDiskData}		= \&storage::vendor::Hitachi::getDiskinfo;

$subRegister {generateEmcDiskId}		= \&storage::vendor::Emc::generateDiskId;
$subRegister {generateEmcClariionDiskId}        = \&storage::vendor::Emc::generateClariionDiskId;
$subRegister {generateSunDiskId}		= \&storage::vendor::Sun::generateDiskId;
$subRegister {generateHitachiDiskId}		= \&storage::vendor::Hitachi::generateDiskId;



#-------------------------------------------------------------------------------
# FUNCTION : setup_test_directories
#
# DESC 
# initialize the testname or subdirectory for capturing or regressing the test 
# files if the name is passed in the environment read it from the test cfg file
# if no name can be got run in normal mode
#
# initialize the test location directory
# read it from the enviromnent
# use the agent state directory if not set in env
#
# Creates the directories if they are not present
#
# return an error if directories cant be setup
#
# ARGUMENTS
#
#-------------------------------------------------------------------------------
sub setup_test_directories()
{

  my $tcfgfile = 'tvmacs.cfg';   # Name of the test config file

  # Get the agent cache directory for storage
  warn " Failed to get directory for test files on host \n"
   and return
    unless $storage::Register::test_directory;

  stat($storage::Register::test_directory);

  warn "Test directory $storage::Register::test_directory is not present \n" 
   and return 
    unless
    (
     -e $storage::Register::test_directory 
      and -d $storage::Register::test_directory 
       and -w $storage::Register::test_directory
    );

  # If no test name is defined read the testname from the config test file 
  # pick test name based on target name passed from the em agent
  if ( not $storage::Register::test_name )
  {

    my $target_name = get_target_name() 
     or warn "Failed to get the target_name for the target\n";
 
    if ( $target_name and $target_name !~ /unknown/i )
    {
      # path to the config test file
      my $testfilepath = catfile($storage::Register::test_directory,$tcfgfile);
     
      stat($testfilepath);
    
      # read from the config test file if one exists
      if ( -e $testfilepath  and -r $testfilepath )
      {
    
         # Open the file for reading
         open(FH,"$testfilepath") or
          warn "Failed to open the test configuration file $testfilepath for reading\n"
           and return;
      
           
         # Read each line and prepare it for printing
         while ( <FH> )
         {
           my $tstcfgr = $_;
    
           chomp($tstcfgr);
      
           # each row should be of format target_name:test_name
           $tstcfgr =~ s/^\s+|\s+$//g;
    
           # ignore comments
           next if $tstcfgr =~ /^#/;

           next unless $tstcfgr =~ /^.+\s*:\s*.+/;
    
           my ( $tname, $tstname) =
            ( $tstcfgr =~ /^(.+)\s*:\s*(.+)/ );
         
           $tname   =~ s/^\s+|\s+$//g; 
           $tstname =~ s/^\s+|\s+$//g; 
    
           next unless $tname;
    
           $tstname = 'none' unless $tstname;
    
           # If the target name matches a target name in the file 
           # pick that test name
           $storage::Register::test_name = $tstname
            and last
             if $tname =~ /^$target_name$/;
    
         }
      
         close(FH) or 
          warn "Failed to close the test configuration file $testfilepath\n";
         
      }

    }

  }

  # change spaces to _ from the test directory name
  $storage::Register::test_name =~ s/\s+/_/g
   if $storage::Register::test_name;

  $storage::Register::test_name = 'none'
   unless $storage::Register::test_name;

  return 1;

}

#-------------------------------------------------------------------------------
# FUNCTION :  get_fn_results($)
#
# DESC     : 
# return the captured results for a function call
#
# ARGS     :
#  $  - mangled interface name
#
#-------------------------------------------------------------------------------
sub get_fn_results($)
{
     
  my ( $interface_name) = @_;

  $interface_name =~ s/\s+//;

  warn "Interface name expected as arg in get_fn_results\n"
   and return
    unless $interface_name;

  $interface_name  =~ s/\./_dt_/g;
   
   if 
   ( 
    not $read_fn_results_ref
     or ref($read_fn_results_ref) !~ /HASH/i
      or not keys %{$read_fn_results_ref} 
   )
   {

     my $test_file = catfile($storage::Register::test_directory,"$storage::Register::test_name.dat");

     stat($test_file);

     warn "File $test_file for captured regression test data is not accessible\n"
      and return
       unless -e $test_file and -r $test_file;

     $read_fn_results_ref = do "$test_file"
      or warn "Failed to reach the captured data from file $test_file\n"
       and return; 
  
     warn "Failed to read the captured test results from file $test_file\n"
      and return 
       unless 
      (
       $read_fn_results_ref
        and ref($read_fn_results_ref) =~ /HASH/i
         and keys %{$read_fn_results_ref}
      );

   }

   warn "No interfaces saved for metric $storage::Register::metric_name, ".
    "Failed to find the interface name $interface_name in test $storage::Register::test_name\n"
     and return 
      unless $read_fn_results_ref->{$storage::Register::metric_name};

   warn "Failed to find the interface name $interface_name in test $storage::Register::test_name\n"
    and return 
     unless $read_fn_results_ref->{$storage::Register::metric_name}{$interface_name};

   my $fnresults = $read_fn_results_ref->{$storage::Register::metric_name}{$interface_name};

   my $VAR1;

   eval $fnresults
    or warn "Failed to eval results for interface $interface_name , $fnresults\n"
     and return;

   return $VAR1;

}


#-------------------------------------------------------------------------------
# FUNCTION :  save_fn_results($$)
#
# DESC     : 
# Save the test results for a function call
#
# ARGS     :
#  $  - mangled interface name
#  $  - ref or scalar results
#
#-------------------------------------------------------------------------------
sub save_fn_results($$)
{

  my ( $interface_name, $fn_results) = @_;

  $interface_name =~ s/\s+//;

  warn "Interface name expected as arg in save_fn_results\n"
   and return
    unless $interface_name;

  $interface_name  =~ s/\./_dt_/g;

  #-----------------------------------------------------------------------------
  # Read the saved function result hash, if the metri is keys|alias|issues
  # and the mode is capture
  #-----------------------------------------------------------------------------
  # Use the existing functions for the issues, keys and alias metrics
  if 
  ( 
    $storage::Register::metric_name !~ /data/  
     and not keys %storage::Register::write_fn_results
  )
  {

    # Read the test results if the earlier results exist in file
    my $test_file = 
     catfile($storage::Register::test_directory,"$storage::Register::test_name.dat");

    stat($test_file);

    warn "File $test_file for captured regression test data is not accessible\n"
     and return
      unless -e $test_file and -r $test_file;

    $read_fn_results_ref = do "$test_file"
     or warn "Failed to reach the captured data from file $test_file\n"
      and return;
   
    warn "Failed to read the captured functions results from file $test_file\n" 
     and return
      unless $read_fn_results_ref 
       and ref($read_fn_results_ref ) =~ /HASH/i;

    %storage::Register::write_fn_results =
     %{$read_fn_results_ref};

  }

  return 1 
   if $storage::Register::write_fn_results{$storage::Register::metric_name}{$interface_name};

  my $dumped_results = Dumper($fn_results)
   or warn "Failed to save results for function $interface_name\n"
    and return;

  $storage::Register::write_fn_results{$storage::Register::metric_name}{$interface_name} = $dumped_results;

  return 1;

}

#-------------------------------------------------------------------------------
# FUNCTION :  save_results_to_file()
#
# DESC     : 
# Save all the test results to <testfile>.dat 
#
# ARGS     :
#
#-------------------------------------------------------------------------------
sub save_results_to_file()
{
  warn "No results captured for test $storage::Register::test_name\n"
   and return 
    unless 
     keys %storage::Register::write_fn_results;

  my $target_name = get_target_name() 
   or warn "Failed to get the target_name for the target\n"
    and return;

  $storage::Register::test_desc = 
   "$target_name-$storage::Register::test_desc"
     if $target_name;

  save_fn_results('desc',$storage::Register::test_desc)
   or warn "Failed to save the description for test $storage::Register::test_name\n"
    and return;
 
  $Data::Dumper::Indent = 2;
  my $thewholestrg = Dumper(\%storage::Register::write_fn_results)
   or warn "Failed to save the results for test $storage::Register::test_name\n"
    and return;

  my $test_file = 
    catfile($storage::Register::test_directory,"$storage::Register::test_name.dat");

  stat($test_file);

  warn "File $test_file for captured regression test data is not accessible\n"
    and return
     if -e $test_file and not -w $test_file;

  open(FH,">$test_file")
   or warn " Failed to open file $test_file for capturing test results \n"
    and return;

  print FH $thewholestrg;

  close(FH);

  return 1;

}

#-------------------------------------------------------------------------------
# FUNCTION : regression_test
#
# DESC 
# Perform a capture for regression test, or perform a regression_test
# Specified by enironment variables
#
# ARGUMENTS
# sub name
# reference to the sub to be executed
# reference to the list of arguments to the sub
#
#-------------------------------------------------------------------------------
sub regression_test($\&\@)
{

  my ($sub,$sub_ref,$array_ref) = @_;

  my @args = @$array_ref;

  #-----------------------------------------------------------------------------
  # fall thru below this line only for test mode
  # either REGRESSION or CAPTURE
  #-----------------------------------------------------------------------------
  # The test mode validation
  warn "Unknown run mode $storage::Register::run_mode , can be REGRESSION or CAPTURE\n"
   and return
    unless 
    (
     $storage::Register::run_mode
      and $storage::Register::run_mode =~ /REGRESSION|CAPTURE/i
    );

  # There should be a metricname defined at this point
  warn "Metric name for execution not specified, metric name required \n"
   and return
    unless $storage::Register::metric_name;

  # set up the test directories if they are not initialized
  # this should be done once per execution
  if 
  ( 
    not $storage::Register::test_name  
     or not $storage::Register::test_directory
  )
  {
    setup_test_directories() or return;
  }

  # testname is none , skip regression 
  # execute the subroutine and return as in a normal run
  return &$sub_ref(@args) 
   if $storage::Register::test_name =~ /^none$/;
 
  return unless $storage::Register::test_directory;

  stat($storage::Register::test_directory);
 
  return 
   unless
   (
       -e $storage::Register::test_directory
        and -d $storage::Register::test_directory
         and -w $storage::Register::test_directory
   );

  #-----------------------------------------------------------------------------
  # Start Regression or capture
  #-----------------------------------------------------------------------------
  # create a mangled name for capture and regression test mode
  my $function_name = "fn_$sub";

  # Get the file seperator
  $file_seperator = get_file_seperator() or
   warn " Failed to get file seperator for host \n"
    and return;

  # mangle the arguments into a function_name to be saved as a file
  # for non scalar refs, put the ref type and not the address
  # the function counter will take care of opening the right file
  # durign regression
  map 
  { 
    my $element_value = $_;
    my $ref_type = ref($element_value); 

    if ( not $ref_type )
    { 
      $function_name =  "$function_name\_$element_value";
    }
    elsif (  $ref_type =~ /SCALAR/i )
    {
      $function_name = "$function_name\_$$element_value";
    }
    else
    {
      $function_name = "$function_name\_$ref_type";
    }

  } @args if @args;

 
  # Remove these special characters from the file name
  $function_name =~ s/\@/_a_/g;
  $function_name =~ s/\&/_am_/g;
  $function_name =~ s/\*/_as_/g;
  $function_name =~ s/\\/_b_/g;
  $function_name =~ s/\(|\)|{|}|\[|\]/_br_/g;
  $function_name =~ s/\^/_c_/g;
  $function_name =~ s/\$/_d_/g;
  $function_name =~ s/\./_do_/g;
  $function_name =~ s/\!/_e_/g;
  $function_name =~ s/\`/_es_/g;
  $function_name =~ s/\=/_eq_/g;
  $function_name =~ s/$file_seperator/_fs_/g;
  $function_name =~ s/\#/_h_/g;
  $function_name =~ s/\-/_hf_/g;
  $function_name =~ s/\n/_nl_/g;
  $function_name =~ s/\+/_p_/g;
  $function_name =~ s/\|/_pp_/g;
  $function_name =~ s/\s+/_s_/g;
  $function_name =~ s/\~/_t_/g;
  $function_name =~ s/\,/_cm_/g;
  $function_name =~ s/\:/_sc_/g;

  warn "Failed to generate a function signature for sub $sub \n" 
   and return unless $function_name;

  # Appened a count to the storage function name 
  # The same function may be invoked multiple times with
  # different results
  $storage::Register::regression_fn_count{$function_name}=0
   unless $storage::Register::regression_fn_count{$function_name};

  $storage::Register::regression_fn_count{$function_name}++;

  $function_name = "$function_name\_cnt\_$storage::Register::regression_fn_count{$function_name}";

  #------------------------------------------------------------
  # If test regression mode read from stored file and return 
  # results
  #------------------------------------------------------------
  if ( $storage::Register::run_mode =~ /REGRESSION/i )
  {
    
    # no results expected, execute the function and return
    return &$sub_ref(@args) if not defined wantarray;

    my $result_ref = get_fn_results($function_name) 
     or warn "Failed to get captured results for interface $function_name for regression \n"
      and return;

    # results expected is a list
    if ( wantarray )
    {
      return @$result_ref; 
    }
    else
    # result expected is a scalar or 
    # a reference to a list
    {
     my $ref_type = ref($result_ref);

     # if the result read is a reference to a scalar, 
     # return scalar value
     return $$result_ref if $ref_type =~ /SCALAR/i;

     # if result read is a ref to a list , return reference
     return $result_ref;
    }

  }

  #------------------------------------------------------
  # fall thru below this line only If test capture mode
  #------------------------------------------------------

  # no results expected, so nothing to capture, 
  # execute the subroutine and return
  return &$sub_ref(@args) if not defined wantarray;

  #------------------------------------------------------
  # fall thru below this line only If return value is 
  # expected
  #------------------------------------------------------

  # If an list result is expected 
  if ( wantarray )
  {

   my @results_array =  &$sub_ref(@args);

   # Store the results
   save_fn_results($function_name,\@results_array)
    or return;

   # return the results
   return @results_array;
  
  }
  else
  # an scalar result is expected
  # scalar could be an reference to a list
  {

   my $results_ref = &$sub_ref(@args);
   my $store_results_ref = $results_ref;

   # if result is not a ref
   # take a pointer to the scalar
   # to store results
   my $ref_type = ref($results_ref);
   $store_results_ref = \$results_ref unless $ref_type;

   # Store the results
   save_fn_results($function_name,$store_results_ref)
    or return;

   # return the results
   return $results_ref;

  }

}

#-----------------------------------------------------------------------------------------
# FUNCTION : AUTOLOAD
#
# DESC 
# Autoload and execute the sub if sub is registered and defined
#
# ARGUMENTS
# Args to be passed to the sub
#
#-----------------------------------------------------------------------------------------
sub AUTOLOAD
{
    
  my @args = @_;

  my $sub = $AUTOLOAD;
    
  $sub =~ s/.*:://;	
    
  warn "ERROR:Invoked without a subroutine name \n" 
   and return unless $sub;

  # get a ref to the sub if its declared and defined in the register 
  my $sub_ref;
  if  ( $subRegister{$sub} and defined &{$subRegister{$sub}} )
  {
    $sub_ref = $subRegister{$sub};
  }
  else
  # If the sub is not in the register get a pointer thru sRawmetrics
  { 
    if ( ("storage::sRawmetrics::$sub" and defined(&{"storage::sRawmetrics::$sub"}))
      or ("storage::sUtilities::$sub" and defined(&{"storage::sUtilities::$sub"}))
      or ("storage::Utilities::$sub" and defined(&{"storage::Utilities::$sub"})) )
    {
      my $sub_path = "storage::sRawmetrics::$sub";
      $sub_ref = \&$sub_path;
    }
  }

  # If the sub is not defined return with warn
  warn "ERROR:Function $sub is not found in storage perl modules \n" 
   and return 
    unless $sub_ref;

  # executed this subroutine and return if run mode is either regression or capture
  return &$sub_ref(@args) 
   unless $storage::Register::run_mode;

  #-----------------------------------------------------------
  # fall thru below this line only for test mode
  # either REGRESSION or CAPTURE
  #-----------------------------------------------------------
  return regression_test($sub,&$sub_ref,@args);

}

#-----------------------------------------------------------------------------------------
# FUNCTION : save_failed_command_to_file
#
# DESC
# Saves the failed commands to a file
#
# ARGUMENTS : NONE
#
#-----------------------------------------------------------------------------------------
sub save_failed_command_to_file()
{
  return
    unless
     keys %storage::Register::failed_command;

  my $agent_state_target_dir = get_agentstatetarget_dir() or
     warn "ERROR:Failed to get directory to cache failed command on host \n"
      and return;
  my $cache_file = catfile($agent_state_target_dir,'nmhsfl.txt');

  stat($cache_file);

  warn "File $cache_file for persisting failed commands is not accessible\n"
    and return
     if -e $cache_file and not -w $cache_file;

  open(FH,">$cache_file")
   or warn " Failed to open file $cache_file for writing failed command \n"
    and return;

  foreach $storage::Register::failed_command (keys %storage::Register::failed_command)
  {
     print FH "$storage::Register::failed_command\n";
  }

  close(FH);
}




#End block , invoked when unloading the module

END
{

  save_failed_command_to_file();

  # If in capture mode, persist the function
  # call results to a file
  if 
  ( 
   $storage::Register::run_mode
    and $storage::Register::run_mode =~ /CAPTURE/i 
  )
  {
    save_results_to_file() 
     or warn "Failed to persist the captured test results to a file in save_results_to_file\n"
      and return;
  }

}

1; #Returning a true value at the end of the module

