#!/usr/local/bin/perl # # patch112.pl # # Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved. # # NAME # patch112.pl - Auto patching tool for Oracle Clusterware/RAC home. # # DESCRIPTION # patch112.pl - Auto patching tool for Oracle Clusterware/RAC home. # # MODIFIED (MM/DD/YY) # ksviswan 03/02/11 - Fix Bugs 11807070, 11824391, 9965477 # ksviswan 01/25/11 - Fix bug 11670519. # ksviswan 01/25/11 - Out of place patching support # ksviswan 10/01/10 - XbranchMerge ksviswan_bug-10147375 from main # ksviswan 09/27/10 - XbranchMerge ksviswan_opauto_copy_initfiles from # main # ksviswan 09/22/10 - XbranchMerge ksviswan_bug-10132822 from main # ksviswan 09/20/10 - Fix Bugs 9475556,9869238,9869250 # ksviswan 08/25/10 - Fix Bug 9863515,9869222,10055092,9869222 # ksviswan 06/24/10 - Fix Bug 9849173 # ksviswan 06/07/10 - Fix Bug 9786647 # ksviswan 05/31/10 - Fix Bug 9755296 # ksviswan 05/25/10 - Fix Bugs 9719845,9719790,9750704 and 9750739 # ksviswan 04/21/10 - Support Automation for New PSU patch format. # ksviswan 03/06/10 - Fix Bugs 9453069 and 9437772 # ksviswan 02/25/10 - Fix Bugs 9362121,9358228,9409808 # add checkCompoent logic # ksviswan 01/08/10 - Fix bug 9209886 and set default log dir. # ksviswan 05/08/09 - Implement 11.2 autopatching support # # NOTES # # Documentation: usage output, # execute this script with -h option # # ################ Documentation ################ # The SYNOPSIS section is printed out as usage when incorrect parameters # are passed =head1 NAME patch112.pl - Auto patching tool for Oracle Clusterware/RAC home. =head1 SYNOPSIS opatch auto [-oh ] [-och ] [-rollback] [-ocmrf ] [-norestart] Options: patch_directory Default is the current directory. This is the top level directory path where the patch is unzipped. Example: Always unzip the gipsu/gi one-off in a empty directory where there are no other directory/files existing. If the directory under which the patch is unzipped is /tmp/patchdir opatch auto /tmp/patchdir -rollback The patch will be rolled back, not applied This option requires patch_directory to be passed Example: opatch auto /tmp/patchdir -rollback -oh Database/Clusterware home locations comma_delimited_list_of_oralcehomes_to_patch The default is all applicable Oracle Homes including the clusterware home .Use this option to patch RDBMS homes where no database is registered or any specific database home or clusterware home. Example: opatch auto /tmp/patchdir -oh -och Grid infrastructure home location absolute path of crs home location. This is only used to patch Clusterware home when the clusterware stack is down Example: opatch auto /tmp/patchdir -och -ocmrf OCM response file location Absolute path of the OCM response file. This is required for silent mode patching -norestart The clusterware stack and database home resources will not be started after patching. To patch a shared Clusterware or Database home, Refer to Section "Patching installation to RAC database and/or the GI home" in the Patch Readme document and follow the steps. =head1 DESCRIPTION This script automates the complete patching process for a Clusterware or RAC database home. This script must be run as root user and needs Opatch version 10.2.0.3.3 or above. Case 1 - On each node of the CRS cluster in case of Non Shared CRS Home. Case 2 - On any one node of the CRS cluster is case of Shared CRS home. =cut ################ End Documentation ################ use strict; use English; use Cwd; use Cwd 'abs_path'; use FileHandle; use File::Basename; use File::Spec::Functions; use File::Copy; use Sys::Hostname; use Net::Ping; use Getopt::Long; use Pod::Usage; BEGIN { # Add the directory of this file to the search path unshift @INC, dirname($PROGRAM_NAME); } use crsconfig_lib; require crsdelete; require crspatch; #Global variables our $g_help = 0; our $g_unlock = 0; our $g_patch = 0; # pull all parameters defined in crsconfig_params and s_crsconfig_defs (if # it exists) as variables in Perl my $paramfile_default = catfile (dirname ($0), "crsconfig_params"); # pull all definitions in s_crsconfig_defs (if it exists) as variables in Perl # this file might not exist for all platforms my $defsfile = catfile (dirname ($0), "s_crsconfig_defs"); my $timestamp = gentimeStamp(); my $scrdir = abs_path(dirname ($0)); my $deflogdir = "$scrdir/../../cfgtoollogs"; my $logfile; if (-e $deflogdir) { $logfile = catfile($deflogdir, "opatchauto$timestamp.log"); } else { $logfile = catfile ($scrdir, "log", "opatchauto$timestamp.log"); } print "opatch auto log file location is $logfile\n"; my $PARAM_FILE_PATH = $paramfile_default; my $patchdir; my $patchdbdir; my $patchfile; my $patchnum; my $rollback; my $ohome; my $chome; my $ocmrf; my $norestart; my $pwd = $ENV{'PWD'}?$ENV{'PWD'}:getcwd(); my %ohdb = (); my %dboh = (); my %ohowner = (); my $OS = `uname`; chomp $OS; my $ORA_CRS_HOME; my $ORA_CRS_USER; my $patchType; my $homeType; my @dbhomes = (); my @homestopatch = (); my @homestostart = (); my ($name, $passwd, $uid, $gid, $quota, $comment, $gcos, $dir, $shell) = getpwuid( $< ); my $crs_running; my @gipatches = (); my @dbpatches = (); my @patchIds = (); my $isconflictok; my $op_silent; my $sihainst = 0; my $cfg; my $dest_crshome; #TBR my $overbose; my $unzip; my $rsh; my $ssh; my $su; my $sed; my $echo; my $mkdir; my $cat; my $rcp; my $kill; my $olrloc; my $ocrloc; my $fuser; if ( $OS eq "Linux") { $unzip = "/usr/bin/unzip"; $rsh = "/usr/bin/rsh"; $ssh = "/usr/bin/ssh"; $su = "/bin/su"; $sed = "/bin/sed"; $echo = "/bin/echo"; $mkdir = "/bin/mkdir"; $cat = "/bin/cat"; $rcp = "/usr/bin/rcp"; $kill = "/bin/kill"; $olrloc = "/etc/oracle/olr.loc"; $ocrloc = "/etc/oracle/ocr.loc"; $fuser = "/sbin/fuser"; } elsif ($OS eq "HP-UX") { $unzip = "/usr/local/bin/unzip"; $rsh = "/usr/bin/remsh"; $ssh = "/usr/bin/ssh"; $su = "/usr/bin/su"; $sed = "/usr/bin/sed"; $echo = "/usr/bin/echo"; $mkdir = "/usr/bin/mkdir"; $cat = "/usr/bin/cat"; $rcp = "/usr/bin/rcp"; $kill = "/usr/bin/kill"; $olrloc = "/var/opt/oracle/olr.loc"; $ocrloc = "/var/opt/oracle/ocr.loc"; $fuser = "/usr/sbin/fuser"; } elsif ($OS eq "AIX" ) { $unzip = "/usr/local/bin/unzip"; $rsh = "/usr/bin/rsh"; $ssh = "/usr/bin/ssh"; $su = "/usr/bin/su"; $sed = "/usr/bin/sed"; $echo = "/usr/bin/echo"; $mkdir = "/usr/bin/mkdir"; $cat = "/usr/bin/cat"; $rcp = "/usr/bin/rcp"; $kill = "/usr/bin/kill"; $olrloc = "/etc/oracle/olr.loc"; $ocrloc = "/etc/oracle/ocr.loc"; $fuser = "/usr/sbin/fuser"; } elsif ( $OS eq "SunOS" ) { $unzip = "/usr/bin/unzip"; $rsh = "/usr/bin/rsh"; $ssh = "/usr/local/bin/ssh"; $su = "/usr/bin/su"; $sed = "/usr/bin/sed"; $echo = "/usr/bin/echo"; $mkdir = "/usr/bin/mkdir"; $cat = "/usr/bin/cat"; $rcp = "/usr/bin/rcp"; $kill = "/usr/bin/kill"; $olrloc = "/var/opt/oracle/olr.loc"; $ocrloc = "/var/opt/oracle/ocr.loc"; $fuser = "/usr/sbin/fuser"; } else { die "ERROR: $OS is an Unknown Operating System\n"; } # the return code to give when the incorrect parameters are passed my $usage_rc = 1; GetOptions('patchdir=s' => \$patchdir, 'patchfile=s' => \$patchfile, 'patchnum=s' => \$patchnum, 'paramfile=s' => \$PARAM_FILE_PATH, 'rollback' => \$rollback, 'oh=s' => \$ohome, 'och=s' => \$chome, 'ocmrf=s' => \$ocmrf, 'norestart' => \$norestart, 'unlock!' => \$g_unlock, 'patch!' => \$g_patch, 'desthome=s' => \$dest_crshome, 'help!' => \$g_help) or pod2usage($usage_rc); # Check validity of args pod2usage(-msg => "Invalid extra options passed: @ARGV", -exitval => $usage_rc) if (@ARGV); if ($g_unlock && $dest_crshome) { $PARAM_FILE_PATH = catfile ($dest_crshome, 'crs', 'install', "crsconfig_params"); } elsif ($g_patch && $dest_crshome) { $PARAM_FILE_PATH = catfile ($dest_crshome, 'crs', 'install', "crsconfig_params"); } ### Set this host name (lower case and no domain name) our $HOST = tolower_host(); die "$!" if ($HOST eq ""); # Set the following vars appropriately for cluster env ### check if run as super user our $SUPERUSER = check_SuperUser (); if (!$SUPERUSER) { error("Insufficient privileges to execute this script"); exit 1; } $patchdir = "$patchdir/$patchnum"; if (!$g_help && ! $patchdir) { error("-patchdir is a Mandatory option"); pod2usage(1); } if ( isSIHA()) { print "Detected Oracle Restart install\n"; $sihainst = 1; } else { print "Detected Oracle Clusterware install\n"; } if ($sihainst) { $cfg = crsconfig_lib->new(IS_SIHA => $sihainst, paramfile => $PARAM_FILE_PATH, osdfile => $defsfile, crscfg_trace => TRUE, crscfg_trace_file => $logfile, HOST => $HOST ); } else { $cfg = crsconfig_lib->new(paramfile => $PARAM_FILE_PATH, osdfile => $defsfile, crscfg_trace => TRUE, crscfg_trace_file => $logfile, HOST => $HOST, HAS_USER => $SUPERUSER, UNLOCK => $g_unlock ); } if ($g_unlock && $dest_crshome) { unlockPatchHome($dest_crshome); exit 0 } elsif ($g_patch && $dest_crshome) { CRSPatchhome($dest_crshome); exit 0 } #Subroutines used by this tool #Remove trailing white spaces in a string sub trim($) { my $string = shift; $string =~ s/^\s+//; $string =~ s/\s+$//; return $string; } sub parseOptions { if ($g_help) { pod2usage(0); } if ( ! $patchdir ) { $patchdir = $pwd; trace("using current directory $patchdir for patch directory"); } if( ! $patchfile ) { trace("No -patchfile specified, assuming the patch is already uncompressed" ); } if ($ohome && $chome) { error("Can't specify -oh with -och"); pod2usage(1); } } sub createPatchdir { my $cmd; my $status; #Create the patch directory if( $patchdir && ! -d $patchdir ) { $cmd = "$mkdir -p $patchdir"; trace( "Executing $cmd as $ORA_CRS_USER" ); $status = run_as_user($ORA_CRS_USER, $cmd ); } } sub unzipPatch { my $rpatchfile = ""; my $cmd = ""; my $node; my $ask_skip; my @ocp_nodes_topatch; my $olocal; my $host; my $status; my @output; my $patchfile = $ENV{'PATCH_ZIP_FILE'}; if ( -e $patchfile ) { $cmd = "$unzip -o $patchfile -d $patchdir"; trace( "Executing $cmd as $ORA_CRS_USER" ); $status = run_as_user2($ORA_CRS_USER, \@output, $cmd ); trace("unzip output is @output"); } } sub findPatchType { #Determine the type of patch my $pdir = $_[0]; my $patchtype; my @cmdout; my @output; my @outp; my $rc; my $patchcount; my $opatch = catfile ($ORA_CRS_HOME, "OPatch", "opatch"); my $cmd = "$opatch query -get_patch_type $pdir -oh $ORA_CRS_HOME"; $rc = run_as_user2($ORA_CRS_USER, \@cmdout, $cmd ); @output = grep(/This patch/, @cmdout); trace("output is @output"); @outp = split(" ", $output[0]); $patchtype = $outp[4]; trace ("Patch type is $patchtype"); return $patchtype; } sub getpatches { my @patches = (); my $patchcount; my $patchids; my $usrinput; my $bfile = catfile($patchdir, "bundle.xml"); if (-e $bfile) { @patches = getpids($patchdir); } else { print "Enter 'yes' if you have unzipped this patch to an empty directory to proceed (yes/no):"; $usrinput = ; if ($usrinput =~ m/yes/i) { ($patchcount, $patchids) = getPatchids($patchdir); @patches = split(/\,/, $patchids); } else { exit 1; } } trace("The patch ids are @patches"); return @patches } sub constructpatchlist { my $patchcount; my $patchids; my $patch; my $ptype; @patchIds = getpatches(); my $patchtype = "DB"; foreach $patch (@patchIds) { $ptype = findPatchType("$patchdir/$patch"); if ($ptype =~ m/legacy_bundle_top/) { push @gipatches, "$patchdir/$patch"; push @dbpatches, "$patchdir/$patch/custom/server/$patch"; $patchtype = $ptype; } else { push @gipatches, "$patchdir/$patch"; push @dbpatches, "$patchdir/$patch"; } } $patchType = $patchtype; } sub checkApplicable { my $home = shift; my $ohown = getoracleowner($home); my $patchlist = shift; my $rc; my @cmdout; my @output; my $cmd; my $opatch = catfile ($home, "OPatch", "opatch"); my $pdir; my $status = FAILED; #Check if the patch is applicable foreach $pdir (@$patchlist) { $cmd = "$opatch prereq CheckApplicable -ph $pdir -oh $home"; $rc = run_as_user2($ohown, \@output, $cmd); @cmdout = grep(/passed/, @output); if ((scalar(@cmdout) > 0) && ($rc == 0)) { $status = SUCCESS; } else { error("The opatch Applicable check failed for $home. The patch is not applicable for $home"); } } return $status; } sub checkConflict { my $home = shift; my $ohown = getoracleowner($home); my $patchlist = shift; my $rc; my @cmdout; my @output; my $cmd; my $opatch = catfile ($home, "OPatch", "opatch"); my $pdir; my $status = SUCCESS; foreach $pdir (@$patchlist) { #Check if there are patch conflicts. $cmd = "$opatch prereq CheckConflictAgainstOH -ph $pdir -oh $home"; $rc = run_as_user2($ohown, \@output, $cmd); @cmdout = grep(/ZOP-40/, @output); # if scalar(@cmdout) > 0, we found the msg we were looking for if ((scalar(@cmdout) > 0) && ($rc == 0)) { $status = FAILED; if (! $rollback) { error("The opatch Conflict check failed for $home. The patch $pdir is either already applied or conflicting with another patch applied in the home"); trace("The error ouput from opatch is @output"); } } } return $status; } sub checkComponent { my $home = shift; my $ohown = getoracleowner($home); my $patchlist = shift; my $rc; my @cmdout; my @output; my $cmd; my $opatch = catfile ($home, "OPatch", "opatch"); my $pdir; my $status = FAILED; foreach $pdir (@$patchlist) { #Check if the patch is applicable $cmd = "$opatch prereq CheckComponents -ph $pdir -oh $home"; $rc = run_as_user2($ohown, \@output, $cmd); @cmdout = grep(/passed/, @output); if ((scalar(@cmdout) > 0) && ($rc == 0)) { $status = SUCCESS; } else { error("The opatch Component check failed. This patch is not applicable for $home"); trace("The component check failed with following error"); trace("@output"); } } return $status; } sub getcrshome { my $crsHome; if (! $chome) { $crsHome = s_get_olr_file ("crs_home") } else { $crsHome = $chome; if ((-f $olrloc) && ($crsHome ne s_get_olr_file ("crs_home"))) { error("Incorrect clusterware home path provided for -och option"); exit 1; } } if (! -e $crsHome) { error("Clusterware home location $crsHome does not exist"); exit 1; } return $crsHome; } sub getoracleowner { my $oh = $_[0]; my $getoh_ox = "$oh/bin/oracle"; my $getoh_u; if ( -f $getoh_ox ) { my ($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size, $atime, $mtime, $ctime, $blksize, $blocks) = stat( $getoh_ox ); ($name, $passwd, $uid, $gid, $quota, $comment, $gcos, $dir, $shell) = getpwuid( $uid ); $getoh_u = $name; trace( "Oracle user for $oh is $getoh_u" ); } else { error("unable to get oracle owner for $oh"); exit 1; } return $getoh_u; } sub findHomes { my @tmps = (); my $db = ""; my $oh = ""; my @ohs = (); my $getoh_ox = ""; my $getoh_u = ""; my $getoh_n = ""; my @ocp_ohs = (); my @ocp_databases; my @ocp_ous; my $ocp_dblist; my $ocp_ohlist; my @tmp_ohlist; my @tmp_ohlist1; my $ocp_oulist; my @out; my $rc; my $cmd; my $path; if ($ohome) { @tmp_ohlist = split( /\,/, $ohome); foreach $path (@tmp_ohlist) { $path =~ s/\/$//; push (@ocp_ohs, $path); } } elsif ($chome) { @ocp_ohs = $chome } else { my $srvctl = catfile ($ORA_CRS_HOME, "bin", "srvctl"); trace( "Looking for configured databases on node $HOST" ); @ocp_databases = `$srvctl config`; chomp @ocp_databases; $ocp_dblist = join " ", @ocp_databases; trace( "Databases configured on node $HOST are: $ocp_dblist" ); trace( "Determining ORACLE_HOME paths for configured databases" ); $ocp_ohlist = ""; @ocp_ohs = (); foreach $db ( @ocp_databases ) { #trace( "Looking at database $db" ); $cmd = "$srvctl config database -d $db"; @out = system_cmd_capture($cmd); $rc = shift @out; if ( $rc != 0 ) { trace("try with srvctl config database -v"); $cmd = "$srvctl config database -v"; @out = system_cmd_capture($cmd); $rc = shift @out; if ($rc == 0) { foreach my $str (@out) { my ($dbname, $dbhome, $dbver) = split(" ", $str); trace("db name is $dbname"); trace("db home is $dbhome"); trace("db ver is $dbver"); chomp $dbname; chomp $dbhome; $dboh{$dbname} = trim($dbhome); trace("Oracle home for database $dbname is $dboh{$dbname}"); } } else { error("Not able to retreive database home information"); exit 1; } last; } else { @tmps = grep(/Oracle home:/, @out); trace("output is @tmps"); my ($dummy, $ohpath) = split( /\:/, $tmps[0] ); $dboh{$db} = trim($ohpath); trace("Oracle home for database $db is $dboh{$db}"); } } #create hash oracle home to dbs foreach $db (keys%dboh) { if(defined($ohdb{$dboh{$db}})) { $ohdb{$dboh{$db}} = "$ohdb{$dboh{$db}}:$db"; } else { $ohdb{$dboh{$db}} = "$db"; } } #get unique oracle home list @ocp_ohs = keys%ohdb; foreach $oh (keys%ohdb) { trace( "Oracle Home $oh is configured with Database\(s\)\-\> $ohdb{$oh}"); } } foreach $oh ( @ocp_ohs ) { my $ohown = getoracleowner($oh); $ohowner{$oh} = $ohown; } trace("oracle home list is @ocp_ohs"); return @ocp_ohs; } sub isSIHA { my $ret= FAILED; my $local_only = get_config_key("ocr", "local_only"); if ($local_only =~ m/true/i) { $ret = SUCCESS; } return $ret; } sub get_config_key { my $src = $_[0]; my $key = $_[1]; $src =~ tr/a-z/A-Z/; my ($val, $cfgfile); if ($src eq 'OCR') { $cfgfile = $ocrloc; } elsif ($src eq 'OLR') { $cfgfile = $olrloc; } open (CFGFL, "<$cfgfile") or return $val; while () { if (/^$key=(\S+)/) { $val = $1; last; } } close (CFGFL); return $val; } sub findHomeType { my $home = $_[0]; my $crshome; my $type; my $local_only = s_get_config_key("ocr", "local_only"); $crshome = getcrshome(); if (($home eq $crshome) && ($local_only =~ m/true/i)) { $type = "HA"; } elsif (($home eq $crshome) && ($local_only =~ m/false/i)) { $type = "CRS"; } else { $type = "DB"; } return $type; } sub applyPatch { my $home = shift; my $ohown = getoracleowner($home); my $patchlist = shift; my $cmd = ""; my $status; my $opatch = catfile ($home, "OPatch", "opatch"); my $pdir; my @output = (); if ($OS eq "AIX" ) { unloadAIXfiles(); } foreach $pdir (@$patchlist) { $cmd = "$opatch napply $pdir -local $op_silent -oh $home"; trace("Executing command $cmd as $ohown"); $status = run_as_user2($ohown, \@output, $cmd ); trace("status of apply patch is $status"); trace("The apply patch output is @output"); if (0 != $status) { error("patch $pdir apply failed for home $home"); last; } else { error("patch $pdir apply successful for home $home"); } } } sub rollbackPatch { my $home = shift; my $ohown = getoracleowner($home); my $patchlist = shift; my $status = 0; my @output = (); my $opatch = catfile ($home, "OPatch", "opatch"); my $pnum; my $skip; if ($OS eq "AIX" ) { unloadAIXfiles(); } foreach $pnum (@patchIds) { $skip = FALSE; trace("checking if patch $pnum exists in $home"); if (checkRollback($pnum, $home)) { my $cmd = "$opatch rollback -id $pnum -local -silent -oh $home"; trace("Executing command $cmd as $ohown"); $status = run_as_user2($ohown, \@output, $cmd ); trace("status of rollback patch is $status"); trace("The rollback patch output is @output"); } else { trace("$pnum does not exist . skipping rollback"); $skip = TRUE; } if (0 != $status) { error("patch $pnum rollback failed for home $home"); last; } else { if (! $skip) { error("patch $pnum rollback successful for home $home"); } } } } sub setrdbmsfileperms { my $home = $_[0]; my $cmd ; my @out; my $rc; $cmd = "$home/rdbms/install/rootadd_rdbms.sh"; @out = system_cmd_capture("$cmd"); $rc = shift @out; if (0 == $rc) { trace("setrdbmsfileperms succeeded"); } else { error("setrdbmsfileperms failed. output is @out"); } } sub removeproc { my $home = $_[0]; my $proc = $_[1]; my $cmd ; my @out; my $rc; trace("Invoking removeproc to clean oracle client procs"); my $fpath = catfile($home, "bin", $proc); $cmd = "$fuser -k $fpath"; @out = system_cmd_capture("$cmd"); $rc = shift @out; if (0 == $rc) { trace("fuser command success. output for $fpath is @out"); } else { trace("fuser command output for $fpath is @out"); } } sub getcrsversion { my $crsctlbin = catfile ($ORA_CRS_HOME, 'bin', 'crsctl'); my @cmd; if ($sihainst) { @cmd = ($crsctlbin, 'query', 'has', 'releaseversion'); } else { @cmd = ($crsctlbin, 'query', 'crs', 'activeversion'); } my @out = system_cmd_capture(@cmd); my $rc = shift @out; my @versionarr = (0, 0, 0, 0, 0); my $verstring = $out[0]; if ($rc == 0) { $verstring =~ m/\[(\d*)\.(\d*)\.(\d*)\.(\d*)\.(\d*)\].*$/; @versionarr = ($1, $2, $3, $4, $5); trace("crs version is @versionarr"); } else { error ("@cmd ... failed rc=$rc with message:\n @out \n"); } return @versionarr; } sub PerformDBPatch { my $home = shift; my $patchlist = shift; my $ohown = $ohowner{$home}; my $cmd; my $status; my @output = (); my $pdir; my @crs_version = getcrsversion(); my $issiha = isSIHA(); if (Stopdbhomeres($home, $issiha, $ohown)) { if ($crs_version[0] == 11 && $crs_version[1] == 2 && $crs_version[2] == 0 && $crs_version[3] == 2) { #Bug 10131381 removeproc($home, "oracle"); } } foreach $pdir (@$patchlist) { if (-e "$pdir/custom/scripts/prepatch.sh") { $cmd = "$pdir/custom/scripts/prepatch.sh -dbhome $home"; } else { $cmd = "true"; } $status = run_as_user2 ($ohowner{$home}, \@output, $cmd); } if ($status == 0) { trace ("prepatch execution for DB home ... success"); if ( $rollback ) { rollbackPatch($home, \@$patchlist); } else { applyPatch($home, \@$patchlist); } } else { error ("prepatch execution for DB home ... failed"); exit 1; } foreach $pdir (@$patchlist) { if (-e "$pdir/custom/scripts/postpatch.sh") { $cmd = "$pdir/custom/scripts/postpatch.sh -dbhome $home"; } else { $cmd = "true"; } $status = run_as_user2 ($ohowner{$home}, \@output, $cmd); } if ($status == 0) { trace ("postpatch execution for DB home ... success"); } else { error ("postpatch execution for DB home ... failed"); exit 1; } } sub gentimeStamp { my ($sec, $min, $hour, $day, $month, $year) = (localtime) [0, 1, 2, 3, 4, 5]; $month = $month + 1; $year = $year + 1900; my $ts = sprintf("%04d-%02d-%02d_%02d-%02d-%02d",$year, $month, $day, $hour, $min, $sec); return $ts; } sub getPatchids { my $searchdir = $_[0]; my $ids; my $line; my $idx; my @dbids = <$searchdir/*>; foreach $line (@dbids) { chomp($line); if ( -d $line) { $idx++; my @temp = split(/\//, $line); $ids = $ids . $temp[-1] . ","; } } chop $ids; return ($idx, $ids); } sub getpids { my $searchdir = $_[0]; my $bfile = catfile($searchdir, "bundle.xml"); my @list; my @out; my $ids; my $tag; my @pids; @list = read_file( $bfile); trace("Bundle.xml content is @list"); @out = grep(/entity location/, @list); foreach my $rec (@out) { chomp($rec); if ($rec =~ m/custom/) { next; } else { $rec =~ s/<|>|"//g; ($tag, $ids) = split(/=/,$rec); push @pids, $ids; } } trace("The patch ids are @pids"); return @pids; } sub isServerready { my $crsctl = catfile ($ORA_CRS_HOME, "bin", "crsctl"); my @output; my $rc; my $retries = 360; my $ready = FALSE; my $grep_val = "STATE=ONLINE"; my @cmdout; while ( $retries) { @output = system_cmd_capture($crsctl, 'stat', 'resource', '-c' , $HOST); $rc = shift @output; @cmdout = grep(/$grep_val/, @output); if (scalar(@cmdout) > 0) { $ready = TRUE; last; } trace ("Waiting for Server assignments"); sleep (5); $retries--; } if ($ready) { trace ("Server assignments completed. Ready to start databases"); } else { error ("Timed out waiting for server assignments"); } return $ready; } sub checkClusterstat { my $crsctl = catfile ($ORA_CRS_HOME, "bin", "crsctl"); my @output; my $rc; my $grep_val = "4537|4529|4533"; my @cmdout; my $nodelist = $CFG->params('NODE_NAME_LIST'); my @nodes = split(/,/, $nodelist); my $node; my $count = 0; foreach $node (@nodes) { if ($node !~ /\b$HOST\b/i) { @output = system_cmd_capture($crsctl, 'check', 'cluster', '-n' , $node); $rc = shift @output; @cmdout = grep(/$grep_val/, @output); if (scalar(@cmdout) > 0) { error("Clusterware stack up on node $node"); $count++; } } } if ($count == 0) { trace ("Clusterware stack is not running on remote nodes"); } else { error ("Clusterware stack is running on remote nodes"); print "Refer to opatch auto help for patching shared homes and follow the steps\n"; exit 1; } } sub checkdbhomestat { my $home = $_[0]; my $ohown = $ohowner{$home}; my @cmdout; my $nodelist = $CFG->params('NODE_NAME_LIST'); my @nodes = split(/,/, $nodelist); my $node; my $count = 0; my $rc; my $retstat; foreach $node (@nodes) { if ($node !~ /\b$HOST\b/i) { @cmdout = Statusdbhomeres($home, 0, $node,$ohown); my $retstat = shift @cmdout; trace("ret code for status db home on $node is $retstat"); if (($retstat == 0) && (scalar(@cmdout) > 0)) { error("Database home resources for $home are running on node $node"); $count++; } } } if ($count == 0) { trace ("Database home resources for $home is not running on remote nodes"); } else { error ("Database home resources for $home is running on remote nodes"); print "Refer to opatch auto help for patching shared homes and follow the steps\n"; exit 1; } } sub checkConflictforhomes { my @homelist = @_; my $chkconflict = SUCCESS; my $count; foreach my $home (@homelist) { my $status; trace("Processing oracle home $home"); $homeType = findHomeType($home); trace("Home type of $home is $homeType"); if ($homeType eq "CRS" || $homeType eq "HA") { if ( $rollback) { $status = checkComponent ($home, \@gipatches); } else { $status = (checkComponent ($home, \@gipatches) && checkConflict ($home, \@gipatches)); } } else { if ( $rollback) { $status = checkComponent ($home, \@dbpatches); } else { $status = (checkComponent ($home, \@dbpatches) && checkConflict ($home, \@dbpatches)); } } trace("Status of component/conflict check for $home is $status"); if ($status) { trace(" Conflict check passes for oracle home $home"); } else { error("Conflict check failed for oracle home $home"); $count++; } } if ($count == 0) { trace ("Conflict check passed for all oracle homes"); } else { error ("Conflict check failed"); $chkconflict = FAILED; } return $chkconflict; } sub checkOCM { my $silentflg; my $ocmrspfile; #check if opatch is bundled with OCM if ( -e "$ORA_CRS_HOME/OPatch/ocm/bin/emocmrsp") { if ($ocmrf) { $ocmrspfile = $ocmrf; } else { error("OPatch is bundled with OCM, Enter the absolute OCM response file path:") ; $ocmrspfile = ; chomp $ocmrspfile; } if ( -e $ocmrspfile) { $silentflg = "-silent -ocmrf $ocmrspfile"; } else { error("Invalid response file path, To regenerate an OCM response file run $ORA_CRS_HOME/OPatch/ocm/bin/emocmrsp"); exit 1; } } else { #report( 0, 0, "$opatch is not bundled with OCM" ); $silentflg = "-silent"; } return $silentflg; } sub ispathShared { my $path = $_[0]; my @capout = (); my $rc; my $user = $CFG->params('ORACLE_OWNER'); my $nodelist = $CFG->params('NODE_NAME_LIST'); my $status; my @nodes = split(/,/, $nodelist); trace("The cluster nodes are @nodes"); if (scalar(@nodes) == 1) { trace("Single node cluster"); $status = FALSE; return $status; } my $CLUVFY = catfile( $ORA_CRS_HOME, 'bin', 'cluvfy'); my @program = ($CLUVFY, 'comp ssa', '-t software', '-s' , $path , '-n', $nodelist, '-display_status'); my $status; trace("checking if path $path is shared"); # run as specific user, if requested $rc = run_as_user2($user, \@capout, @program); trace("return code for shared check is $rc"); trace("output of sharedness check is @capout"); if (scalar(grep(/EFAIL/, @capout)) > 0) { $status = ERROR; } elsif ((scalar(grep(/VFAIL/, @capout))) > 0){ $status = FALSE; } else { trace("The path $path is shared "); $status = TRUE; } return $status; } sub isHomesShared { my @homelist = @_; my $path; my $isshared = FALSE; my $usrinput; foreach my $home (@homelist) { my $homeType = findHomeType($home); if (($homeType eq "CRS") || ($homeType eq "HA")) { $path = catdir( $home, 'crs', 'install'); } else { $path = $home; } $isshared = ispathShared($path); trace("the ishared value is $isshared"); if ( $isshared == TRUE) { if ($homeType eq "CRS") { checkClusterstat(); } else { checkdbhomestat($home); } } elsif ($isshared == ERROR) { print "\n"; print "Unable to determine if $home is shared oracle home\n"; print "Enter 'yes' if this is not a shared home or if the prerequiste actions are performed to patch this shared home (yes/no):"; $usrinput = ; if ($usrinput =~ m/yes/i) { next; }else { print "\n"; print "Refer to opatch auto help (opatch auto -h) for patching shared homes and follow the steps\n"; exit 1; } } else { trace("The oracle home $home is not shared"); } } } sub getOpatchver { my $home = shift; my $ohown = getoracleowner($home); my $rc; my @cmdout; my @output; my $cmd; my $opatch = catfile ($home, "OPatch", "opatch"); my $tmpstr; $cmd = "$opatch version -oh $home"; $rc = run_as_user2($ohown, \@output, $cmd); @cmdout = grep(/Version/, @output); $cmdout[0] =~ s/ //g; my ($txt , $ver) = split(/:/, $cmdout[0]); trace("opatch version in oracle home $home is $ver"); chomp $ver; return $ver; } sub checkOpatchver { my $home = shift; my $ohown = getoracleowner($home); my $patchlist = shift; my $rc; my @cmdout; my @output; my $cmd; my $pdir; my $version; my $opatch = catfile ($home, "OPatch", "opatch"); my $status = FAILED; $version = getOpatchver($home); foreach $pdir (@$patchlist) { $cmd = "$opatch util checkMinimumOPatchVersion -ph $pdir -version $version -oh $home"; $rc = run_as_user2($ohown, \@output, $cmd); @cmdout = grep(/true/, @output); if ((scalar(@cmdout) > 0) && ($rc == 0)) { $status = SUCCESS; } else { error("The opatch minimum version check for patch $pdir failed for $home"); trace("The opatch version check failed with following error"); trace("@output"); } } return $status; } sub checkOpatchverforhomes { my @homelist = @_; my $chkver = SUCCESS; my $count; foreach my $home (@homelist) { my $status; trace("Processing oracle home $home"); $homeType = findHomeType($home); trace("Home type of $home is $homeType"); if ($homeType eq "CRS" || $homeType eq "HA") { $status = checkOpatchver ($home, \@gipatches); } else { $status = checkOpatchver ($home, \@dbpatches); } trace("Status of opatch version check for $home is $status"); if ($status) { trace("Opatch version check passed for oracle home $home"); } else { error("Opatch version check failed for oracle home $home"); $count++; } } if ($count == 0) { trace ("Opatch version check passed for all oracle homes"); } else { error ("Opatch version check failed"); $chkver = FAILED; } return $chkver; } sub checkRollback { my $pid = $_[0]; my $home = $_[1]; my $ohown = getoracleowner($home); my $rc; my @cmdout; my @output; my $cmd; my $opatch = catfile ($home, "OPatch", "opatch"); my $status = FAILED; $cmd = "$opatch prereq checkRollbackable -id $pid -oh $home"; $rc = run_as_user2($ohown, \@output, $cmd); @cmdout = grep(/passed/, @output); if ((scalar(@cmdout) > 0) && ($rc == 0)) { $status = SUCCESS; } else { error("The patch $pid does not exist in $home"); trace("The rollback check output is @output"); } return $status; } sub unloadAIXfiles { my $cmd = "/usr/sbin/slibclean"; my @out = system_cmd_capture("$cmd"); my $rc = shift @out; trace("slibclean output is @out"); } ##MAIN BODY parseOptions(); $ORA_CRS_HOME = getcrshome(); $ORA_CRS_USER = getoracleowner($ORA_CRS_HOME); $op_silent = checkOCM(); trace("silent mode option is $op_silent"); createPatchdir(); #set patchdir permissions s_set_perms("0775", $patchdir); unzipPatch(); constructpatchlist(); trace("GI patches are @gipatches"); trace("DB patches are @dbpatches"); #check if clusterware running my $status = isSIHA(); if (! ($status == SUCCESS)) { $crs_running = check_service ("cluster", 2); } else { $crs_running = check_service ("ohasd", 2); } if ((! $crs_running) && (! $chome)) { print "Clusterware is either not running or not configured. You have the following 2 options\n"; print "1. Configure and Start the Clusterware on this node and re-run the tool\n"; print "2. or Run the tool with the -och option and then invoke tool with -oh to patch the RDBMS home\n"; exit 1; } @dbhomes = findHomes(); @homestopatch = @dbhomes; @homestostart = @dbhomes; if (($patchType =~ m/legacy_bundle_top/) && (! $ohome) && (! $chome)) { unshift (@homestostart, $ORA_CRS_HOME); } if (($patchType =~ m/legacy_bundle_top/) && (! $ohome) && (! $chome)) { push (@homestopatch , $ORA_CRS_HOME); } if (! checkOpatchverforhomes(@homestopatch)) { error("update the opatch version for the failed homes and retry"); exit 1; } if (! isSIHA()) { isHomesShared(@homestopatch); } $isconflictok = checkConflictforhomes(@homestopatch); if ($isconflictok == SUCCESS) { foreach my $home (@homestopatch) { my $status; trace("Processing oracle home $home"); $homeType = findHomeType($home); trace("Home type of $home is $homeType"); if ($homeType eq "CRS") { unlockCRSHomeforpatch(); #wait for complete stack to shutdown. sleep(120); trace("Waiting for complete CRS stack to stop"); removeproc($home, "crsctl.bin"); $status = checkApplicable ($home, \@gipatches); trace("Status of Applicable check for $home is $status"); if (($status == SUCCESS) || ($rollback)) { if ( $rollback ) { rollbackPatch($home, \@gipatches); } else { applyPatch($home, \@gipatches); } } else { error("Patch Applicable check failed for $home"); Instantiatepatchfiles (); StartCRS(); exit 1; } } elsif ($homeType eq "HA") { unlockHAHomeforpatch(); removeproc($home, "crsctl.bin"); $status = checkApplicable ($home, \@gipatches); trace("Status of Applicable check for $home is $status"); if (($status == SUCCESS) || ($rollback)) { if ( $rollback ) { rollbackPatch($home, \@gipatches); } else { applyPatch($home, \@gipatches); } } else { error("Patch Applicable check failed for $home"); Instantiatepatchfiles (); StartHA(); exit 1; } } else { trace("Performing DB patch"); $status = checkApplicable ($home, \@dbpatches); trace("Status of Applicable check for $home is $status"); if (($status == SUCCESS) || ($rollback)) { PerformDBPatch($home, \@dbpatches); } else { error("Patch Applicable check failed for $home"); exit 1; } } } } else { error ("Conflict-Check has failed . Please refer to $logfile for details"); exit 1; } foreach my $home (@homestostart) { #post patch sequence trace("Performing Post patch actions"); trace("norestart flag is set to $norestart"); $homeType = findHomeType($home); my $ohown = $ohowner{$home}; my $issiha = isSIHA(); if ($homeType eq "CRS") { if (-f $olrloc) { trace("Performing Post patch actions for Grid Home $home"); setrdbmsfileperms($home); CRSPatch($norestart); } } elsif ($homeType eq "HA") { if (-f $olrloc) { trace("Performing Post patch actions for HA Home $home"); setrdbmsfileperms($home); HAPatch($norestart); } } else { trace("Performing Post Patch start action for DB Home $home"); if (! $norestart && isServerready() ) { Startdbhomeres($home, $issiha,$ohown); } } } 0;