MINI MINI MANI MO
#
# $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