#!/usr/local/bin/perl
# 
# $Header: emdb/sysman/webapps/em/WEB-INF/perl/config/asmConfig.pl /st_emdbsa_11.2/4 2009/06/15 17:29:05 hasriniv Exp $
#
# asmConfig.pl
# 
# Copyright (c) 2008, 2009, Oracle and/or its affiliates. All rights reserved. 
#
#    NAME
#      asmConfig.pl - <one-line expansion of the name>
#
#    DESCRIPTION
#      <short description of component this file declares/defines>
#
#    NOTES
#      <other useful comments, qualifications, etc.>
#
#    MODIFIED   (MM/DD/YY)
#    hasriniv    09/02/08 - Creation
# 
require "emd_common.pl";
require "$ENV{EMDROOT}/sysman/admin/scripts/db/dbclone/clone_util.pl";
require "$ENV{EMDROOT}/sysman/admin/scripts/db/dbclone/db_clone.pl";

use strict;
use File::Basename;
use File::Copy;
use File::Temp qw/ tempfile tempdir /;
use File::stat;
use vars qw/$hostUserID $OS $NT $S $TEMP $CP $MV $PS $DF $DELIMITER $Registry/;

$ENV{EMAGENT_PERL_TRACE_LEVEL} = 0;  #DEBUG level.

# Global variables
$hostUserID = "";

##########################################################
## Create single instance ASM 
##########################################################
sub createSIAsm
{
  EMD_PERL_DEBUG("asmConfig.createSIAsm(): Start");
  my ($asmHome, $tnsAdmin, $asmSysPassword, $diskString, $listener) = @_;
  
  EMD_PERL_DEBUG("asmConfig.createSIAsm(): asmHome: $asmHome");
  EMD_PERL_DEBUG("asmConfig.createSIAsm(): tnsAdmin: $tnsAdmin");
  EMD_PERL_DEBUG("asmConfig.createSIAsm(): diskString: $diskString");
  EMD_PERL_DEBUG("asmConfig.createSIAsm(): listener: $listener");
  
  &set_env_var($asmHome, "");
  $ENV{TNS_ADMIN} = "$tnsAdmin";
  # Call dbca and create asm
  my($dbcaexec) = "$asmHome${S}bin${S}dbca";
  
  #redirect output to a temp file
  (my $fh, my $filename) = &create_temp_file();
  if($NT)
  {
    $filename = "$TEMP\\"."asmconfig.$$";
  }
  EMD_PERL_DEBUG("asmConfig.createSIAsm(): Temp pfile: $filename");
  my($cmd) = "$dbcaexec -silent -configureASM -asmSysPassword \'$asmSysPassword\' -diskString \'$diskString\' -listeners \'$listener\'";
  EMD_PERL_DEBUG("asmConfig.createSIAsm(): Command: $cmd");
  my(@res) = `$cmd >$filename 2>&1`;
  
  my $mesg = getOutputFromFile($filename);
  print STDOUT $mesg;
  if($?)
  {
      EMD_PERL_DEBUG("asmConfig.createSIAsm(): dbca command failed");
      exit(1);
  }
  close $fh;
  if($NT)
  {
      &removeFile($filename);
  }
        
  return;
}



##########################################################
## Get Disk List using kfod 
##########################################################
sub getDisklist
{
  EMD_PERL_DEBUG("asmConfig.getDisklist(): Start");
  my ($oracleHome, $asmDiskString, $isVersionLessThan102) = @_;
  
  EMD_PERL_DEBUG("asmConfig.getDisklist(): oracleHome: $oracleHome");
  EMD_PERL_DEBUG("asmConfig.getDisklist(): asmDiskString: $asmDiskString");
  EMD_PERL_DEBUG("asmConfig.getDisklist(): isVersionLessThan102: $isVersionLessThan102");
  set_env_var($oracleHome, "");
  my $kfodExe = "kfod";
  if($NT)
  {
     $kfodExe = "kfod.exe"
  }
  my($kfodexec) = "$oracleHome${S}bin${S}$kfodExe";
  if(! -e "$kfodexec")
  {
    $kfodexec = "$oracleHome${S}rdbms${S}bin${S}$kfodExe"; 	
  }
  #redirect output to a temp file
  (my $fh, my $filename) = &create_temp_file();
  if($NT)
  {
    $filename = "$TEMP\\"."asmconfig.$$";   
  }
  EMD_PERL_DEBUG("asmConfig.getDisklist(): Temp pfile: $filename");
  my($cmd) = ""; 
  my $origOsmInstallSetting =  $ENV{"OSM_INSTALL_TEST"};

  if ($origOsmInstallSetting ne "")
  {
    $ENV{"OSM_INSTALL_TEST"} = "";
    if ($isVersionLessThan102 eq "TRUE")
    {
        $cmd = "$kfodexec nohdr=TRUE OP=DISKS _asm_allow_only_raw_disks=FALSE asm_diskstring='$asmDiskString'";
    }
    else
    {
        $cmd = "$kfodexec nohdr=TRUE OP=DISKS disks=all status=true _asm_allow_only_raw_disks=FALSE asm_diskstring='$asmDiskString'";
    }
    EMD_PERL_DEBUG("asmConfig.getDisklist(): Command: $cmd");
    my(@res) = `$cmd >$filename 2>&1`;
    $ENV{"OSM_INSTALL_TEST"} = $origOsmInstallSetting;
  }
  else
  { 
    if ($isVersionLessThan102 eq "TRUE")
    {
        $cmd = "$kfodexec nohdr=TRUE OP=DISKS asm_diskstring='$asmDiskString'";
    }
    else
    {
        $cmd = "$kfodexec nohdr=TRUE OP=DISKS disks=all status=true asm_diskstring='$asmDiskString'";
    }
    EMD_PERL_DEBUG("asmConfig.getDisklist(): Command: $cmd");
    my(@res) = `$cmd >$filename 2>&1`;
  }
  my $mesg = getOutputFromFile($filename);
  print STDOUT $mesg;
  if($?)
  {
      EMD_PERL_DEBUG("asmConfig.getDisklist(): kfod command failed");
      exit(1);
  }
  close $fh;
  if($NT)
  {
      &removeFile($filename);
  }
        
  return;
}

# check if the Oracle Home is valid.
# validateOracleHome(oracleHome)
sub validateOracleHome
{
  my $oracleHome = $_[0];
  EMD_PERL_DEBUG("asmConfig.validateOracleHome(): the given oracleHome: $oracleHome");

  my $dirExist = dirExists($oracleHome);
  if( $dirExist ne "OK")
  {
    EMD_PERL_DEBUG("asmConfig.validateOracleHome(): Invalid directory: $oracleHome");
    return "NOK1";
  }

  set_env_var($oracleHome, "");
  my $nls_lang = $ENV{NLS_LANG};
  $ENV{NLS_LANG} = 'American_America.al32utf8';


  #use sqlplus to detect if this is a valid Oracle Home

  my @versionString = `$oracleHome/bin/sqlplus -v 2>&1`;
  my $versionString = "@versionString";
  EMD_PERL_DEBUG("asmConfig.validateOracleHome(): versionString: $versionString");

  my $sqlplusPos = index($versionString, "SQL*Plus:");
  if( $sqlplusPos < 0)
  {
    EMD_PERL_DEBUG("asmConfig.validateOracleHome(): Bad SQLPLUS in Oracle Home: $oracleHome");
    return "NOK3";
  }
  
  if(defined($nls_lang))
  {
    $ENV{NLS_LANG} =$nls_lang;
  }

  #the length of Recovery Manager: plus a space is 18
  my $sqlVersion = trim(substr($versionString, $sqlplusPos + 18, 11));
  EMD_PERL_DEBUG("asmConfig.validateOracleHome(): sqlVersion: $sqlVersion");


  if(&compareVer($sqlVersion, "10.1.0.0") < 0)
  {
    EMD_PERL_DEBUG("asmConfig.validateOracleHome(): $sqlVersion is not supported to create ASM.");
    return "NOK4";
  }
 if (&compareVer($sqlVersion, "11.2.0.0") > -1 )
 {
   my $asmcaExe = "asmca";
   if($NT)
   {
     $asmcaExe = "asmca.bat"
   }
   if(! -e "$oracleHome/bin/$asmcaExe")
   {
    EMD_PERL_DEBUG("asmConfig.validateOracleHome(): ASMCA executable file does not exist in Oracle Home: $oracleHome");
    return "NOK2";
   }
 }
 else
 {
   my $dbcaExe = "dbca";
   if($NT)
   {
     $dbcaExe = "dbca.bat"
   }
   if(! -e "$oracleHome/bin/$dbcaExe")
   {
    EMD_PERL_DEBUG("asmConfig.validateOracleHome(): DBCA executable file does not exist in Oracle Home: $oracleHome");
    return "NOK2";
   }
 } 

  my $dbs = 'dbs';
  if($NT)
  {
    $dbs = 'database';
  }
  my $dbsLocation = ${oracleHome}.${S}.${dbs};
  my $wPermission = dirWritePermission($dbsLocation);
  if( $wPermission ne "OK")
  {
    EMD_PERL_DEBUG("asmConfig.validateOracleHome(): No write permission in: $dbsLocation");
    return "NOK5";
  }

  EMD_PERL_DEBUG("asmConfig.validateOracleHome(): Verification success");
  return $sqlVersion;
}
# Check if an Oracle process(es) is running for the specified instance.
# This check is done for linux only. For NT, it calls serviceCheck().
# Return OK if the process does not exist, otherwise, return NOK.
# Need to pass in OracleHome for NT, otherwise, it returns agent's TNS_ADMIN
# instanceCheck(OracleHome)
sub asmInstanceCheck
{
  EMD_PERL_DEBUG("asmConfig.asmInstanceCheck(): *** START ***");
  my $oracleHome = $_[0];
  my($line);

  if($OS ne "NT")
  {
    EMD_PERL_DEBUG("asmConfig.asmInstanceCheck(): Linux platform");
    #Check: Look for running processes for this instance
    my(@res) = `$PS -ef 2>&1`;
    if(@res > 1)
    {
      EMD_PERL_DEBUG("asmConfig.asmInstanceCheck(): examining process listing");
      foreach $line (@res)
      {
	chop($line);
	if ($line =~ /asm_.+_\+ASM$/)
        {
          EMD_PERL_DEBUG("asmConfig.asmInstanceCheck(): found process for +ASM: $line");
          EMD_PERL_DEBUG("asmConfig.asmInstanceCheck(): *** END ***");
	  return "NOK";
	}
      }
    }
    EMD_PERL_DEBUG("asmConfig.asmInstanceCheck(): NOT found process for +ASM");
  }
  else
  {
    EMD_PERL_DEBUG("asmConfig.asmInstanceCheck(): OS type is $OS");
    if($NT)
    {
      EMD_PERL_DEBUG("asmConfig.asmInstanceCheck(): NT platform");
      return asmServiceCheck("+ASM", $oracleHome);
    }
  }

  EMD_PERL_DEBUG("asmConfig.asmInstanceCheck(): *** END ***");
  return "OK";
}

# For Linux, add an entry to oratab for asm instance.
# addEntryAsmToOratab()
sub addAsmEntryToOratab
{
  EMD_PERL_DEBUG("asmConfig.addEntryAsmToOratab(): *** START ***");

  if(!$NT)
  {
    my($oratab) = getOratab();
    EMD_PERL_DEBUG("asmConfig.addEntryAsmToOratab(): Adding an entry to oratab: $oratab");

     #temp handle no oratab case
    if($oratab eq "")
    {
      EMD_PERL_DEBUG("asmConfig.addEntryAsmToOratab(): oratab does not exist");
      return;
    }

    if (-w "$oratab")
    {
      my $oracleHome = $ENV{ORACLE_HOME};
      my $instance = "+ASM";     #$ENV{ORACLE_SID};
      EMD_PERL_DEBUG("asmConfig.addEntryAsmToOratab(): Examining oratab file for instance $instance");

      #if an instance with the same name is running, abort 
      my $chkStatus = &asmInstanceCheck($oracleHome);
      if($chkStatus eq "NOK")
      {
        EMD_PERL_ERROR("asmConfig.addEntryAsmToOratab(): Could not add $instance to $oratab");
        my(@res) = `$PS -ef 2>&1`;
        my $line;
        if(@res > 1)
        {
          print STDOUT "$PS -ef | grep ora_ | grep _+ASM\n";
          foreach $line (@res)
          {
            chop($line);
            if ($line =~ /asm_.+_\+ASM$/)
            {
              print STDOUT "$line\n";
            }
          }
        }
        exit(1);
      }

      #Allow reusing existing SID
      $chkStatus = &oratabCheck("\\+ASM");
      if($chkStatus eq "NOK")
      {
        #comment out the original line
        &commentOutEntryInOratab("\\+ASM");
      }

      open(ORATAB, ">>$oratab") || die "Cannot open $oratab";

      #Append the new entry to oratab file
      print ORATAB "${instance}:${oracleHome}:N\n";

      close ORATAB || die "Cannot close $oratab";
		}
    else
    {
      EMD_PERL_ERROR("asmConfig.addEntryAsmToOratab(): NO write permission to file $oratab");
      #should we add the entry to a temp file and notify users?
    }
	}
  else
  {
    EMD_PERL_DEBUG("asmConfig.addEntryAsmToOratab(): NT platform, no need to add entry to oratab");
  }

  EMD_PERL_DEBUG("asmConfig.addEntryAsmToOratab(): *** END ***");
}

# Check if asm service already exists for the specified instance.
# This check is done for NT only.
# Return OK if the service does not exist, otherwise, return NOK.
# serviceCheck(inst, OracleHome)
sub asmServiceCheck
{
  EMD_PERL_DEBUG("asmConfig.asmServiceCheck(): *** START ***");
  my($instance) = $_[0];
  my $passedInOracleHome = $_[1];

  if($NT)
  {
    #Check: Look for existing service for this instance by checking the registry.

    my $oracleHome = $ENV{ORACLE_HOME};
    if(defined($passedInOracleHome))
    {
      $oracleHome = $passedInOracleHome;
    }
    EMD_PERL_DEBUG("asmConfig.asmServiceCheck(): Oracle Home: $oracleHome, Instance: $instance");

    my $service_value= $Registry->{"LMachine${S}System${S}CurrentControlSet${S}Services${S}OracleASMService${instance}${S}ImagePath"}
      or EMD_PERL_DEBUG("Can not find the service for LMachine${S}System${S}CurrentControlSet${S}Services${S}OracleASMService${instance}${S}ImagePath: $^E");
    if(!defined $service_value)
    {
      EMD_PERL_DEBUG("asmConfig.asmServiceCheck(): NOT found service for $instance");
      EMD_PERL_DEBUG("asmConfig.asmServiceCheck(): *** END ***");
      return "OK";
    }

    EMD_PERL_ERROR("asmConfig.asmServiceCheck(): Found service for $instance !!!");
    EMD_PERL_DEBUG("asmConfig.asmServiceCheck(): registry value: $service_value");
    EMD_PERL_DEBUG("asmConfig.asmServiceCheck(): *** END ***");
    return "LMachine${S}System${S}CurrentControlSet${S}Services${S}OracleASMService${instance}${S}ImagePath: $service_value";
  }
  else
  {
    EMD_PERL_DEBUG("asmConfig.asmServiceCheck(): NOT NT platform");
  }

  EMD_PERL_DEBUG("asmConfig.asmServiceCheck(): *** END ***");
  return "OK";
}

# runOradimAsm()
sub runOradimAsm
{
  EMD_PERL_DEBUG("asmConfig.runOradimAsm(): *** START ***");
  
  if($NT)
  {
    my $oracleHome = $ENV{ORACLE_HOME};
    my $instance = $ENV{ORACLE_SID};


    my $runAs = "";
    if ($hostUserID ne "")
    {
      $runAs = " -RUNAS ".$hostUserID;
    }

    EMD_PERL_DEBUG("asmConfig.runOradimAsm(): Running oradim to install service for $instance");
		my($cmd) = "${oracleHome}${S}bin${S}oradim.exe -NEW -ASMSID $instance -STARTMODE auto -SRVCSTART system ";
    EMD_PERL_DEBUG("asmConfig.runOradimAsm(): Command: ${cmd}");
    $cmd = $cmd.$runAs;

    &tempLocFallback();
    my $filename = "$TEMP\\"."asmConfig.$$";
    EMD_PERL_DEBUG("asmConfig.runOradimAsm(): Output file: $filename");
      
    # Do NOT check the return value of this command
    my(@res) = `$cmd >$filename 2>&1`;
    if($?)
    {
      my($err) = "@res";
      EMD_PERL_ERROR("asmConfig.runOradimAsm(): ${oracleHome}${S}bin${S}oradim.exe -NEW -ASMSID $instance -STARTMODE auto  -SRVCSTART system: $err");
      exit(1);
    }
    &removeFile($filename);
    EMD_PERL_DEBUG("asmConfig.runOradimAsm(): service for $instance has been started");
  }
  else
  {
    EMD_PERL_DEBUG("asmConfig.runOradimAsm(): Not NT platform, no need to run oradim");
  }
		
  EMD_PERL_DEBUG("asmConfig.runOradimAsm(): *** END ***");
}

# Run specified SQL script file or text
# Call &set_env_var($oracleHome, $oracleSid) before calling this method.
# run112AsmScript(userName,password,script)
sub run112AsmScript
{
  EMD_PERL_DEBUG("asmConfig.run112AsmScript(): *** START ***");

  my ($username,$password,$script) = @_;
  EMD_PERL_DEBUG("asmConfig.run112AsmScript(): script: $script");

  my $sql_string = "";
  $sql_string .= "set echo on\n";
  $sql_string .= "$script\n";
  $sql_string .= "exit;\n";

  (my $fh, my $filename) = &runSqlOnAsm($username,$password,"sysasm",$sql_string);

  close $fh;
  if($NT)
  {
    &removeFile($filename);
  }

  EMD_PERL_DEBUG("asmConfig.run112AsmScript(): *** END ***");
}
# Run specified SQL script file or text
# Call &set_env_var($oracleHome, $oracleSid) before calling this method.
# runPre112AsmScript(userName,password,script)
sub runPre112AsmScript
{
  EMD_PERL_DEBUG("asmConfig.runPre112AsmScript(): *** START ***");

  my ($username,$password,$script) = @_;
  EMD_PERL_DEBUG("asmConfig.runPre112AsmScript(): script: $script");

  my $sql_string = "";
  $sql_string .= "set echo on\n";
  $sql_string .= "$script\n";
  $sql_string .= "exit;\n";

  (my $fh, my $filename) = &runSqlOnAsm($username,$password,"sysdba",$sql_string);

  close $fh;
  if($NT)
  {
    &removeFile($filename);
  }

  EMD_PERL_DEBUG("asmConfig.runPre112AsmScript(): *** END ***");
}

# Run given sql script on destination oracleHome
# The caller is responsible to close the returned fileHandle
# runSqlOnAsm(sqlScript, hideOutput) will hide standard output for any
# defined parameter "hideOutput"
# runSqlOnAsm(username,password,dbrole,sqlScript, hideOutput, hideSQL) will hide standard
# output for any defined parameter "hideOutput" and SQL script for any defined
# parameter "hideSQL"
# runSqlOnAsm(sqlScript) will print standard output
sub runSqlOnAsm
{
  EMD_PERL_DEBUG("asmConfig.runSqlOnAsm(): *** START ***");
  my ($userID) = $_[0]."/".$_[1];
  my ($sql_string) = $_[3];
  my ($dbRole) = "";
  if (!defined($_[2]) || $_[2] eq "")
  {
    $dbRole = "sysasm";
  }
  else
  {
    $dbRole = $_[2];
  }
  if(!defined($_[5]))
  {
    EMD_PERL_DEBUG("asmConfig.runSqlOnAsm(): SQL:\n$sql_string");
  }

  (my $fh, my $filename) = &create_temp_file();
  if($NT)
  {
    $filename = "$TEMP\\"."dbclone.$$";
  }

  # If a username/password had been specified, use it. Otherwise,
  # default to OS authentication
  if($userID){
    EMD_PERL_DEBUG("asmConfig.runSqlOnAsm(): Connecting with connect descriptor");
    open(SQL_SCRIPT, "|$ENV{ORACLE_HOME}/bin/sqlplus /nolog >$filename")
      || die "Cannot open pipe for SQL_SCRIPT";
    print SQL_SCRIPT "CONNECT $userID AS $dbRole;\n";
  }
  else{
    EMD_PERL_DEBUG("asmConfig.runSqlOnAsm(): Connecting with OS auth");
    open(SQL_SCRIPT, "|$ENV{ORACLE_HOME}/bin/sqlplus '/ AS $dbRole' >$filename")
        || die "Cannot open pipe for SQL_SCRIPT";
  }
  print SQL_SCRIPT $sql_string;
  ## close SQL_SCRIPT || die "Bad SQL_SCRIPT";

  close SQL_SCRIPT || die "Bad SQL_SCRIPT : sqlplus exited with $?";

  #Open the temp file to print output to standard output and debug trace file
  open (OUT_PUT, "$filename") || die "Unable to open tempfile for OUT_PUT\n";
  my @output_content = <OUT_PUT>;
  my $output_string = "@output_content";
  close OUT_PUT;

  if(!defined($_[4]))
  {
    print STDOUT $output_string;
  }

  EMD_PERL_DEBUG("asmConfig.runSqlOnAsm(): OUT_PUT:\n$output_string");
  &parseOutput($output_string);

  EMD_PERL_DEBUG("asmConfig.runSqlOnAsm(): *** END ***");

  return ($fh, $filename);
}

sub getASMInstances 
{
 my ($oh) = @_;

 my $cmd = "$oh/bin/srvctl status asm -S 1";
 my ( $err, $output ) = executeCommand( $cmd, $oh );
 EMD_PERL_DEBUG("$cmd returned (err=$err, output=$output)");
 my @instance_name_array = ();
 if($err eq "")
 {
  my @lns = split /\n/, $output;
  for my $ln (@lns)
  {
     if ($ln =~ /inst_name={(.*)} node_name={(.*?)} /) 
     {
	my $res = "$1:$2";	
        push(@instance_name_array, $res);
     }
  }
 }

 my $instance_names = join (",", @instance_name_array);
 ($instance_names);
}
sub executeCommand
{
 my ( $cmd, $oh ) = @_;
 $ENV{ORACLE_HOME} = $oh;
 my $err    = "";
 my $output = "";
 if ( open( CMDOUTPUT, "$cmd 2>&1 |" ))
 {
  my @outputs = <CMDOUTPUT>;
  $output = join "", @outputs;
  if ( !close(CMDOUTPUT) )
  {
   # close error
   if ( $output ne "" )
   {
    $err    = "\"${cmd}\" returned: \"" . $output . "\"";
    $output = "";
   }
   else
   {
    $err = "bad \"$cmd\": $! $?";
   }
  }
 }
 else
 {
  # open error
  $err = "cannot execute \"$cmd\": $!";
 }

 ( $err, $output );
}

#Takes in array of diskgroup names and mounts them 
# at all the nodes of a cluster
# Applicable when oracle home version is 11.2 or above
sub mountDGsOnAllNodes
{
  my ( $dgList, $oh ) = @_;
  my $separator = ",";
  my @diskGroups = split /$separator/, $dgList;
  my $cmd = "";
  for my $diskGroup (@diskGroups)
  {
    $cmd = "$oh/bin/srvctl start diskgroup";
    $cmd = $cmd . " -g $diskGroup";
    my ( $err, $output ) = executeCommand( $cmd, $oh );
    EMD_PERL_DEBUG("$cmd returned (err=$err, output=$output)");
  }
}
1;
