#!/usr/local/bin/perl
# 
# $Header: opatchauto/oms/generateMultiOMSPatchingScripts.pl /st_opatchauto_pt-em13.5mos/13 2021/11/11 05:38:28 sallam Exp $
#
# generateMultiOMSPatchingScripts.pl
# 
# Copyright (c) 2013, 2021, Oracle and/or its affiliates.
#
#    NAME
#      generateMultiOMSPatchingScripts.pl - <one-line expansion of the name>
#
#    DESCRIPTION
#      This perl file is a utility to generated user-friendly scripts (one per OMS instance) for actual
#      patch execution/deployment based on the output steps from OPatchauto OMS. This perl file does not
#      support Windows.
#
#    NOTES
#      <other useful comments, qualifications, etc.>
#
#    MODIFIED   (MM/DD/YY)
#       sallam   09/22/21 - job queue changes
#       sallam   09/16/21 - bug#33350050
#       sallam   08/13/21 - ru01 d4 fix
#       smkumbar 08/04/21 - LCM lock feature
#       sallam   11/03/20 - adding code to run the downtime
#    kamlesku    10/25/2016 - Zero downtime patching support for RCU patch
#    skkkurapa   06/28/15 - Support to copy automation files from primary host to remote hosts
#			    This is needed as starting from 13.1 opatch api does not 
#			    copy automation files implicitly
#    vganesan    06/10/14 - Fix Bug #18937446 
#    vganesan    06/04/14 - Fix Bug #18900162
#    vganesan    06/01/14 - (1) Trap error codes given by individual command and rely it to echo $?
#                               of the script
#                           (2) For empty execution tracker deletion, check for file existence, 
#                               fix bug #18871442
#                           (3) Populate previous script path directly for failed session
#                               fix bug #18870628 
#                           (4) Fix 'opatch apply/napply' error code issue. Earlier error from
#                               command script generation was not trapped due to 'tee' into a.out
#    vganesan    05/28/14 - Check empty execution tracker and delete if needed
#    skkurapa    04/23/14 - XbranchMerge skkurapa_ps3_mos_fixes3 from
#                           st_opatchauto_pt-em12.1ps3mos
#    skkurapa    04/10/14 - Fix Bug#18509096,18372309
#    skkurapa    04/22/14 - Fix bug#18372230,18372297
#    skkurapa    03/25/14 - Add EMDROOT=<Platform home> to avoid user intervention of opatch
#				napply prompt for OCM configuration
#    skkurapa    02/27/14 - Support singleoms resume 
#    vganesan    02/10/14 - (1) Support master log file to record sessions of a script file
#                           (2) Support resume capability of bash scripts from point of execution
#                               of failure
#                           (3) Enhance script to store session state and retrieve state later to
#                               check for completeness
#    vganesan    11/25/13 - (1) Remove -analyze block (EM bootcamp feedbacks)
#                           (2) Get local host relayed from OPatchauto OMS code (correct way)
#                           	to get host
#                           (3) Make a.out script of napply - host, timestamp based naming convention
#                               so that parallel execution is allowed.
#                           (4) Support multi-oms scripts stage in user specified directory
#
#    vganesan    09/26/13 - Creation
# 

use strict;
use File::Spec();
use File::Path();
use File::Basename;
use Sys::Hostname;
use Cwd;
use English;

use constant S_EMPTY        => '';
use constant S_FALSE        => 'false';
use constant S_TRUE         => 'true';

use constant OPT_FLAG       => '-';
use constant OPT_ANALYZE    => 'analyze';
use constant OPT_HELP       => 'help';

use constant E_SUCCESS      => 0;
use constant E_FAIL         => 1 * (1 << 8);


my $OptFlag                 = OPT_FLAG;
my $OptAnalyze              = OPT_ANALYZE; 
my $OptHelp                 = OPT_HELP;


my $isInAnalyzeMode        = S_FALSE; 
my $parseFile              = S_EMPTY;

my $oracleHomePath = $ENV{'ORACLE_HOME'};
my $localHost = $ENV{'OMSPatcher.OMS_LOCAL_HOST'};
my $stageDir = $ENV{'OMSPatcher.OMS_SCRIPTS_DIR'};
my $sessionFile = $ENV{'OMSPatcher.MULTI_OMS_SESSION_FILE'};
my $masterLogFile = $ENV{'OMSPatcher.OMS_MASTER_LOG_FILE'};
my $singleomsResumeScript = $ENV{'OMSPatcher.SINGLEOMS_RESUME_SCRIPT'};
my $omsJavaPath = $ENV{'OMSPatcher.OMS_JAVA_PATH'};
my $isSilent = $ENV{'isSilent'};
my $propertyfile = $ENV{'propertyfile'};
my $operType = "";
my $isDowntime = "";
my $workingOH= "";
my $patchLocation		   = S_EMPTY;
my $isMultiOMS		   = S_EMPTY;
my $isNzdtMode		   = S_EMPTY;

# --------------------- Subroutines -------------------------------------
# parseArgs()
#
# Parse the args and store them for future use
#

sub parseArgs
{

    if ($#ARGV <= -1 || grep(/$OptFlag$OptHelp/, @ARGV))
    {
        help(); 
        exit E_SUCCESS;        
    }
	
	$workingOH=pop(@ARGV);
	$isNzdtMode=pop(@ARGV);
	$isMultiOMS=pop(@ARGV);
	$patchLocation = pop(@ARGV);
	$operType = pop(@ARGV);
	$isDowntime = pop(@ARGV);
    $parseFile = pop(@ARGV);

    if(!-e $parseFile)
    {
        print "Please make sure the steps file $parseFile exist.\n";
        help();
        exit E_FAIL;
    }
}


# help()
#
# To show help usage
#

sub help
{
    print "Usage:";
    print "PLATFORM_HOME/perl/bin/perl generateMultiOMSPatchingScripts.pl %filename% \n";    
    print "Example:\n" ;
    print "------------------------------------------\n";  
    print "PLATFORM_HOME/perl/bin/perl generateMultiOMSPatchingScripts.pl stepsfile.txt    \n";
    print "------------------------------------------\n";
}



# currentTimeStamp()
#
# Returns: current time stamp in YYYY-MM-DD_HH24-MM-SS SQL format
#

sub currentTimeStamp 
{
    my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime(time());
    my $date = sprintf(
        "%4d-%02d-%02d_%02d-%02d-%02d",
        ($year + 1900),
        ($mon + 1),
        $mday, $hour, $min, $sec
    );
    return $date;
}


##
# Write message to the command file
# $commandFile - bash script file that is being generated by the perl script.
# $msg - Message that would be written to the bash script file.
#
##

sub addMsg($$)
{
    my ($commandFile, $msg)=@_;   
    open(GFILE, ">>$commandFile")
    or die "Cannot write file because $commandFile not opened($!)";
    printf(GFILE "%s\n", $msg);
    close GFILE;
}


##
#
# Add error code, grep pattern for input command and send out the
# updated command.
# $command - input command
# $intermediateStep - Current step ID
# $previousStep -Previous step ID
# $sessionFile - File that holds the script that is being executed + steps' status
#
#
##
sub getUpdatedCommand($$$$)
{
    my ($command,$intermediateStep,$sessionFile,$previousStep)=@_;

    my $stepStatusPassCheck = "";
    my $stepStatusAnalyzePassCheck = "";
    my $codeAppend = "";

     
    if ($previousStep eq $intermediateStep)
    {
      $codeAppend = $codeAppend.$command;
    }
    else
    { 
         ## Check for Step status PASS or ANALYZED_PASS to verfy success   
         $stepStatusPassCheck =  "grep -l 'Step$intermediateStep=PASS\$' $sessionFile >/dev/null 2>&1 \n";
         $stepStatusAnalyzePassCheck =  "grep -l 'Step$intermediateStep=ANALYZED_PASS\$' $sessionFile >/dev/null 2>&1 \n";
         $codeAppend = $codeAppend.$stepStatusAnalyzePassCheck."RESULT=\$? \n".
                                        "    if [ \$RESULT != 0 ]; then \n".
                                        "    $stepStatusPassCheck ".
                                        "    RESULT=\$? \n".
                                        "    if [ \$RESULT != 0 ]; then \n".
                                        "        $command \n".
                                        "    else \n".
                                        "    echo \"SKIP command for step $intermediateStep...\"\n".
                                        "    fi \n".
                                        "    else \n".
                                        ## Tell bash script not to re-execute any command that has PASS or ANALYZED_PASS status 
                                        "       echo \"SKIP command for step $intermediateStep...\"\n".
                                        "    fi \n";
    }
   

    return $codeAppend;
}



##
# Method to pur right status in sessionFile after execution.
# $step - Current step ID
# $sessionFile - File that holds the script that is being executed + steps' status
# $previousStep - Previous step ID
# $isAnalyze - Is the step an analysis step? or update/deploy step?
#
##

sub getStatusBlockWithStepRecord($$$$)
{
   my ($step,$sessionFile,$previousStep,$isAnalyze)=@_;
         

   my $exitCodeAppendWithStepRecord;

   my $grepFailPattern = "";
   my $grepPassPattern = "";

   if ($isAnalyze eq "yes")
   {
     ## For analysis commands, we use ANALYZED_FAIL for failure, ANALZED_PASS for pass.
     ## Please note if it is N
     $grepFailPattern = "grep -l \"Step$step=\" $sessionFile | xargs $oracleHomePath/perl/bin/perl -pi -w -e 's/Step$step=.*/Step$step=ANALYZED_FAIL/g';\n"; 
     $grepPassPattern = "grep -l \"Step$step=\" $sessionFile | xargs $oracleHomePath/perl/bin/perl -pi -w -e 's/Step$step=.*/Step$step=ANALYZED_PASS/g';\n";
   }
   else
   {
     $grepFailPattern = "grep -l \"Step$step=\" $sessionFile | xargs $oracleHomePath/perl/bin/perl -pi -w -e 's/Step$step=.*/Step$step=FAIL/g';\n";
     $grepPassPattern = "grep -l \"Step$step=\" $sessionFile | xargs $oracleHomePath/perl/bin/perl -pi -w -e 's/Step$step=.*/Step$step=PASS/g';\n";
   }
 

   ## same step as previous step If result is a failure, put FAIL in bash script.
   ## Don't record PASS as we would have many more steps coming in the same ID and also, first command in the block
   ## would have already recorded it as PASS
   if ($step eq $previousStep)
   {
      $exitCodeAppendWithStepRecord = "RESULT=\$? \n".
                                "    if [ \$RESULT != 0 ]; then \n".
                                "        echo \"The command failed with error code \$RESULT\";  \n".
                                "        $grepFailPattern".
                                "        echo \"\n\nAll operations for this script are appended to log file: \"$masterLogFile\"\";\n".
                                "	chmod 660 $masterLogFile;\n".
                                "        return \${RESULT} \n".
                                "    fi\n";

 
   }
   else
   {
      ## Previous step <> current step. Please record PASS pattern in sessionFile for success, if it succeeded in bash    
      ## We record PASS if first command of the step block succeeds and delegate it other commands of the same block to
      ## to mark it FAILED, if they fail. If they too PASS, status is not changed.
      $exitCodeAppendWithStepRecord = "RESULT=\$? \n".
                                "    if [ \$RESULT != 0 ]; then \n".
                                "        echo \"The command failed with error code \$RESULT\";  \n".
                                "        $grepFailPattern".
                                "        echo \"\n\nScript execution has failed. Please refer to log file: \"$masterLogFile\" for more details\n".
				"        \nPlease fix the failures and re-run the same script to complete the patching session.\";\n".
                                "	chmod 660 $masterLogFile;\n".
                                "        return \${RESULT} \n".
                                "    else \n".
                                "	$grepPassPattern".
                                "    fi\n";


   }

   return $exitCodeAppendWithStepRecord;

}

##
# This method is for those commands for which user has pressed 'n' and hence stopped on request. 
# $step - Current step ID
# $sessionFile - File that holds the script that is being executed + steps' status
# $isAnalyze - Is the step an analysis step? or update/deploy step?
##
sub getReverseStatusBlockWithStepRecord($$$)
{
  my ($step,$sessionFile,$isAnalyze)=@_; 

  my $reverseExitCodeAppend = "";

  if ($isAnalyze eq "yes")
  {
    $reverseExitCodeAppend  =    "RESULT=\$? \n".
                                "    if [ \$RESULT == 0 ]; then \n".
                                "        grep -l \"Step$step=\" $sessionFile | xargs $oracleHomePath/perl/bin/perl -pi -w -e 's/Step$step=.*/Step$step=ANALYZED_FAIL/g';\n".
                                "    fi\n";


  }
  else
  {
    $reverseExitCodeAppend  =    "RESULT=\$? \n".
                                "    if [ \$RESULT == 0 ]; then \n".
                                "        echo \"The script can't proceed further as patching operations are not completed.\";  \n".
                                "        grep -l \"Step$step=\" $sessionFile | xargs $oracleHomePath/perl/bin/perl -pi -w -e 's/Step$step=.*/Step$step=FAIL/g';\n".
                                "        echo \"\n\nAll operations for this script are appended to log file: \"$masterLogFile\"\";\n".
                                "	chmod 660 $masterLogFile;\n".
                                "        return 1 \n".
                                "    fi\n";
  }

  return $reverseExitCodeAppend;
}


##
# This method appends error code block for generic command input.
# $command - Input command
#
##
sub commandErrorCode($)
{
  my ($command)=@_;

  my $commandErrorCodeAppend = "RESULT=\$? \n".
                                "    if [ \$RESULT != 0 ]; then \n".
                                "        echo \"The command \"$command\" failed with error code \$RESULT\";  \n".
                                "        return \${RESULT} \n".
                                "    fi\n";

  return $commandErrorCodeAppend; 
}

sub addErrorCode($)
{
  my ($command)=@_;

  my $commandErrorCodeAppend = "exe_tracker=$workingOH/.omspatcher_storage/oms_session/execution_tracker\n".
								"fileName=\$(grep session_file \$exe_tracker | awk -F= '{print \$2}')\n".
								"isInFile=\$(cat \$fileName | grep -c FAIL);\n".
								"if [ \$isInFile -gt 0 ]; then \n".
								"   exit\n".
								"fi\n";  

  return $commandErrorCodeAppend; 
}

##
# This method initializes Session file for the script. Please note that script in remote hosts are being exected
# without any initialization efforts from OPatchauto. This method will address those gaps.
#
# $sessionFile - File that holds the script that is being executed + steps' status 
# $markSteps - List of steps in the sessionFile separated by "\n"
# $commandFileName - The complete bash script path
# $oracleHomePath - Platform OMS home
# $masterLogFile - Log file of the session
#  
##
sub initializeSessionFile($$$$$)
{
	# need to pass the clonehome
  my ($sessionFile,$markSteps,$commandFileName,$oracleHomePath,$masterLogFile)=@_;

  ## execution tracker file that contains pointers to sessionFile and masterLogFile (of the session)
  my $execution_tracker = File::Spec->catfile($oracleHomePath,".omspatcher_storage","oms_session","execution_tracker");

  my $sessionFileCheck =

                       ## Create session file if it does not exist, populate sessionFile with script location
                       ## and Steps
		       "if [ ! -f \"$sessionFile\" ]; then \n".
                       "  echo \"Creating  session file \"$sessionFile\"...\"; \n". 
                       "  touch $sessionFile;   \n".
                       " " .commandErrorCode("touch $sessionFile")." ".
                       "  chmod 660 $sessionFile; \n".
                       "  echo \"script=$commandFileName\" > $sessionFile; \n".
                       "  echo \"$markSteps\" >> $sessionFile; \n".
                       "fi\n".

                       ## If there is a zero byte execution tracker file, delete it
                       "if [ -f \"$execution_tracker\" ] && [ ! -s \"$execution_tracker\" ]; then \n".
                       " rm $execution_tracker; \n".
                       "fi \n".

                       ## Create execution tracker file, it does not exist and populate with sessionFile and masterLogFile  
                       "if [ !  -f \"$execution_tracker\" ]; then \n".
                       "  touch $execution_tracker; \n".
                       " " .commandErrorCode("touch $execution_tracker")." ".
                       "  chmod 660 $execution_tracker; \n".
                       "  echo \"session_file=$sessionFile\" >> $execution_tracker;"."\n".
                       "  echo \"log_file=$masterLogFile\" >> $execution_tracker;"."\n". 
                       "  else\n".
                       ## If executon tracker file exists, get the old session file from execution tracker file 
                       "  OLD_SESSION_FILE=`grep \"session_file=\" $execution_tracker |  sed 's/session_file=//g'`\n".
                       " if [ \"$sessionFile\" != \"\$OLD_SESSION_FILE\" ]; then \n".
                       ## Does the session file contain both step=PASS & step= (empty)? If so, it is incomplete        
                       "grep -l \"Step.*=\$\" \$OLD_SESSION_FILE >/dev/null 2>&1 \n".
                       "MATCH1=\$?\n".
                       "grep -l \"Step.*=PASS\$\" \$OLD_SESSION_FILE >/dev/null 2>&1 \n".   
                       "MATCH2=\$?\n".
                       ## If old session file indicates incomplete session, error out and ask to complete the previous session.
                       "if [ \$MATCH1 -eq 0 ] && [ \$MATCH2 -eq 0 ]; then \n".
                       "active_script_path=`grep \"script=\" \$OLD_SESSION_FILE |  sed 's/script=//g'`\n".
                       "echo \"Previous session is not completed. To complete previous session, please execute script \$active_script_path\";\n".
                       "return 1; \n".
                       "fi \n".
                       ## Does the session file contain step=FAIL? If so, it needs to be re-executed   
                       "grep -l \"Step.*=FAIL\$\" \$OLD_SESSION_FILE >/dev/null 2>&1 \n".
                       "MATCH3=\$?\n".
                       ## If old session file indicates incomplete session, error out and ask to complete the previous session
                       "if [ \$MATCH3 -eq 0 ]; then \n".
                       "active_script_path=`grep \"script=\" \$OLD_SESSION_FILE |  sed 's/script=//g'`\n".
                       "echo \"Previous session is not completed. To complete previous session, please execute script \$active_script_path\";\n".
                       "return 1; \n".
                       "fi \n".
                       "fi \n".
                       ## Here, you can go ahead and revamp the executon tracker as no old session file is incomplete.     
                       "rm $execution_tracker; \n".
                       " " .commandErrorCode("rm $execution_tracker")." ".
                       "  touch $execution_tracker; \n".
                       " " .commandErrorCode("touch $execution_tracker")." ".
                       "  chmod 660 $execution_tracker; \n".
                       "  echo \"session_file=$sessionFile\" >> $execution_tracker;"."\n".
                       "  echo \"log_file=$masterLogFile\" >> $execution_tracker;"."\n". 
                       "fi \n".
                       ## Copy your current script file to exact $commandFileName location for future re-use, reference and getting out error msg for incomplete sessions
                       ## This is needed because irrespective of where the scripts are copied, the original reference place
                       ## from OPatchauto is recorded and loaded in sessionFile
                       " if [ ! -f \"\$OPATCHAUTO_SCRIPT_PATH\" ]; then \n".
                        "  mkdir -p \$OPATCHAUTO_SCRIPT_DIR; \n".
                        " ".commandErrorCode("mkdir -p \$OPATCHAUTO_SCRIPT_DIR")." ".
                        "  echo \"Copying your script to OMSPatcher defined path \"\$OPATCHAUTO_SCRIPT_PATH\"...\"; \n".
                        "cp \$CURRENT_SCRIPT_LOCATION/\$CURRENT_SCRIPT_NAME \$OPATCHAUTO_SCRIPT_PATH; \n".
                        " ".commandErrorCode("cp \$CURRENT_SCRIPT_LOCATION/\$CURRENT_SCRIPT_NAME \$OPATCHAUTO_SCRIPT_PATH")." ".
                       "fi \n"; 

  return $sessionFileCheck; 
}


# --------------------- Main program -----------------------------------

if($OSNAME =~ m#Win32#) 
{
#  print "\nThis script is not compatible to run on Windows.";
}


# parseArgs
parseArgs();


#1. read input file    

open(GFILE, "< $parseFile")
or die "Cannot open file ($!)";    
my @filecontent_list=<GFILE>;    
close GFILE;

    
#2.create output folder in PlatformHome/OPatch/.patch_storage/oms

if ( not exists ($ENV{'ORACLE_HOME'} ))  
{
  print "\n Platform home is not set by ORACLE_HOME environment variable.";
  exit E_FAIL;
}


my $workDir = "";
my $commandOutDir = "";


if ( $localHost eq "" ) 
{
  print "\n Proper local host address not provided by OMSPatcher\n";
  exit E_FAIL;
}

if ($stageDir eq "")
{
 # Fall back to default $OH/.opatchauto_patch_storage/oms_session
 $workDir = File::Spec->catdir($workingOH,".omspatcher_storage","oms_session"); 
}
else
{
  $workDir = $stageDir;
}

if ($sessionFile eq "")
{ 
   print "\nScript was not able to get valid session file from OMSPatcher.\n";
   exit E_FAIL;
}

if ($masterLogFile eq "")
{
   print "\nScript was not able to get valid session log file from OMSPatcher in the path\n";
   exit E_FAIL;
}


$commandOutDir =  File::Spec->catdir($workDir,"scripts_".currentTimeStamp());

if(not -e $commandOutDir)
{
    File::Path::mkpath( $commandOutDir );        

   if (not -e $commandOutDir) 
   {
     print "\nScript was not able to create $commandOutDir structure.\n";
     exit E_FAIL;
   } 
}                       

#3. parse    

## Host to script path map
my %hostToFileName   = ();
## Host to STEP<id>= as string map
my %hostToMarkSteps = ();
## Host to the actual step map
my %hostToSteps = ();
my @hostOrder;
## Script path for the host
my $commandFileName = "";
my $fileNum = 0;
my $markSteps = "";
my $hostName = "";
my $user = "";

my $exitCodeAppend = "RESULT=\$? \n".
                                "    if [ \$RESULT != 0 ]; then \n".
                                "        echo \"The script failed with error code \$RESULT\";  \n".
                                "        exit \${RESULT} \n".
                                "    fi";


my $reverseExitCodeAppend = "RESULT=\$? \n".
                                "    if [ \$RESULT == 0 ]; then \n".
                                "        echo \"The script can't proceed further as patching operations are not completed.\";  \n".
                                "        exit 1 \n".
                                "    fi";

my $recordPwd = "";
my $recordDbaPwd = "";
my $credHostName = "";
my $credUser = "";

## Loop once to know the exact host,user for which you need OMS SYSMAN credential prompt.
## Once that is found, we can inject this prompt into the specific bash script.

foreach my $line (@filecontent_list)
{
	if($recordPwd ne ""  && $recordDbaPwd ne "")
	{
	last;
	}
   if($line  =~ /Step.*On\s+host\s+(.*)\s+run\s+below\s+command.*as\s+user\s+\"(.*)\".*/ )
   {
      $credHostName = $1;
      $credUser = $2;
   }
   elsif ($line  =~ /^NOTE.*/ || $line  =~ /^\s+$/ )
   {
      next;
   }
   elsif ($line  =~ /-sysman_pwd %EM_REPOS_PASSWORD%/ || $line =~/register/ || $line =~ /deregister/ || $line =~ /NZDT_PREDOWNTIME_CREATE_EDITION/ || $line =~ /NZDT_DOWNTIME_ACTIVATE_MRS/ || $line =~ /NZDT_SUBMIT_JOB/ || $line =~ /MGMT_PATCH_POST_DOWNTIME_ACTION/ || $line =~ /MGMT_PATCH_POST_NZDT_ACTION/ || $line =~ /NZDT_ACQUIRE_LOCK/ || $line =~ /NZDT_RELEASE_LOCK/ || $line =~ /UPD_FLAG_VALUE/  || $line =~ /RESET_FLAG_VALUE/ )
   {
     $recordPwd = "echo \"\nPlease provide credential for OMS repository SYSMAN user: \"\nstty -echo\nread EM_REPOS_PASSWORD\nstty echo";
   }
   elsif ( $line =~ /applypatch/ || $line =~ /rollbackpatch/ || $line =~ /NZDT_DOWNTIME_SWITCH_EDITION/ || $line =~ /NZDT_DOWNTIME_RECOMPILE/)
   {
     $recordDbaPwd = "echo \"\nPlease provide credential for OMS repository SYS user: \"\nstty -echo\nread SYS_DBA_PASSWORD\nstty echo";
     ## break here, assumption is you have got credHostName, credUser  
    # last;
   }
   elsif ($line =~ /RESET_JOB_QUEUE/)
   {
      $recordPwd = "echo \"\nPlease provide credential for OMS repository SYSMAN user: \"\nstty -echo\nread EM_REPOS_PASSWORD\nstty echo";
      $recordDbaPwd = "echo \"\nPlease provide credential for OMS repository SYS user: \"\nstty -echo\nread SYS_DBA_PASSWORD\nstty echo";
  }
}

my $markStep = "";
my $stepMarker = "";
my $stepHostName = "";

# loop to mark the steps for each host.
# This is needed to know the contents of the steps (number of steps) for sessionFile
foreach my $line  (@filecontent_list)
{

  if($line  =~ /Step\s+(.*)\.\s+On\s+host\s+(.*)\s+run\s+below\s+command.*as\s+user\s+\"(.*)\".*/ )
  {
        $markStep = ""; 
        $stepMarker = $1;
        $stepHostName = $2;

        $markStep = "Step$stepMarker="."\n";

        if (!$hostToMarkSteps{$stepHostName})
        {
            $hostToMarkSteps{$stepHostName} = $markStep;
        }
        else
        {
            $hostToMarkSteps{$stepHostName} = $hostToMarkSteps{$stepHostName}.$markStep;
        } 

  }
  elsif($line  =~ /^NOTE.*/ || $line  =~ /^\s+$/ )
  {        
        next; 
  }
  else
  {
        if ($stepHostName ne "")
        {  
          $hostToSteps{$stepHostName} = $hostToSteps{$stepHostName}."Step $stepMarker: ".$line."\n";
        }
  }

}


## Current step in loop
my $intermediateStep;
## Previous step in loop
my $previousStep;
## Command + error code dressing
my $updatedCmd = "";
## Is analyze cmd?
my $addAnalyze = "";

foreach my $line (@filecontent_list)
{
  
    if($line  =~ /Step\s+(.*)\.\s+On\s+host\s+(.*)\s+run\s+below\s+command.*as\s+user\s+\"(.*)\".*/ )            
    {  
        ## Add to bash script, last loop's updated command
        if ($updatedCmd ne "")
        {
          addMsg($commandFileName,getUpdatedCommand($updatedCmd,$intermediateStep,$sessionFile,"")); 

          if ($addAnalyze ne "")
          {
             addMsg($commandFileName,$addAnalyze);
             $addAnalyze = ""; 
          } 

          $updatedCmd = "";  
        }  

        $previousStep = $intermediateStep;       
        $intermediateStep = $1; 
        $hostName = $2;  
        $user = $3;

        ## Script initialization for the host
       if(!exists $hostToFileName{$hostName})
	   #if ($hostName eq $localHost) 
        {
            $fileNum++;
            my $tmpfileName = $hostName;        
            $tmpfileName =~ s/\./_/g;
            $commandFileName = File::Spec->catfile($commandOutDir, "run_script#".$fileNum."_on_host_".$tmpfileName."_as_user_".$user.".sh");
      
            if ($singleomsResumeScript ne "" && $hostName eq $localHost)
	    {
	    	$commandFileName = $singleomsResumeScript;	   	    
	    }		
		
            $hostToFileName{$hostName} = $commandFileName;   
            $hostOrder[$fileNum] = $hostName;

            if(!-e $commandFileName)
            {   
                addMsg($commandFileName, "#!/bin/bash");            

                ## Add timestamp module	
                addMsg($commandFileName,"\ntimestamp() \n{ \ndate +\"%F %T\" \n}\n"); 

                ## Add filter module
                addMsg($commandFileName,"\nfilter() \n{ \nwhile IFS= read -r line; do \n echo \"\$line\" | tee -a \"\$1\" \n done \n } \n");

                ## Add status module
                addMsg($commandFileName,"\nstdintoexitstatus() \n{ \nread exitstatus \n return \$exitstatus \n } \n");

                ## Add napply/nrollback execution module - Need this to trap error code of 'opatch napply/nrollback' without 'tee'
                addMsg($commandFileName,"\nexecuteBinaryOperation() \n { \n \$1 \n }  \n");

                ## Add mainProg module
                addMsg($commandFileName,"\nmainProg() \n { \n");
	
				## setup EMDROOT to platfrom home to prevent OCM configure prompt
				addMsg($commandFileName,"\nexport EMDROOT=$oracleHomePath"); 

                ## Embed the host for which the script is created
                addMsg($commandFileName,"\nEMBEDDED_HOST_NAME=$hostName");

                my $opatchauto_script_dir = dirname($commandFileName);
                ## Record the current script location & name
                addMsg($commandFileName,"\nCURRENT_SCRIPT_NAME=`basename \$0`");
                addMsg($commandFileName,"\nCURRENT_SCRIPT_LOCATION=`dirname \$0`");
                ## Record the OPatchauto script directory and file path path  
                addMsg($commandFileName,"\nOPATCHAUTO_SCRIPT_PATH=$commandFileName");
                addMsg($commandFileName,"\nOPATCHAUTO_SCRIPT_DIR=$opatchauto_script_dir");

                addMsg($commandFileName,"\n");
                my $timeStamp = currentTimeStamp();

                ## OMS patch storage
                my $patch_storage = File::Spec->catdir($workingOH,".omspatcher_storage");

                ## Session directory
                my $oms_session = File::Spec->catdir($workingOH,".omspatcher_storage","oms_session");


                ## Create patch storage, if it does not exist
                addMsg($commandFileName,"if [ !  -d \"$patch_storage\" ]; then \n".
                       "  mkdir $patch_storage; \n".
                       " " .commandErrorCode("mkdir $patch_storage")." ".
                       "fi \n");

                ## Create OMS session directory, if it does not exist
                addMsg($commandFileName,"if [ !  -d \"$oms_session\" ]; then \n".
                       "  mkdir $oms_session; \n".
                       " " .commandErrorCode("mkdir $oms_session")." ".
                       "fi \n");


                ## Create Master log file of the session, if it does not exist
                addMsg($commandFileName, "if [ ! -f \"$masterLogFile\" ]; then \n".
                                         "  echo \"Creating  master log file \"$masterLogFile\"...\"; \n".
                                         "  touch $masterLogFile;   \n".
                                         " " .commandErrorCode("touch $masterLogFile")." ".
                                         "  chmod 660 $masterLogFile; \n".
                                         "fi\n");

   

                addMsg($commandFileName,"\n echo \"\n\nStart of new session at \$(timestamp)\" >> $masterLogFile");
                addMsg($commandFileName,"\n echo \"-------------------------------------------\" >> $masterLogFile\n\n");


                addMsg($commandFileName,"\n$omsJavaPath -cp $oracleHomePath/OMSPatcher/jlib/oracle.omspatcher.classpath.jar:.: oracle.opatchauto.oms.OMSHostInterfaceChecker $hostName");
                addMsg($commandFileName,"\n".$exitCodeAppend);

                addMsg($commandFileName,
			initializeSessionFile($sessionFile,$hostToMarkSteps{$hostName},$commandFileName,$workingOH,$masterLogFile));
                addMsg($commandFileName,"\n".$exitCodeAppend);


                my $stepsToRecord = $hostToSteps{$hostName};
                 ## Announce all commands to be executed for this session in stdout as well as in master log file 
                addMsg($commandFileName,"\n echo \"Execute Commands:\" >> $masterLogFile\n\n"."echo \"$stepsToRecord\n\n\" >> $masterLogFile"."\n\n");
 
                if ((($credHostName eq $hostName) && ($credUser eq $user)) || $recordPwd ne "")
                { 
                    addMsg($commandFileName, $recordPwd);
                    addMsg($commandFileName, "\n".$exitCodeAppend);
                }              
				if ((($credHostName eq $hostName) && ($credUser eq $user)) || $recordDbaPwd ne "")
                { 
                    addMsg($commandFileName, $recordDbaPwd);
                    addMsg($commandFileName, "\n".$exitCodeAppend);
                } 
            }

        }        

        $commandFileName =  $hostToFileName{$hostName};

      
    }elsif($line  =~ /^NOTE.*/ || $line  =~ /^\s+$/ )

    {
        next;
    }elsif($fileNum > 0)

    {
        my $bufferline = $line;
        #input password for applypatch|rollbackpatch command

        if( $line =~/register/ || $line =~ /deregister/ || $line =~ /NZDT_PREDOWNTIME_CREATE_EDITION/ || $line =~ /NZDT_DOWNTIME_ACTIVATE_MRS/ || $line =~ /NZDT_SUBMIT_JOB/ || $line =~ /NZDT_ACQUIRE_LOCK/ || $line =~ /NZDT_RELEASE_LOCK/ || $line =~ /MGMT_PATCH_POST_DOWNTIME_ACTION/ || $line =~ /MGMT_PATCH_POST_NZDT_ACTION/ || $line =~ /UPD_FLAG_VALUE/ || $line =~ /RESET_FLAG_VALUE/ || $line =~ /-force -oms_name/)
        {
            $line = "echo \$EM_REPOS_PASSWORD \| ".$line;        
		}elsif ($line =~ /applypatch/ || $line =~ /rollbackpatch/ ||$line =~ /NZDT_DOWNTIME_SWITCH_EDITION/ || $line =~ /NZDT_DOWNTIME_RECOMPILE/ ) {
            $line = "echo \$SYS_DBA_PASSWORD \| ".$line;
		}elsif ($line =~ /RESET_JOB_QUEUE/){
            $line = "echo \$EM_REPOS_PASSWORD \$SYS_DBA_PASSWORD \| ".$line;        
		}

        $bufferline =~ s/[\n\r]//g;
        if ($line !~ /scp\s+-r/)
        {
           addMsg($commandFileName, "\necho \"Command to execute (Step $intermediateStep): ". $bufferline."\"\n");
        }  
        
        #replace %EM_REPOS_PASSWORD% to the password var of $EM_REPOS_PASSWORD
        $line =~ s/-sysman_pwd %EM_REPOS_PASSWORD%/-sysman_pwd \$EM_REPOS_PASSWORD/g;

        if ($line !~ /scp\s+-r/) 
        {
          if ($line =~ /opatch\s+napply/ || $line =~ /opatch\s+nrollback/)
          {
             my $time = currentTimeStamp();
             $line =~ s/[\n\r]//g;
             my $tempOutLoc = $hostName.$time.".a.out";
          
             
             #$updatedCmd = $updatedCmd.getUpdatedCommand($line." | tee $tempOutLoc;",$intermediateStep,$sessionFile,$previousStep);   
             $updatedCmd = $updatedCmd.getUpdatedCommand("\n((((executeBinaryOperation \"$line\" ;echo \$? >&3) | filter $tempOutLoc >&4) 3>&1) | stdintoexitstatus) 4>&1 ",$intermediateStep,$sessionFile,$previousStep);
             $updatedCmd = $updatedCmd."\n".getStatusBlockWithStepRecord($intermediateStep,$sessionFile,$previousStep,"")."\n"." grep  'OPatch stopped on request\\|OPatch failed' $tempOutLoc >/dev/null 2>&1;\n".getReverseStatusBlockWithStepRecord($intermediateStep,$sessionFile,"no")."\n";
             $updatedCmd = $updatedCmd . " rm $tempOutLoc \n";
          }
          else
          {
             $updatedCmd = $updatedCmd.getUpdatedCommand($line,$intermediateStep,$sessionFile,$previousStep);

             if ($line =~ /omspatcher\s+checkApplicable/)
             {
               ## This is an analyze command, hence, 'yes' is passed as argument
               $updatedCmd = $updatedCmd."\n".getStatusBlockWithStepRecord($intermediateStep,$sessionFile,$previousStep,"yes");
             }
             else
             {
               $updatedCmd = $updatedCmd."\n".getStatusBlockWithStepRecord($intermediateStep,$sessionFile,$previousStep,"");
             }
          }  
        }         
        else
        {
          # Get the patch directory to be copied
          if( $line =~ /.*mkdir\s+-p\s+(.*);scp\s+-r(.*):(.*)\s+(.*)\s.*/i )
          {
             my $substring = $3;
             my $bufferline = $line;
             $bufferline =~ s/[\n\r]//g;

	     my $tempStr = ".omspatcher_storage";

             # Copy automation files to the remote hosts to support invoking omspatcher rollback from any node.
	     # automation files are present in patch_top location and these are not stored implicitly on remote hosts.
	     # Without these automation files rollback cannot done from remote nodes.
 	     if (index($line,$tempStr) != -1){
               my $buff = "\nif [ !  -d \"$substring\" ]; then".
                          "\n\n  echo \"The Patch backup location $substring does not exists in the machine (this could mean that the patch automation data are not present on this host and which is mandatory to rollback this patch later from this host). You need to provide host credential to copy it. \"".
		"\nelse\n".
		"if [ -f \"$substring/etc/xml/GenericActions.xml\" ]; then\n".
			"chmod 777 \"$substring/etc/xml/GenericActions.xml\"\n".
	        "fi\n".	 
		"if [ -f \"$substring/etc/xml/ShiphomeDirectoryStructure.xml\" ]; then\n".
                        "chmod 777 \"$substring/etc/xml/ShiphomeDirectoryStructure.xml\"\n".
		"fi\n".          
		"\nfi\n";
		
    	       $updatedCmd = $updatedCmd . "\n" . $buff;
	       
	       my $addSCPUpdate = getUpdatedCommand($line,$intermediateStep,$sessionFile,$previousStep);

               $updatedCmd = $updatedCmd . "\n" .  "echo \"Executing command: ". $bufferline."\"\n". $addSCPUpdate . "\n".getStatusBlockWithStepRecord($intermediateStep,$sessionFile,$previousStep,"yes"). "\n";
             }
	     else {
               my $addPromptMsg =
               "\n yn='y'". 
               "\nif [ -d \"$substring\" ]; then".
		  "\necho \"The System Patch directory $3 already exists in the machine (this could mean that System Patch is already downloaded). Do you want to overwrite it (y|n)?  \"".
                    "\nread yn".
                    "\n  case \"\${yn}\" in".
        	    "\n[Yy]* ) echo \"\nUser provided \"\${yn}\" for patch transfer. Starting patch transfer...\";;".
        	    "\n[Nn]* ) echo \"\nUser provided \"\${yn}\" for patch transfer. Ignoring patch transfer...\";;".
        	    "\n * ) echo \"\nWrong input for prompt (or) timed out. Please run the script again.\"". 
                            "\nexit 1;;".
                      "\nesac".
               "\nfi\n"; 


               addMsg($commandFileName,$addPromptMsg);
               my $addSCPUpdate = getUpdatedCommand($line,$intermediateStep,$sessionFile,$previousStep); 
             
               $updatedCmd = "if [  \"\${yn}\" = \"y\"  ]; then \n". "echo \"Executing command: ". $bufferline."\"\n". $addSCPUpdate . "\n".getStatusBlockWithStepRecord($intermediateStep,$sessionFile,$previousStep,"yes")."\nelse\n". "grep -l \"Step$intermediateStep=\" $sessionFile | xargs $oracleHomePath/perl/bin/perl -pi -w -e 's/Step$intermediateStep=.*/Step$intermediateStep=ANALYZED_FAIL/g';\n"."\nfi";   
             }	
           }
        } 
        if($line =~ /omspatcher\s+checkApplicable/)
        {
          $addAnalyze =
            "\nwhile getopts \":a\" "."OPTION;do\n".
                "case \"\${OPTION}\" in\n".
                  "\*  \) [ \$OPTIND -ge 1 ] && optind=\$(expr \$OPTIND - 1 ) || optind=\$OPTIND\n".
                  "eval OPTION=\"\\\$\$optind\"\n".
                  "OPTION=\$(echo \$OPTION)\n".
                  "case \$OPTION in\n".
                    "-analyze)\n".
                       "echo Patching script stops here on this host as '-analyze' option is given.\n".
                       "exit 0;;\n".
                    "\* \)\n".
                       "echo Invalid arguments \"\$OPTION\" given. Please check script inputs. Script failed to proceed further.\n".
                       "exit 1;;\n".
                    "esac\n".
                 "esac\n".
            "done\n";
        }
        ## Now if this is the same in next turn for this block, it is exactly same step reference. 
        $previousStep = $intermediateStep;
    }
}

## After loop breaks, any remnants of updatedCommand? Please add it here
if ($updatedCmd ne "")
{
   addMsg($commandFileName,getUpdatedCommand($updatedCmd,$intermediateStep,$sessionFile,""));

   if ($addAnalyze ne "")
   {
      addMsg($commandFileName,$addAnalyze);
      $addAnalyze = "";
   }
  $updatedCmd = "";
}


# Set script permission to 710
foreach my $hostName (keys %hostToFileName) {
    my $scriptLoc = $hostToFileName{$hostName};
    chmod 0770, $scriptLoc;
}

if ($singleomsResumeScript eq "") 
{
    print "\nPlease perform the following steps to complete patching operations.\n";
    print   "-------------------------------------------------------------------";
}

my $count = 0;
for my $hostName (@hostOrder)
{
  #if (exists $hostToFileName{$hostName})
  if ($hostName eq $localHost) 
  {
    chmod 0770,$hostToFileName{$hostName};
    addMsg($hostToFileName{$hostName},"echo \"\n\nAll operations for this script are appended to log file: \"$masterLogFile\"\";\n");
    addMsg($hostToFileName{$hostName},"chmod 660 $masterLogFile;\n");
    ## End "}" here for main program
    addMsg($hostToFileName{$hostName},"\n} ");
  
    addMsg($hostToFileName{$hostName},"\n((((mainProg;echo \$? >&3) | filter $masterLogFile >&4) 3>&1) | stdintoexitstatus) 4>&1"); 

    addMsg($commandFileName,addErrorCode("resume"));
	 my $predowntimeFile = File::Spec->catfile($oracleHomePath,"predowntime_success");

   ## adding code to run the downtime
   
	if ($isDowntime eq "false" && $isNzdtMode eq "true") {
		addMsg($hostToFileName{$hostName},"\ntouch $predowntimeFile");
		if ($operType eq "apply") {
			#OMSPatcher/omspatcher apply /scratch/nzdt_134/patches/31882382 -oh <omshome> -sys_pwd <pwd> -sysman_pwd <pwd>
			if ($isSilent eq "true") {
				addMsg($hostToFileName{$hostName},"echo $oracleHomePath/OMSPatcher/omspatcher apply $patchLocation -oh $oracleHomePath -nzdt -silent -property_file $propertyfile;");
				addMsg($hostToFileName{$hostName},"\n$oracleHomePath/OMSPatcher/omspatcher apply $patchLocation -oh $oracleHomePath -nzdt -silent -property_file $propertyfile");
			}
			else{
				addMsg($hostToFileName{$hostName},"echo $oracleHomePath/OMSPatcher/omspatcher apply $patchLocation -oh $oracleHomePath -nzdt;");
				addMsg($hostToFileName{$hostName},"\n$oracleHomePath/OMSPatcher/omspatcher apply $patchLocation -oh $oracleHomePath -nzdt");
			}
		 my $org_execution_tracker = File::Spec->catfile($oracleHomePath,".omspatcher_storage","oms_session","execution_tracker");
		 my $execution_tracker = File::Spec->catfile($workingOH,".omspatcher_storage","oms_session","execution_tracker");

		 addMsg($hostToFileName{$hostName},"\nrm $execution_tracker; \n");
		 addMsg($hostToFileName{$hostName},"\nrm $org_execution_tracker; \n");
		}		
	}
	else{
		my $resumeFile = File::Spec->catfile($oracleHomePath,".omspatcher_storage",".resume.properties");

		addMsg($commandFileName,addErrorCode("resume"));

		addMsg($hostToFileName{$hostName},"\nrm -rf $predowntimeFile");
		addMsg($hostToFileName{$hostName},"\nrm -rf $resumeFile");
		
		## add the code in case of multi oms but in single oms mode.
		if ($isMultiOMS eq "true") {
			## addMsg($hostToFileName{$hostName},"\necho $EM_REPOS_PASSWORD | $oracleHomePath/perl/bin/perl $oracleHomePath/OMSPATCHER/oms/script/runAddoms.pl $oracleHomePath $operType $patchLocation"); 
			my $scriptFDir = "scripts_".currentTimeStamp();
			   addMsg($commandFileName,"\n$oracleHomePath/perl/bin/perl $oracleHomePath/OMSPatcher/oms/createAddOMSScript.pl $oracleHomePath $operType $patchLocation $scriptFDir");
                           addMsg($commandFileName,"\necho Successfully created the add oms script.");
			   addMsg($commandFileName,"\necho Successfully executed resume script.");

		}
	}
    my $fileName = basename($hostToFileName{$hostName});
    $count = $count + 1;
    if ($singleomsResumeScript eq "")
    {
    	if ($localHost eq $hostName)
    	{
   	   	print "\t\n\t$count. Please execute the script \"$hostToFileName{$hostName}\" on local host.";
    	}
	else
    	{
                if ($stageDir eq "" || $stageDir =~ /^$oracleHomePath/)
                {
      		  print "\t\n\t$count. Please copy the script \"$hostToFileName{$hostName}\" to \"$hostName\" and execute the script on host \"$hostName\"."; 
                }
                else
                {
                  print "\t\n\t$count. Please execute the script \"$hostToFileName{$hostName}\" on host \"$hostName\".";
                }
    	}
    }	
  }
}
