Viewing file: emdpatch.pl (18.4 KB) -rw-rw-rw- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
#
# Copyright (c) 2003, 2005, Oracle. All rights reserved.
#
# DESCRIPTION
# EM script to patch OPatch inside ORACLE_HOME
#
# USAGE
# perl emdpatch.pl <patch-id> [<options>] [<patch-loc>]
#
# patch-id : PSE patch number
# patch-loc : Location of patch files
# options
# -invPtrLoc <OUI-inventory-loc> : Location of OUI inventory
# -oh <ORACLE_HOME> : Location of ORACLE_HOME
# -no_inventory : Ignore OUI inventory update
#
# NOTES
# <other useful comments,qualifications,etc>
#
# MODIFIED (MM/DD/YY)
# shgangul 04/18/05 - shgangul_opatch_backup_rollback_emdpatch
# shgangul 04/15/05 - Fix minor bug in pattern matching version string
# shgangul 01/27/05 - Bounce agent while patching agent
# shgangul 01/21/05 - shgangul_opatch_add_emdpatch_pl_ver1
# shgangul 01/20/05 - Created
# --- Set up necessary variables for proper running of this environment ---
use English;
use strict;
use File::Basename;
use File::Find;
use File::Path;
use File::Spec();
use File::Spec::Functions qw (:ALL);
use File::Copy();
use File::Copy;
my $scriptName = basename($0);
my $scriptDir = dirname($0);
# ------ Initialize global variables -------------------------------------
my $patch_path = rel2abs($scriptDir);
# This script will be located inside OPatch directory. So patch path
# is actually a level over that
$patch_path = dirname($patch_path);
my $patch_id = $ARGV[0]; # patch number (e.g. 2944899)
my $patch_loc = ''; # location of patch files
my $inv_loc = ''; # OUI inventory location
my $PERL = $^X; # Perl executable
my $EMDROOT = $ENV{'EMDROOT'};
my $ORACLE_HOME = $ENV{'ORACLE_HOME'};
my $PERL5LIB = $ENV{'PERL5LIB'};
my $FAIL = (1<<9);
my $blackout = "${patch_id}-$$"; # blackout name
my $wantMask = ''; # used as mask for wantName()
my $sleepTime = 60; # used to give job a chance to suspend
my $status = $FAIL; # used to display if a patch is successful
my $stopAgent = 0; # 1 = stopped; 0 = failed
my $startBlackout = 0; # 1 = started; 0 = failed
my $retStatus = 0; # used for testing system commands
# --------------------- OSD platform-specific ---------------------------
my $NULL_DEVICE = '/dev/null';
my $EMCTL = 'emctl';
my $OPATCHPL = 'opatch.pl';
my $PATCHSH = 'patch.sh';
my $RMFR = '/bin/rm -fr';
my $SHELL = '/bin/sh';
my $TAR = '/bin/tar';
my $INVPTRLOCFILEPATH = '';
my $INVPTRLOCFILE = 'oraInst.loc';
# --------------------- Subroutines -------------------------------------
# setupOSD()
#
# Setup OSD commands
#
sub setupOSD
{
if (!defined $EMDROOT || $EMDROOT eq '')
{
abortf("EMDROOT not defined.");
}
if (!defined $ORACLE_HOME || $ORACLE_HOME eq '')
{
abortf("ORACLE_HOME not defined.");
}
if (!defined $PERL5LIB || $PERL5LIB eq '')
{
$PERL5LIB = getPERL5LIB();
$ENV{'PERL5LIB'} = $PERL5LIB;
}
# This will enable emctl to return error codes, not required for
# Unix, add it anyway
if ( !defined $ENV{NEED_EXIT_CODE} )
{
$ENV{NEED_EXIT_CODE} = 1;
}
if (onWindows())
{
$NULL_DEVICE = 'NUL';
$EMCTL = 'emctl.bat';
$PATCHSH = 'patch.bat';
$RMFR = 'ERASE /S /Q';
$SHELL = 'CMD.EXE';
$TAR = 'TAR';
# Convert path separators for windows case
$EMDROOT =~ s/\//\\/g;
$ORACLE_HOME =~ s/\//\\/g;
$PERL5LIB =~ s/\//\\/g;
}
# Use perl utility to concat file names
# $EMCTL = "$EMDROOT/bin/$EMCTL";
$EMCTL = File::Spec -> catfile($EMDROOT, "bin", $EMCTL);
}
# parseArgs()
#
# Parse the arguments and store them away for future use
#
sub parseArgs
{
my $opt = '';
my $argcount = scalar(@ARGV);
$patch_id = $ARGV[0]; # PSE number
if (!defined $patch_id || $patch_id eq '')
{
abortf("PSE number not specified.")
}
for (my $i = 1 ; $i < $argcount ; $i++)
{
$opt = $ARGV[$i];
if (substr($opt,0,1) eq '-')
{
if (index('-invPtrLoc', $opt) == 0)
{
if ($i < ($argcount - 1))
{
$i += 1;
$inv_loc = "-invPtrLoc $ARGV[$i]";
}
}
elsif (index('-oh', $opt) == 0)
{
if ($i < ($argcount - 1))
{
$i += 1;
$ORACLE_HOME = $ARGV[$i];
$ENV{'ORACLE_HOME'} = $ORACLE_HOME;
}
}
elsif (index('-no_inventory', $opt) == 0)
{
$inv_loc = '-no_inventory';
}
}
elsif ($patch_loc eq '')
{
if (-d "$opt")
{
$patch_loc = $opt;
$patch_path = $patch_loc;
}
}
}
if ( $inv_loc eq '' )
{
$INVPTRLOCFILEPATH = File::Spec->catfile($ORACLE_HOME, $INVPTRLOCFILE);
if (-f $INVPTRLOCFILEPATH)
{
$inv_loc = "-invPtrLoc $INVPTRLOCFILEPATH";
}
}
}
#
# getPERL5LIB()
#
# Return the equated value of $PERL5LIB
#
sub getPERL5LIB
{
my $PERL5LIB = $ENV{'PERL5LIB'};
if (!defined $PERL5LIB || $PERL5LIB eq '')
{
$PERL5LIB = $ENV{'PERLLIB'};
if (!defined $PERL5LIB || $PERL5LIB eq '')
{
for (my $i = 0 ; $i < @INC ; $i++)
{
if ($i == 0)
{
$PERL5LIB = $INC[$i];
}
else
{
$PERL5LIB .= ':' . $INC[$i];
}
}
}
}
return $PERL5LIB;
}
# currentDir()
#
# Return absolute value of working directory
#
sub currentDir
{
my $curDir = '.';
if (onWindows())
{
chomp($curDir = `cd`);
}
else
{
chomp($curDir = `pwd`);
}
return $curDir;
}
# onWindows()
#
# Return true if running under MS Windows
#
sub onWindows
{
return ($^O eq 'MSWin32');
}
#
# echodo(<cmd>)
#
# Display the command and execute it
#
# Return exit status
#
sub echodo($)
{
my ($cmd) = @_;
printf("\n%s\n", $cmd);
return system($cmd);
}
#
# logf(<message>)
#
# Display the message with timestamp
#
#
sub logf($)
{
my ($msg) = @_;
printf("\n%s - %s\n", scalar(localtime()), $msg);
}
#
# errorf(<message>)
#
# Display an error message
#
#
sub errorf($)
{
my ($msg) = @_;
printf("\nError: %s\n", $msg);
}
#
# abortf(<message>,<status>)
#
# Display a fatal error message and exit
#
#
sub abortf($;$)
{
my ($msg, $status) = @_;
$status = $FAIL if (!defined $status);
printf("\nFatal Error: %s\n", $msg);
logf("Patching aborted.");
exit statusf($status);
}
#
# statusf(<status>)
#
# Returns the exit status of failed command
#
#
sub statusf($)
{
my ($status) = @_;
$status = $FAIL if (!defined $status);
return ($status >> 8);
}
#
# setOutputAutoflush()
#
# Set STDOUT,STDERR to autoflush
#
sub setOutputAutoflush
{
my $outHandle = select(STDOUT);
$| = 1; # set OUTPUT_AUTOFLUSH
select(STDERR);
$| = 1; # flush std error as well
select($outHandle); #reset handle back to original
}
# wantName()
#
# Helper routine called by findFile to find matching file
#
sub wantName
{
# Could be source of the famous unicode problem in perl
# if ($_ =~ m/^$wantMask$/)
if ($_ eq $wantMask)
{
$wantMask = $File::Find::name;
}
}
# findFile(<path>,<mask>)
#
# Return the absolute name of file matching a pattern mask
#
sub findFile($$)
{
my ($path, $mask) = @_;
$wantMask = $mask;
find(\&wantName, $path);
if ($wantMask eq $mask)
{
return '';
}
# Update directory name to take care of windows case
if ( onWindows() ) {
$wantMask =~ s/\//\\/g;
}
return $wantMask;
}
# Returns true(1) if the home is agent home, and false(0) otherwise
sub isAgentHome
{
if ($EMDROOT eq $ORACLE_HOME)
{
return 1;
}
else
{
return 0;
}
}
# Copy directory recursively to destination
# result = copy_dir ( <Source directory>, <Destination directory> )
# Copies the contents of Source directory to Destination directory
sub copy_dir
{
my $src = $_[0]; # Source Directory
my $dst = $_[1]; # Destination Directory
my $result = $FAIL; # Default to failure
# Try to copy if both source and destination are directories
if ( ( -d $src ) && ( -r $src ) && ( -d $dst ) && ( -w $dst) )
{
$src =~ s/\\/\//g;
$dst =~ s/\\/\//g;
find
(
sub
{
my $targetdir = $File::Find::dir;
my $target = $targetdir;
$targetdir = $dst . substr($targetdir, length($src));
mkpath( $targetdir ) if not -e $targetdir;
my $file = $_;
my $source = "./" . $file;
my $dest = "$targetdir/$file";
if ( ($file ne $scriptName) && (-f $source) )
{
copy ($source, $dest);
chmod 0755, $dest;
}
},
$src
);
$result = 0; # Copy success
}
return $result;
}
# returns OPatch version. Expects OPatch directory as input
sub get_opatch_version
{
my $opatch_dir = $_[0];
my $opatch_dir_abs = rel2abs($opatch_dir);
my $opatch_exec = File::Spec -> catfile($opatch_dir_abs, "opatch");
if ($OSNAME =~ m#Win32#)
{
$opatch_exec = File::Spec -> catfile($opatch_dir_abs, "opatch\.bat");
}
my $version = "0.0.0.0.0";
if ( -x $opatch_exec)
{
my $system_command = $opatch_exec . " version";
my $sys_call_result = qx/$system_command/;
my $return_code = $CHILD_ERROR >> 8;
if ( $return_code == 0 )
{
($version) = ($sys_call_result =~ /.* ([0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*).*/);
}
}
return $version;
}
# Compare two OPatch versions <1> and <2>.
# If <1> Greater than <2> returns 1
# If <1> Equal to <2> returns 0
# If <1> Less than <2> returns -1
sub compare_opatch_version
{
my $opatchv1 = $_[0];
my $opatchv2 = $_[1];
my $cmpresult = 0;
# Now compare
my (@v1) = ($opatchv1 =~ /([0-9][0-9]*)\.([0-9][0-9]*)\.([0-9][0-9]*)\.([0-9][0-9]*)\.([0-9][0-9]*).*/);
my (@v2) = ($opatchv2 =~ /([0-9][0-9]*)\.([0-9][0-9]*)\.([0-9][0-9]*)\.([0-9][0-9]*)\.([0-9][0-9]*).*/);
# Loop and compare the versions
for (my $i = 0; $i < 5; $i ++)
{
if ($v1[$i] > $v2[$i])
{
$cmpresult = 1;
last;
}
elsif ($v1[$i] < $v2[$i])
{
$cmpresult = -1;
last;
}
}
return $cmpresult;
}
# --------------------- Main program -------------------------------------
# make sure output is flushed
setOutputAutoflush();
# make sure arguments are correct
parseArgs();
# make sure environment is correct
setupOSD();
# output a little info for feedback
printf("PERL = $^X\n");
printf("SCRIPT = $0\n");
printf("PERL5LIB = $PERL5LIB\n");
printf("EMDROOT = $EMDROOT\n");
printf("ORACLE_HOME = $ORACLE_HOME\n");
# Sleep for 60 secs if patching agent only
if (isAgentHome())
{
# wait for a minute - Job System requirement
logf("Working...");
sleep($sleepTime);
}
# Step 1: add blackout for all targets on host
if ( ($startBlackout == 0) && isAgentHome())
{
logf("Attempting to add blackout for host...");
if ( onWindows() )
{
$retStatus = echodo("$EMCTL start blackout $blackout -nodelevel");
}
else
{
$retStatus = echodo("$EMCTL start blackout $blackout -nodelevel 2>&1");
}
# right shift retstatus by 8 to get the correct error code
$retStatus = statusf($retStatus);
if ($retStatus != 0)
{
errorf("failed to start blackout: status = " . $retStatus);
$startBlackout = 0;
}
else
{
logf("Blackout succeeded");
$startBlackout = 1;
}
}
# Step 2: shutdown the running Oracle Agent
if ( ($startBlackout == 1) && isAgentHome())
{
logf("Attempting to stop Oracle Management Agent...");
if ( onWindows() )
{
$retStatus = echodo("$EMCTL stop agent");
}
else
{
$retStatus = echodo("$EMCTL stop agent 2>&1");
}
# right shift retstatus by 8 to get the correct error code
$retStatus = statusf($retStatus);
if ($retStatus != 0)
{
errorf("failed to stop Management Agent: status = " . $retStatus);
$stopAgent = 0;
}
else
{
logf("Agent stopped successfully");
$stopAgent = 1;
}
}
# Step 3: apply the patch here
if ((!isAgentHome()) || (isAgentHome() && $stopAgent == 1))
{
# Apply the patch here
# Basically copy the OPatch from present location to ORACLE_HOME/OPatch
if (isAgentHome())
{
logf("Attempting to patch Oracle Management Agent...");
}
else
{
logf("Attempting to patch non-Agent Oracle Home...");
}
# either call opatch here or a substitute mechanism
# We expect this script to be inside OPatch
my $opatch_src = File::Spec -> catfile($patch_path, "OPatch");
# opatch destination to ORACLE_HOME
my $opatch_dst = File::Spec -> catfile($ORACLE_HOME, "OPatch");
# Default result to FAIL
my $result = $FAIL;
# Copy OPatch to ORACLE_HOME here
if ($opatch_src ne '')
{
logf("OPatch located in the patch...");
if ( ! -e $opatch_dst ) # OPatch does not exist
{
logf("OPatch not present in ORACLE_HOME...");
logf("Patching OPatch to ORACLE_HOME...");
mkpath( $opatch_dst ) if not -e $opatch_dst;
$result = copy_dir ($opatch_src, $opatch_dst);
logf("Patching OPatch to ORACLE_HOME complete. Result = $result");
# Set status
if ($result == 0)
{
$status = 0;
}
}
else
{
if ( -d $opatch_dst )
{
logf("ORACLE_HOME has OPatch already present...");
# Calculate the versions
my $version_dst = get_opatch_version($opatch_dst);
my $version_src = get_opatch_version($opatch_src);
logf("Version of OPatch in ORACLE_HOME is $version_dst");
logf("Version of OPatch in the patch is $version_src");
# returns 1 if version_src > version_dst and 0 if
# version_src = version_dst and -1 if
# version_src < version_dst
my $version_cmp_result = compare_opatch_version ($version_src, $version_dst);
# Patch if source OPatch is higher or equal to one
# inside the ORACLE_HOME
if ( $version_cmp_result >= 0 )
{
# Backup the original OPatch before proceeding
my $opatch_bak_dir = "OPatch_" . $version_dst;
my $opatch_bak = File::Spec -> catfile($ORACLE_HOME, $opatch_bak_dir);
logf("Backing up original OPatch to $opatch_bak...");
mkpath( $opatch_bak ) if not -e $opatch_bak;
$result = copy_dir ($opatch_dst, $opatch_bak);
# Proceed with original copy if backup succeeds
if ( $result == 0 )
{
logf("Patching OPatch to ORACLE_HOME...");
$result = copy_dir ($opatch_src, $opatch_dst);
logf("Patching OPatch to ORACLE_HOME complete. Result = $result");
}
}
}
# Set status
if ($result == 0)
{
$status = 0;
}
}
}
}
# Step 4: restart the patched Oracle Agent
if (($stopAgent == 1) && isAgentHome())
{
logf("Attempting to start Oracle Management Agent...");
if ( onWindows() )
{
$retStatus = echodo("$EMCTL start agent");
}
else
{
$retStatus = echodo("$EMCTL start agent 2>&1");
}
# right shift retstatus by 8 to get the correct error code
$retStatus = statusf($retStatus);
if ($retStatus != 0)
{
errorf("failed to start Management Agent: status = " . $retStatus);
logf("Please start the agent and then stop blackout manually");
$stopAgent = 1;
}
else
{
logf("Agent started successfully");
$stopAgent = 0;
}
}
# Step 5: remove blackout for all targets on host
if ( isAgentHome() && ($startBlackout == 1) && ($stopAgent == 0) )
{
logf("Attempting to remove blackout for host...");
if ( onWindows() )
{
$retStatus = echodo("$EMCTL stop blackout $blackout");
}
else
{
$retStatus = echodo("$EMCTL stop blackout $blackout 2>&1");
}
# right shift retstatus by 8 to get the correct error code
$retStatus = statusf($retStatus);
if ($retStatus != 0)
{
errorf("failed to stop blackout: status = " . $retStatus);
logf("Please stop blackout manually");
$startBlackout = 1;
}
else
{
logf("Blackout stopped successfully");
$startBlackout = 0;
}
}
# Exit with status
if ($status == 0)
{
if (isAgentHome())
{
printf("\napplyPatch %s successful\n", $patch_id); # To satisfy showResults
}
else
{
logf("Patching completed");
}
}
else
{
logf("Patching failed");
}
if ($status == $FAIL)
{
$status = statusf($status);
}
exit $status;
|