MINI MINI MANI MO

Path : /opt/oracle/product/18c/dbhomeXE/srvm/admin/
File Upload :
Current File : //opt/oracle/product/18c/dbhomeXE/srvm/admin/rhpmovedb.pl

# 
# $Header: opsm/utl/rhpmovedb.pl /st_has_18.0/4 2018/06/20 21:11:23 prajm Exp $
#
# rhpmovedb.pl
# 
# Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
#
#    NAME
#      rhpmovedb.pl - Perl script to execute Datapatch from DB Home 
#
#    DESCRIPTION
#     This file contains functions that computes the patches associated
#     with a database home. This is then used during datapatch apply 
#     operation.
#
#    NOTES
#      <other useful comments, qualifications, etc.>
#
#    MODIFIED   (MM/DD/YY)
#    prajm       06/20/18 - Backport prajm_bug-28182503 from main
#    prajm       06/15/18 - fix bug#28182503
#    vgunredd    05/30/18 - fix rc for prereq
#    vgunredd    04/11/18 - fix bug 27811439
#    vgunredd    03/01/18 - fix bug 27577122
#    yifyang     12/14/17 - setting LD_LIBRARY_PATH
#    vgunredd    12/10/17 - workaround dbi connect problem
#    aaniyeri    08/14/17 - fix bug 26583245
#    yifyang     06/28/17 - bug-25804698
#    vgunredd    04/18/17 - fix err handling
#    yifyang     02/21/17 - ojvm check for rollong or nonrolling mode
#    vgunredd    02/10/17 - fix bug 25505841
#    lureyes     12/01/16 - Fix bug 25186865 
#    ksviswan    08/15/16 - XbranchMerge ksviswan_dbmove from st_has_12.2.0.1.0
#    ksviswan    08/04/16 - ojvm patch support
#    aaniyeri    03/11/16 - Creation
#

use strict;
use English;
use Scalar::Util qw(looks_like_number);
use File::Copy;
use File::Path;
use File::Find;
use File::Basename;
use File::Spec::Functions;
use DBI;
use constant TRUE                 => "1";
use constant FALSE                => "0";

#OS type
my $SOLARIS = ($^O eq 'solaris' || $^O eq 'sunos');

# OCR config file location
my $OCR_CONFIG_FILE = "/etc/oracle/ocr.loc";

if ($SOLARIS)
{
    $OCR_CONFIG_FILE = "/var/opt/oracle/ocr.loc";
}

# OLR config file location
my $OLR_CONFIG_FILE = "/etc/oracle/olr.loc";

if ($SOLARIS)
{
    $OLR_CONFIG_FILE = "/var/opt/oracle/olr.loc";
}

# Error tags
my $RHPMOVEDB_FILE_FAIL = "PrGh-1105"; #could not open file or directory
my $RHPMOVEDB_CONNECTDB_FAIL = "PrGh-1106"; #could not connect to database
my $RHPMOVEDB_EXE_SQL_FAIL = "PrGh-1107"; #could execute SQL statement
my $RHPMOVEDB_INTERNAL_ERROR = "PrGo-1069"; #internal error

# startup modes:
my $NORMAL_MODE = "NORMAL_MODE";
my $RESTRICTED_MODE = "RESTRICT";
my $UPGRADE_MODE = "UPGRADE";

my $DEBUG = $ENV{'SRVM_TRACE'};

my $isNonGI = isNonGI(); #true if local node is a standalone non-GI and non-SIHA node

#Command Line Arguments

#source workingcopy home
my $srchome = $ARGV[0];

#destination workingcopy home
my $dsthome = $ARGV[1];

#SID of the db to be moved - needed in case of non-GI and non-SIHA env
my $sid = getSID(); #ARGV[2]

#Path to temp file containing Patch ID's of source
my $src_file_patches = $ARGV[3];

#Path to file containing Patch ID's of destination
my $dst_file_patches = $ARGV[4];

#String indicating whether it is "rollback" or "apply" of datapatch
my $patch_command = $ARGV[5];

#Options: such as "nonrolling"
my $option = $ARGV[6];

#DB name of DB to be moved - needed in case of cluster env
my $dbname = getDBName(); #ARGV[7]

#Stop option for stopping DB
my $stopoption = $ARGV[8];

chomp($patch_command);

my $isPrimary = FALSE;
my $isCDB = FALSE;
my $isDVPatchAdminGranted = FALSE; #if true, DV_PATCH_ADMIN role has already been granted
my $isUpgrade = FALSE;
my $isNonrolling = FALSE;
my $srvctl = "$dsthome/bin/srvctl";
my $sqlplus = "$dsthome/bin/sqlplus";
my $isRac;
my $isPre12 = isPre12($dsthome);
my $isPre122 = isPre122($dsthome);
my $perlver = $^V;
substr ($perlver,0,1) = "";


#include the perl paths needed
BEGIN {
   #Add the directory of this file to the search path
   push @INC, dirname($PROGRAM_NAME);
   push @INC, "$dsthome/perl/lib/$perlver";
   push @INC, "$dsthome/perl/lib/site_perl/$perlver";
   push @INC, "$dsthome/lib";
}

if(uc($patch_command) eq uc("rollback"))
{
  $ENV{"LD_LIBRARY_PATH"} = "$srchome/lib";
  $ENV{"ORACLE_HOME"} = "$srchome";
  $ENV{"ORACLE_SID"} = "$sid";

  system ('echo ORACLE_HOME $ORACLE_HOME');
  print "\n";
  system ('echo ORACLE_SID $ORACLE_SID');
  print "\n";
 
  my @spatches;
  my @dpatches; 
 
  my $src_patches;
  my $dst_patches;
  open sfile, $src_file_patches or reportAndDie($RHPMOVEDB_FILE_FAIL, ($src_file_patches));
  open dfile, $dst_file_patches or reportAndDie($RHPMOVEDB_FILE_FAIL, ($dst_file_patches));
  chomp($src_patches = <sfile>);
  chomp($dst_patches = <dfile>);
  close sfile;
  close dfile;
  @spatches = split(',',$src_patches);
  @dpatches = split(',',$dst_patches);
  

 my $src_datapatch = "$srchome/OPatch/datapatch";
 my $src_sqlpatch_path = "$srchome/sqlpatch";

 #ls in dbhome/sqlpatch/ directory will also list other
 ###unnecessary files. All patches are numbers. May need to
 ###fix this logic, for the user may create a file/directory
 ###within the sqlpatch directory with the name containing all 
 ###numericals.
 ##[betausr1@rwsak11 sqlpatch]$ ls
 ##19769480  20831110  rhpmovedb.pl  sqlpatch.bat            sqlpatch.pl
 ##20299023  21359755  sqlpatch      sqlpatch_bootstrap.sql  sqlpatch.pm
 ##[betausr1@rwsak11 sqlpatch]$ 
 ##[betausr1@rwsak11 sqlpatch]$ ls -d [0-9]*
 ##19769480  20299023  20831110  21359755
 ##[betausr1@rwsak11 sqlpatch]$

 opendir (my $ab, $src_sqlpatch_path) or reportAndDie($RHPMOVEDB_FILE_FAIL, ($src_sqlpatch_path));
 my @afiles = readdir $ab;
 closedir $ab;
 my $i;
 my @src_db_patches;
 foreach $i (@afiles)
 {
   my $b = looks_like_number($i);
   if ($b == 1)
   {
    push @src_db_patches, $i;
   }
 }
 


 #Get delta of source - destination.
 #If source = {1,2,3,4}  and destination = {3,4}
 #delta = {1,2}. src_db_patches will contain list
 #of source specific db patches. For example if
 #src_db_patches = {2} then the patches that needs to be
 #rolled back is the intersection of this list with delta.i.e {2}
 my @del_all_patches = array_delta(\@spatches,\@dpatches);
 my @del_db_patches = array_intersect(\@del_all_patches,\@src_db_patches); 

 my $del = join(',',@del_db_patches);

 trace("Delta patches are ".$del."\n");
 #Keep isPatchUpg() api available but use the passed option now
 if ($option =~ /nonrolling/)
  {
     $isUpgrade = TRUE;
  }
 else
  {
     $isUpgrade = FALSE;
  }

 if ($isUpgrade)
 {
    startdb_upgrade(TRUE);
 }


 my $cmd = "$src_datapatch -db $sid  -rollback $del -force";
 trace("Exectuing datapatch rollback\n");

 #print "Executing $cmd \n";
 #my $res = `$cmd`;
 #print $res;
 
 my @res = run_cmd($cmd, TRUE);
 my $rc = shift @res;

 if($rc ne "0")
 {
    exit($rc);    
 }

 if ($isUpgrade)
 {
    startdb_upgrade(FALSE);
 }

}
elsif(uc($patch_command) eq uc("apply"))
{
  $ENV{"LD_LIBRARY_PATH"} = "$dsthome/lib";
  $ENV{"ORACLE_HOME"} = "$dsthome";
  $ENV{"ORACLE_SID"} = "$sid";
  system ('echo $ORACLE_HOME');
  print "\n";
  system ('echo $ORACLE_SID');
  print "\n";

  $isUpgrade = FALSE;
  if ($option =~ /nonrolling/)
  {
     $isNonrolling = TRUE;    
     trace("non-rolling mode ...\n");

     stopdb($dbname); #globally stop DB always so that
                      #script is re-runnable 
     startdb($dbname, $sid, $dsthome, $RESTRICTED_MODE);
     trace("started $dbname in restricted mode ...\n");
  }

  $isCDB = isCDB($dsthome, $sid);

  my @sqlout = runSelectRowsSQL("select database_role from v\$database", $dsthome, $sid);
  my $val  = shift @sqlout;
  if ($val eq "PRIMARY")
  {
      $isPrimary = TRUE;
      trace("is a PRIMARY DB\n");
  }
  if (!$isPrimary || $isPre12)
  {
     trace("skip datapatch ...\n");
     if ($isNonrolling)
     {
         stopdb($dbname);
         startdb($dbname, $sid, $dsthome, $NORMAL_MODE);
         trace("started $dbname in normal mode ...\n");
     }   
     exit(0);
  }

  if ($isNonrolling)
  { #detect if DB needs to be in upgrade mode for datapatch
      $isUpgrade = isPatchUpg($dsthome);
  }
  elsif ($isCDB)
  {
      trace("opening PDBs for datapatch ...\n");
      openpdbs($NORMAL_MODE, $dsthome, $sid);
  }

  if ($isUpgrade)
  {
      trace("datapatch requires DB in upgrade mode.\n");
      startdb_upgrade(TRUE);
  }

  grantDVPatchAdminRole($dsthome, $sid);
  my $des_datapatch = "$dsthome/OPatch/datapatch";
  my $cmd = "$des_datapatch -db $sid ";
  my @res = run_cmd($cmd, TRUE);
  my $rc = shift @res;

  #restart DB in normal mode before
  #checking if datapatch failed so that
  #the script is reentrant
  revokeDVPatchAdminRole($dsthome, $sid);
  if ($isUpgrade)
  {
     trace("restarting DB in normal mode.\n");
     startdb_upgrade(FALSE);
  }
  elsif ($isNonrolling)
  {
     trace("restarting DB in normal mode ...\n");
     stopdb($dbname);
     startdb($dbname, $sid, $dsthome, $NORMAL_MODE);
  }

  if($rc ne "0")
  {
      trace("datapatch returned non-zero exit code.\n");
      exit($rc);
  }
}
else
{
 trace("\nInvalid Argument $ARGV[5] passed  to datapatch");
 trace( "\n Usage :- \n");
 trace("$ARGV[0]/perl/bin/perl -I $ARGV[0]/perl/lib/ $ARGV[0]/crs/install/rhpdata/rhpmovedb.pl 	$ARGV[3] $ARGV[4] rollback\n");
}

sub array_intersect {


  my($rarray1, $rarray2, $risect) = @_;

  my (%union, %isect, $e);
  %union = %isect = ();

  foreach $e (@$rarray1) {
    $union{$e} = 1;
  }

  foreach $e (@$rarray2) {
    $isect{$e} = 1 if $union{$e};
  }

  @$risect = keys %isect;

}

sub array_delta {

 
  my($rarray1, $rarray2, $risect) = @_;

  my (%union, %isect, $e);
  %union = %isect = ();

  foreach $e (@$rarray2) {
    $union{$e} = 1;
  }

  foreach $e (@$rarray1) {
    $isect{$e} = 1 if !$union{$e};
  }

  @$risect = keys %isect;

}

sub array_print {

 my($array)= @_;
 print join(",",@$array);

}

sub startdb_upgrade
{
  my $isPre = $_[0];
  my @output;

  my $home;
  if(uc($patch_command) eq uc("apply"))
  {
    $ENV{'ORACLE_HOME'} = $dsthome;
    $home = $dsthome;
  }
  else
  {
    $ENV{'ORACLE_HOME'} = $srchome;
    $home = $srchome;
  }
  $ENV{'ORACLE_SID'} = $sid;

  if ($isPre)
  {
    trace("Performing operations to put Database in upgrade mode\n");
    #Need to check the database type
    my @sqlout =
        runSelectRowsSQL("select value from v\$parameter where name = 'cluster_database'", $home, $sid);
    $isRac = shift @sqlout;
    trace("value of cluster_database param : $isRac\n");
    
    #the cluster_database is only changed if the database type is RAC
    if ($isRac eq "TRUE")
    {
        trace("un-setting cluster_database param ...\n");
        #Execute SQL stmts to start the db in upgrade mode 
        runSQL("alter system set cluster_database=false scope=spfile", $home, $sid);
    }
    stopdb($dbname);
    trace("stopped database\n");

    sqlstartdb($UPGRADE_MODE);
    trace("Database started in upgrade mode\n");
    if ($isCDB)
    {
       openpdbs($UPGRADE_MODE, $home, $sid);
       trace("PDBs opened in upgrade mode\n");
    }
  }
  else
  {
    trace("Performing operations to start Database in normal mode\n");

    #the cluster_database is only changed if the database type is RAC
    if ($isRac eq "TRUE")
    {   
        trace("re-setting cluster_database param to true ...\n");
        #Execute SQL stmts to change cluster_database to true 
        runSQL("alter system set cluster_database=true scope=spfile", $home, $sid);
    }

    sqlstopdb();
    trace("stopped database\n");

    startdb($dbname, $sid, $home, $NORMAL_MODE);
    trace("Database started back in normal mode\n");
  }
}

sub trace {
    print @_ if ($DEBUG);
}

# Reports the error in below given format and call die.
#
# Format derived from opsm/jsrc/oracle/cluster/gridhome/client/GridHomeActionResult.java
# that encapsulates the result of the Grid Home operations. Here it is utilized only
# to report error.
#
# String result with the following DTD:
#        <STATUS>0|1|2</STATUS>    where 0=SUCCESS, 1=WARNING, 2=EXCEPTION
#        <OUTPUT>
#        <FACILITY>PrGo</FACILITY>
#        <KEY>1000</KEY>
#        <PRINTKEY>true|false</PRINTKEY>
#        <ARG>arg1</ARG>
#        <ARG>arg2</ARG>
#        ....
#        </OUTPUT>
#        <OUTPUT>
#        ...
#        </OUTPUT>
#        <EXCEPTION>stack trace</EXCEPTION>
#    The STATUS tag is mandatory. There can be 0 or more OUTPUT tags. When 
#    there is an OUTPUT tag, it will always be followed by FACILITY, KEY and
#    PRINTKEY tags. The PRINTKEY tag should has only true or false value where
#    false means not to print the facility and key with the message and 
#    true means the facility and key will be printed with the message. There 
#    can be 0 or more ARG tags under the OUTPUT tag. There is only one EXCEPTION
#    tag which is used for sending stack trace to ghctl for printing into the
#    trace file.
#
sub reportAndDie {
    my $msgId = shift;
    my @args = shift;
    my $native = shift;
    my @final;

    trace("Message ID $msgId\n");
    my ($facility, $key) = split(/-/, $msgId, 2);

    # collate the args
    my @out_args;
    foreach (@args)
    {
        trace ("args $_");
        push(@out_args, "<ARG>".$_."</ARG>");
    }

    push(@final, "<OUTPUT>");
    push(@final, "<FACILITY>".$facility."</FACILITY><KEY>".$key."</KEY>");
    # always print key in message output
    push(@final, "<PRINTKEY>true</PRINTKEY>");
    push(@final, @out_args);
    push(@final, "</OUTPUT>");
    if (defined $native)
    {
        push(@final, "<OUTPUT>Native Error: ".$native."</OUTPUT>");
    }
    my $errStr = join("", @final);
    die "$errStr\n";
}

=head1 isPatchUpg

  Executes datapatch prereq to determine if
  DB needs to be in upgrade mode.

=head2 Parameters

  [0] Oracle home path

=head2 Returns

   true if datapatch reports that DB needs to be in upgrade mode

=cut
sub isPatchUpg
{

   my $dbhome = $_[0];
   my @output;
   my @upgtxt;
   $ENV{"ORACLE_HOME"} = "$dbhome";
   $ENV{"ORACLE_SID"} = "$sid";
   
   my $des_datapatch = "$dbhome/OPatch/datapatch";

   my $cmd = "$des_datapatch -db $sid -prereq";

   trace("Check if the patch requires Database in upgrade mode\n");
   my @res = run_cmd($cmd, TRUE);
   my $rc = shift @res;

   my @upgtxt = grep(/must be in upgrade mode/, @res);
   if (scalar(@upgtxt > 0))
   {
      trace("Patch needs database to be in upgrade mode\n");
      return TRUE;
   }
   else
   {
      if($rc ne "0")
      {
          reportAndDie($RHPMOVEDB_INTERNAL_ERROR, ("rhpmovedb.pl-isPatchUpg-1"))
      }
      return FALSE;
   }
}

sub sqlstartdb
{
  my $mode = $_[0];
  if ($mode eq $NORMAL_MODE)
  {
      $mode = "";
  }
  my $connect_string = '/ as sysdba';
  my $sqlplus_settings = '';
  my $result = qx { $sqlplus $connect_string <<EOF
$sqlplus_settings
startup $mode;
exit;
EOF
};
}

sub sqlstopdb
{
  my $connect_string = '/ as sysdba';
  my $sqlplus_settings = '';
  my $shutdownsql = "shutdown";
  if ($stopoption)
  {
      if (lc $stopoption ne "null")
      {
          trace("stopping DB with stop option $stopoption\n");
          $shutdownsql .= " $stopoption"; 
      } 
  }
  my $result = qx { $sqlplus $connect_string <<EOF
$sqlplus_settings
$shutdownsql;
exit;
EOF
};
}

sub openpdbs
{
  my $mode = $_[0];
  my $home = $_[1];
  my $sid = $_[2];
  if ($mode eq $NORMAL_MODE)
  {
      runSQL("alter pluggable database all open", $home, $sid);
  }
  elsif ($mode eq $UPGRADE_MODE)
  {
      runSQL("alter pluggable database all open upgrade", $home, $sid);
  }
  elsif ($mode eq $RESTRICTED_MODE)
  {
      runSQL("alter pluggable database all open read write restricted", $home, $sid);
  }
}

sub run_cmd
{
  my $cmd = $_[0];
  my $isPrintOut = $_[1];
  my @stdout = ("");
  my $exitcode = 0;

  open (CMD, "$cmd 2>&1 |") or die "cannot execute command $cmd: $!";
  while (my $line  = <CMD>)
  {
      chomp $line;
      if ($isPrintOut)
      {
          print "$line\n";
      }
      else
      {
          trace("$line\n");
      }
      push @stdout, $line;
  }
  close (CMD);
  my $exitcode = $CHILD_ERROR >> 8;

  return ($exitcode, @stdout);
}

sub output_lines
{
  foreach my $line (@_)
  {
      trace("$line\n");
  }
}


=head1 runSelectRowsSQL

  Executes SQL to select rows from a single column.

=head2 Parameters

  [0] statement selecting a single column
  [1] Oracle home path
  [2] Oracle SID

=head2 Returns

   array containing rows from the selected column

=cut
sub runSelectRowsSQL
{
    my $stmt = $_[0];
    my $home = $_[1];
    my $inst = $_[2];

    my $usr;
    my $passwd;
    my %attr;
    my $driver;
    my $sth;
    my @result;

    $ENV{'ORACLE_HOME'} = $home;
    $ENV{'ORACLE_SID'} = $inst;

    $attr{'ora_session_mode'} = 2; # connect as sysdba

    $driver = 'dbi:Oracle:';

    trace("connecting to DB instance ...\n");
    my $dbh = DBI->connect($driver, $usr, $passwd, \%attr);
    if (! $dbh)
    {
        my @args = ($DBI::errstr);
        reportAndDie($RHPMOVEDB_CONNECTDB_FAIL, @args);
    }

    #workaround for Oracle bug
    $SIG{CHLD} = 'DEFAULT'; 

    my $sth = $dbh->prepare($stmt);
    if (!$sth)
    {
        $dbh->disconnect
            or warn "Failed to disconnect: " . $DBI::errstr ."\n";
        my @args = ($DBI::errstr);
        reportAndDie($RHPMOVEDB_EXE_SQL_FAIL, @args);
    }
  
    my $rv = $sth->execute();
    if (!$rv)
    {
        $dbh->disconnect
            or warn "Failed to disconnect: " . $DBI::errstr ."\n";
        my @args = ($DBI::errstr);
        reportAndDie($RHPMOVEDB_EXE_SQL_FAIL, @args);
    }

    my @row;
    while (@row = $sth->fetchrow_array())
    {
        push(@result, @row);
    }

    $dbh->disconnect
        or warn "Failed to disconnect: " . $DBI::errstr ."\n";

    return @result;
}

=head1 runSQL

  Executes non-SELECT SQL statement.

=head2 Parameters

  [0] non-SELECT SQL statement
  [1] Oracle home path
  [2] Oracle SID

=cut
sub runSQL
{
    my $stmt = $_[0];
    my $home = $_[1];
    my $inst = $_[2];

    my $usr;
    my $passwd;
    my %attr;
    my $driver;
    my $sth;

    $ENV{'ORACLE_HOME'} = $home;
    $ENV{'ORACLE_SID'} = $inst;

    $attr{'ora_session_mode'} = 2; # connect as sysdba

    $driver = 'dbi:Oracle:';

    trace("connecting to DB instance ...\n");
    my $dbh = DBI->connect($driver, $usr, $passwd, \%attr);
    if (! $dbh)
    {
        my @args = ($DBI::errstr);
        reportAndDie($RHPMOVEDB_CONNECTDB_FAIL, @args);
    }

    #workaround for Oracle bug
    $SIG{CHLD} = 'DEFAULT'; 

    my $sth = $dbh->do($stmt);
    if (!$sth)
    {
        $dbh->disconnect
            or warn "Failed to disconnect: " . $DBI::errstr ."\n";
        my @args = ($DBI::errstr);
        reportAndDie($RHPMOVEDB_EXE_SQL_FAIL, @args);
    }

    $dbh->disconnect
        or warn "Failed to disconnect: " . $DBI::errstr ."\n";

}

=head1 runSQLStmtms

  Executes non-SELECT SQL statements in same session.

=head2 Parameters

  [0] Oracle home path
  [1] Oracle SID
  [2..n] non-SELECT SQL statements

=cut
sub runSQLStmts
{
    my $home = $_[0];
    my $inst = $_[1];
    my @stmts = @_[2..$#_];

    my $usr;
    my $passwd;
    my %attr;
    my $driver;
    my $sth;

    $ENV{'ORACLE_HOME'} = $home;
    $ENV{'ORACLE_SID'} = $inst;

    $attr{'ora_session_mode'} = 2; # connect as sysdba

    $driver = 'dbi:Oracle:';

    trace("connecting to DB instance ...\n");
    my $dbh = DBI->connect($driver, $usr, $passwd, \%attr);
    if (! $dbh)
    {
        my @args = ($DBI::errstr);
        reportAndDie($RHPMOVEDB_CONNECTDB_FAIL, @args);
    }

    #workaround for Oracle bug
    $SIG{CHLD} = 'DEFAULT'; 

    for (my $idx = 0; $idx < scalar(@stmts); $idx++)
    {
       my $stmt = $stmts[$idx];
       my $sth = $dbh->do($stmt);
       if (!$sth)
       {
           $dbh->disconnect
               or warn "Failed to disconnect: " . $DBI::errstr ."\n";
           my @args = ($DBI::errstr);
           reportAndDie($RHPMOVEDB_EXE_SQL_FAIL, @args);
       }
    }
    $dbh->disconnect
        or warn "Failed to disconnect: " . $DBI::errstr ."\n";

}


sub startdb
{
    my $sid = $_[1];
    my $home = $_[2];
    my $mode = $_[3];

    if ($isNonGI)
    {
        sqlstartdb($mode);
    } 
    else
    {
        my $dbname = $_[0];
        my $cmd;
        if ($mode eq $RESTRICTED_MODE)
        {
            $cmd = "$srvctl start database -d $dbname -o RESTRICT";
        }
        else
        {
            $cmd = "$srvctl start database -d $dbname";
        }
        my @res = run_cmd($cmd, TRUE);
        my $rc = shift @res;
        if($rc ne "0")
        {
           exit($rc);
        }
    }

    if (isCDB($home, $sid)) #don't use global var $isCDB
    {
        openpdbs($mode, $home, $sid);
        trace("opened PDBs in $mode mode\n");
    }
}

sub isCDB
{
    if ($isPre12)
    {
        return FALSE;
    }
    my $home = $_[0];
    my $sid = $_[1];
    my @sqlout = runSelectRowsSQL("select cdb from v\$database", $home, $sid);
    my $val = shift @sqlout;
    if ($val eq "YES")
    {
        trace("is a CDB\n");
        return TRUE;
    }
    return FALSE;
}

sub isDVEnabled
{
    my $home = $_[0];
    my $sid = $_[1];
    my @sqlout = runSelectRowsSQL("select value from v\$option where parameter = 'Oracle Database Vault'",
                                  $home, $sid);
    my $val = shift @sqlout;
    if ($val eq "TRUE")
    {
        trace("Data vault is enabled\n");
        return TRUE;
    }
    return FALSE;
}

sub isDVPatchAdminRoleGranted
{
    my $home = $_[0];
    my $sid = $_[1];
    my $isDVEnabled = isDVEnabled($home, $sid);
    if (!$isDVEnabled)
    {
        return FALSE;
    }
    my @sqlout;
    if ($isCDB)
    {
       @sqlout = runSelectRowsSQL("select count(*) from cdb_role_privs where granted_role in ('DV_PATCH_ADMIN') and grantee = 'SYS'",
                                  $home, $sid);
    }
    else
    {
       @sqlout = runSelectRowsSQL("select count(*) from dba_role_privs where granted_role in ('DV_PATCH_ADMIN') and grantee = 'SYS'",
                                  $home, $sid);

    }
    my $val = shift @sqlout;
    if ($val ne "0")
    {
        trace("dv_patch_admin role has already been granted\n");
        return TRUE;
    }
    return FALSE;
}

sub grantDVPatchAdminRole
{
    my $home = $_[0];
    my $sid = $_[1];
    my $isDVEnabled = isDVEnabled($home, $sid);
    $isDVPatchAdminGranted = isDVPatchAdminRoleGranted($home, $sid);
    if ($isDVEnabled && !$isDVPatchAdminGranted)
    {
        trace("dv_patch_admin role needs to be granted\n");
        runSQLStmts($home, $sid, "begin DBMS_MACSEC_ROLES.SET_ROLE('DV_OWNER'); end;",
                                 "grant dv_patch_admin to sys",
                                 "begin DBMS_MACADM.ENABLE_DV_PATCH_ADMIN_AUDIT; end;");
    }
}

sub revokeDVPatchAdminRole
{
    my $home = $_[0];
    my $sid = $_[1];
    my $isDVEnabled = isDVEnabled($home, $sid);
    if ($isDVEnabled && !$isDVPatchAdminGranted)
    {
        trace("dv_patch_admin role needs to be revoked\n");
        runSQLStmts($home, $sid, 
                    "revoke dv_patch_admin from sys",
                    "begin DBMS_MACADM.DISABLE_DV_PATCH_ADMIN_AUDIT; end;");
    }
}

sub isPre12
{
    my $home = $_[0];
    $ENV{"LD_LIBRARY_PATH"} = "$home/lib";
    $ENV{"ORACLE_HOME"} = "$home";
    $sqlplus = "$dsthome/bin/sqlplus";
    my $cmd = "$sqlplus -V";
    my @res = run_cmd($cmd, FALSE);
    my $rc = shift @res;
    if($rc ne "0")
    {
        reportAndDie($RHPMOVEDB_INTERNAL_ERROR, ("rhpmovedb.pl-isPre12-1"))
    }
    my $vrsnRegex = "Release 11\.";
    my @vrsnCheck = grep(/$vrsnRegex/, @res);
    if (scalar(@vrsnCheck > 0))
    {
        trace("DB version is pre-12.1\n");
        return TRUE;
    }
    trace("DB version is at least 12.1\n");
    return FALSE;
}

sub isPre122
{
    my $home = $_[0];
    if (isPre12($home))
    {
        trace("DB version is pre-12.2\n");
        return TRUE;
    }
    $ENV{"LD_LIBRARY_PATH"} = "$home/lib";
    $ENV{"ORACLE_HOME"} = "$home";
    $sqlplus = "$dsthome/bin/sqlplus";
    my $cmd = "$sqlplus -V";
    my @res = run_cmd($cmd, FALSE);
    my $rc = shift @res;
    if($rc ne "0")
    {
        reportAndDie($RHPMOVEDB_INTERNAL_ERROR, ("rhpmovedb.pl-isPre122-1"))
    }
    my $vrsn121Regex = "Release 12\\.1";
    my @vrsn121Check = grep(/$vrsn121Regex/, @res);
    if (scalar(@vrsn121Check > 0))
    {
        trace("DB version is pre-12.2\n");
        return TRUE;
    }
    trace("DB version is at least 12.2\n");
    return FALSE;
}

sub stopdb
{
    if ($isNonGI)
    {
        sqlstopdb();
    }
    else
    {
        my $dbname = $_[0];
        my $cmd = "$srvctl stop database -d $dbname";
        if ($stopoption)
        {
            if (lc $stopoption ne "null")
            {
                trace("stopping DB with stop option $stopoption\n");
                $cmd .= " -o $stopoption"; 
            }
        }
        trace("executing $cmd\n");
        my @res = run_cmd($cmd, TRUE);
        my $rc = shift @res;
        if($rc eq "1")
        { #ignore AlreadyStoppedException indicated by 2
            exit($rc);
        }
    }
}


=head1 isNonGI

  Determines if this node is a standalone node
  i.e. non-GI and non-SIHA node.
  Basically, to decide whether to execute
  SQL or srvctl to restart DB.

=cut
sub isNonGI
{
    if (-e $OCR_CONFIG_FILE)
    {
        return FALSE;
    }
    trace("standalone non-GI and non-SIHA node\n");
    return TRUE;
}

=head1 getSID

  Returns name of local instance (Oracle SID)
  of the database being patched.

=cut
sub getSID
{
    $ENV{"ORACLE_HOME"} = "$dsthome";
    my $sidArg = $ARGV[2];
    if (lc $sidArg eq "null")
    {
        if ($isNonGI)
        {   #instance name must be specified in non-cluster env
            reportAndDie($RHPMOVEDB_INTERNAL_ERROR, ("rhpmovedb.pl-SID-1"))
        }

        my $dbnameArg = $ARGV[7];

        my $nodename = getLocalNode();

        my $srvctl = "$dsthome/bin/srvctl";
        if (isPre122($dsthome))
        {
            trace("DB version is pre-12.2\n");
            my $cmd = "$srvctl status database -d $dbnameArg -S 1";
            my @res = run_cmd($cmd, FALSE);
            my $rc = shift @res;
            if($rc ne "0")
            {
                reportAndDie($RHPMOVEDB_INTERNAL_ERROR, ("rhpmovedb.pl-SID-2"))
            }
            my $noderegex = "node_name={$nodename}";
            my @instdetails = grep(/$noderegex/, @res);
            my $inststr;
            if (scalar(@instdetails > 0))
            {
                $inststr = join " ", @instdetails;
                trace("instance details : $inststr\n");
            }
            else
            {
                my $instregex = "inst_name=";
                @instdetails = grep(/$instregex/, @res);
                if (scalar(@instdetails > 0))
                {
                    $inststr = join " ", @instdetails;
                    trace("Oracle Restart DB instance details : $inststr\n");
                }
            }
            if ($inststr)
            {
                my $loc1 = index($inststr, "inst_name="); 
                $loc1 = index($inststr, "{", $loc1+1)+1;
                my $loc2 = index($inststr, "}", $loc1); 
                $sidArg = (substr($inststr, $loc1, $loc2-$loc1));
                trace("Oracle SID : $sidArg\n");
                return $sidArg;
            }
            reportAndDie($RHPMOVEDB_INTERNAL_ERROR, ("rhpmovedb.pl-SID-3"))
        }
        else
        {
            trace("DB version is at least 12.2\n");
            my $cmd = "$srvctl status database -d $dbnameArg -sid";
            my @res = run_cmd($cmd, FALSE);
            my $rc = shift @res;
            if($rc ne "0")
            {
                reportAndDie($RHPMOVEDB_INTERNAL_ERROR, ("rhpmovedb.pl-SID-4"))
            }
            chomp($sidArg = (join("", @res)));
            trace("Oracle SID : $sidArg\n");
            return $sidArg;
        }
    }
    return $sidArg;
}

=head1 getDBName

  Returns the DB unique name of the database being patched.

=cut
sub getDBName
{
    $ENV{"ORACLE_HOME"} = "$dsthome";
    my $dbnameArg = $ARGV[7];
    my $useSQL = FALSE;
    if ($isNonGI)
    { #DB unique name not required in case of standalone non-cluster node
        trace("standalone non-cluster node\n");
        return $dbnameArg;
    }

    if ($dbnameArg)
    {
        if (lc $dbnameArg eq "null")
        {
            trace("query v\$database for DB unique name\n");
            $useSQL = TRUE;    
        }
    }
    else
    {
        trace("query v\$database for DB unique name\n");
        $useSQL = TRUE;
    }

    if ($useSQL)
    {
        trace("executing SQL query to retrieve DB unique name\n");
        my @sqlout = runSelectRowsSQL("select DB_UNIQUE_NAME from v\$database", $dsthome, $sid);
        my $val = shift @sqlout;
        trace("DB unique name: $val\n");
        return $val;
    }
    trace("using specified DB unique name\n");
    return $dbnameArg;
}

=head1 getLocalNode

  Returns name of the local node.
  To be called only in case of cluster/siha env.

=cut
sub getLocalNode
{
    my $crshome = getCRSHome();
    my $cmd = "$crshome/bin/olsnodes -l";
    my @res = run_cmd($cmd, FALSE);
    my $rc = shift @res;
    if($rc ne "0")
    {
        reportAndDie($RHPMOVEDB_INTERNAL_ERROR, ("rhpmovedb.pl-localnode-1"))
    }
    my $localnode = join("", @res);
    trace ("local node : $localnode\n");
    return $localnode; 
}

=head1 getCRSHome

  Returns CRS home path.
  To be called only in case of cluster/siha env.

=cut
sub getCRSHome
{
    unless (-e $OLR_CONFIG_FILE)
    {
        reportAndDie($RHPMOVEDB_INTERNAL_ERROR, ("rhpmovedb.pl-crs_home-1"));
    }
    trace("opening olr.loc \n");
    open (my $fh, "<", $OLR_CONFIG_FILE)
        or reportAndDie($RHPMOVEDB_FILE_FAIL, ($OLR_CONFIG_FILE)); 

    my $regex = "crs_home";
    while (my $row = <$fh>)
    {
        trace("olr.loc line: $row\n");
        if (index($row, $regex) != -1)
        {
            my $loc1 = index($row, $regex)+1; 
            $loc1 = index($row, "=", $loc1)+1;
            my $substrlen = length($row) - $loc1; 
            chomp(my $crshome = (substr($row, $loc1, $substrlen)));
            trace("crs home : $crshome\n");
            return $crshome;
        }
    }
    reportAndDie($RHPMOVEDB_INTERNAL_ERROR, ("rhpmovedb.pl-crs_home-2"))
}


OHA YOOOO