# $Header: emdb/sysman/admin/scripts/db/dbclone/db_clone.pl /st_emdbsa_11.2/5 2009/06/11 14:41:05 sjconnol Exp $ # # db_clone.pl # # Copyright (c) 2002, 2009, Oracle and/or its affiliates. All rights reserved. # # NAME # db_clone.pl - scripts for database cloning # # DESCRIPTION # # # NOTES # # # MODIFIED (MM/DD/YY) # sjconnol 06/10/09 - Bug 8575467 # ngade 02/11/09 - Code slap 10.2.0.5.0 -> 11.2 SA round 2 # ngade 02/02/09 - Code slap 10.2.0.5.0 -> 11.2 round 2 # rimmidi 02/05/09 - LRG fix 3775465 # ngade 11/12/08 - Fix bug 7492475 # rimmidi 09/12/08 - Bug fix 7385965 # rimmidi 05/05/08 - Code slap from 11GC to 10.2.0.5 # rimmidi 09/18/08 - Backport rimmidi_bug-7385965 from # st_emdbgc_10.2.0.1.0 # rimmidi 07/30/08 - Code slap from 10205 to 11.2DBControl # nachen 06/19/08 - XbranchMerge nachen_bug-7144603 from st_emdbsa_11.1 # nachen 06/06/08 - fix internal references security issue # sxzhu 03/26/07 - Config listener service to auto start # sxzhu 03/12/07 - Fix bug 5913084: handle spaces in ftp passwd # sxzhu 09/29/06 - Add quotes for alter user # ngade 10/18/06 - Backport ngade_bug-5141850 from main # sxzhu 08/14/06 - Backport fix for bug 5352507 # sxzhu 06/21/05 - Add Linux as an OS type # sxzhu 06/07/05 - Remove version check # sxzhu 06/28/06 - Increase buffer for bigvalue # mappusam 04/20/06 - Fix for the bug-5023992 # ngade 12/22/05 - fix bug 4771808 # sxzhu 06/21/05 - Add Linux as an OS type # sxzhu 06/07/05 - Remove version check # sxzhu 05/19/05 - Add compareVer # sxzhu 04/07/05 - Handle large plsql output # sxzhu 02/07/05 - Remove dir creation for external files # sxzhu 08/06/04 - Change to DEBUG for ORA messages # sxzhu 07/29/04 - Move compress and external obj to db_clone_10_2 # sxzhu 07/23/04 - Set linesize for sqlplus # sjconnol 07/15/04 - Bug 3633774 # sjconnol 06/29/04 - Don't use connect desc in db connections # szhu 05/24/04 - Support compressing backup files # szhu 04/14/04 - Handle glob for NFS mount case # mbhoopat 03/10/04 - linux port # szhu 02/11/04 - Create dirs for external files # szhu 02/05/04 - Re-create directory objects # szhu 12/09/03 - Set dbsnmp password # szhu 11/19/03 - Use source pfile for cold clone # szhu 11/12/03 - Cleanup cloned DB # szhu 11/04/03 - Fix startLsnr on NT # szhu 10/31/03 - Handle startLsnr on NT # sjconnol 10/10/03 - Don't do pfile remove if DG # szhu 10/06/03 - Fix spfile used flag for 10g DB # szhu 09/16/03 - Handle nls_ init parameters # szhu 09/11/03 - Convert back to single quotes from %%% # szhu 09/03/03 - Fix version comparison # szhu 08/05/03 - NT support # sjconnol 06/17/03 - Don't do pfile rename if DG # szhu 06/06/03 - Rename pfile if using spfile # szhu 06/05/03 - Create admin dirs # szhu 06/04/03 - Handle obsolete ORA message # gallison 06/03/03 - Handle resetlogs # szhu 05/29/03 - Fix bug 2982763, support multiple members in one log group # szhu 04/22/03 - Set destination DB to noarchivelog for cold cloning # szhu 04/17/03 - Allow deprecated init parameters # szhu 04/08/03 - set TNS_ADMIN env variable # szhu 03/31/03 - Handle no source password file # szhu 01/27/03 - Handle filenames delimeters for NT # szhu 01/24/03 - addDatafilesForTesting # szhu 01/17/03 - Add hiding FTP output # sjconnol 01/18/03 - Change to modifyInitFile # szhu 01/08/03 - # sjconnol 01/11/03 - Change createNewDirs to error if mkDir fails # szhu 12/06/02 - # szhu 12/06/02 - Add ftp support # szhu 11/18/02 - Correct control file backup # sjconnol 12/05/02 - Change to modifyInitFile # sjconnol 11/12/02 - Logical standby 10i support # sjconnol 10/28/02 - Fix parameter filtering # szhu 10/18/02 - Switch log mode with specified parameters # szhu 10/11/02 - Support cold cloning # szhu 10/10/02 - Redirect output of startLsnr to a temp file # sjconnol 10/08/02 - Support for physical/logical standby # sjconnol 09/24/02 - Add startLsnr # szhu 09/22/02 - Backup control file # szhu 09/19/02 - Handle offline datafiles # szhu 09/18/02 - Set dest DBName in control file # szhu 09/13/02 - Fix archive log sql # szhu 09/13/02 - # szhu 09/12/02 - Create new directories for database files and temp dir # szhu 09/11/02 - Get maxInfo for control file # sjconnol 09/07/02 - Don't run addTempfiles if there aren't any # szhu 09/03/02 - Customize database file locations # szhu 08/29/02 - Handle ORA messages specially # sjconnol 08/30/02 - Don't run clonePrepare if standby; fix SQL # szhu 08/22/02 - Set clone purpose # sjconnol 08/23/02 - Add optional TNS descriptor to connect str # szhu 08/15/02 - Restore log on same host # szhu 08/12/02 - More init.ora creation # szhu 08/09/02 - Create init.ora for restrarting source DB # szhu 08/02/02 - Add tempfile support # szhu 08/01/02 - Add init.ora location for destination DB startup # szhu 07/31/02 - Solve 817 DB issues # szhu 07/25/02 - Hide standard output when get datafile numbers # szhu 07/24/02 - More debug info # szhu 07/23/02 - Add debug info # szhu 07/23/02 - Move script location # szhu 07/16/02 - Add flag for control file backup # szhu 06/26/02 - Hide source DB credential # xxu 06/25/02 - remove /usr/local/bin/perl # szhu 06/19/02 - Get source spfile info # szhu 06/17/02 - Create standby control file # szhu 06/14/02 - Init parameters # szhu 06/02/02 - szhu_clone_db # szhu 06/02/02 - More switch archivelog mode # szhu 05/31/02 - Add checking and switching archivelog mode # szhu 05/29/02 - More script work. # szhu 05/28/02 - More database recovery # szhu 05/24/02 - Recover database # szhu 05/23/02 - Restore archivedLogs and recover database # szhu 05/22/02 - backup datafiles # szhu 05/21/02 - Creation # require "emd_common.pl"; require "$ENV{EMDROOT}/sysman/admin/scripts/db/db_common.pl"; use strict; use File::Temp qw/ tempfile tempdir /; use vars qw/ $userID $clonePurpose $clodClone $OS $NT $S $TEMP $CP $MV $PS $DF $DELIMITER/; # Global variables $userID = ""; $clonePurpose = "NORMAL"; $clodClone = "N"; #A temporary solution and will be removed after EMD supports this setting #in logging.xml. #$ENV{EMAGENT_PERL_TRACE_LEVEL} = 0; #DEBUG level. # Set environment variables # To set TNS_ADMIN to non-default value, pass listener.ora full file name # as the third argument. # Call sub set_env_var() in emd_common.pl # set_env(oracleHome, oracleSid) sub set_env { EMD_PERL_DEBUG("db_clone.set_env(): *** START ***"); my($listenerFile) = @_[2]; if(!$listenerFile) { &set_env_var(@_[0], @_[1]); } else { my($idx) = rindex($listenerFile,"$S"); if($idx == -1){ EMD_PERL_DEBUG("db_clone.set_env(): Cannot determine TNS_ADMIN"); exit(1); } my($tns) = substr($listenerFile,0,$idx); &set_env_var(@_[0], @_[1], undef(), $tns); } EMD_PERL_DEBUG("db_clone.set_env(): *** END ***"); } # Set the source DB credential # setSrcDBCredential(userName, password) sub setSrcDBCredential { EMD_PERL_DEBUG("db_clone.setSrcDBCredential(): *** START ***"); my ($userName, $password, $tns) = @_; # if(!$tns){ $userID = $userName."/".$password; # } # else{ # $userID = $userName."/".$password."\@${tns}"; # } EMD_PERL_DEBUG("db_clone.setSrcDBCredential(): User Name: $userName"); EMD_PERL_DEBUG("db_clone.setSrcDBCredential(): *** END ***"); } # Set the source DB credential (added for DG; need TNS string to # connect in dev environment). Reuses source credentials sub setDestDBCredential { EMD_PERL_DEBUG("db_clone.setDestDBCredential(): *** START ***"); setSrcDBCredential(@_); EMD_PERL_DEBUG("db_clone.setDestDBCredential(): *** END ***"); } # Set the clone purpose # setClonePurpose(purpose) sub setClonePurpose { EMD_PERL_DEBUG("db_clone.setClonePurpose(): *** START ***"); my ($purpose) = @_; s/$_/\U$purpose/gi; $clonePurpose = $_; EMD_PERL_DEBUG("db_clone.setClonePurpose(): Clone purpose: $clonePurpose"); EMD_PERL_DEBUG("db_clone.setClonePurpose(): *** END ***"); } # Set cold clone flag: Y or N # setColdCloneFlag(cold) sub setColdCloneFlag { EMD_PERL_DEBUG("db_clone.setColdCloneFlag(): *** START ***"); my ($cold) = @_; s/$_/\U$cold/gi; $clodClone = $_; EMD_PERL_DEBUG("db_clone.setColdCloneFlag(): Cold clone flag: $clodClone"); EMD_PERL_DEBUG("db_clone.setColdCloneFlag(): *** END ***"); } # Filter out DB credential from sql script, which is written to emd_perl.trc # filterDBCredential(sqlScript) sub filterDBCredential { my ($sqlScript) = @_; my $position1 = index($sqlScript, "CONNECT"); my $position2 = index($sqlScript, "AS"); my $replacedLength = $position2 - $position1 - 9; substr($sqlScript, $position1 + 8, $replacedLength) = "username/password(Hiden intentionally)"; return $sqlScript; } # Parse the output string to detect ORA- errors # parseOutput(output) sub parseOutput { EMD_PERL_DEBUG("db_clone.parseOutput(): *** START ***"); my ($output) = @_; if ($output eq "") { EMD_PERL_ERROR("db_clone.parseOutput(): Output file is empty!"); } if($output !~ /ORA-[0-9]/) { EMD_PERL_DEBUG("db_clone.parseOutput(): No Error found."); } elsif (($output =~ /ORA-00278/) || ($output =~ /ORA-00279/) || ($output =~ /ORA-00280/) || ($output =~ /ORA-00289/) || ($output =~ /ORA-32006/) || ($output =~ /ORA-32004/)) { EMD_PERL_DEBUG("db_clone.parseOutput(): ORA- messages!"); } else { EMD_PERL_ERROR("db_clone.parseOutput(): ORA- ERROR! Exit! See output log."); exit(1); } EMD_PERL_DEBUG("db_clone.parseOutput(): *** END ***"); } # Run given sql script on source DB # The caller is responsible to close the returned fileHandle # runSqlOnSource(sqlScript, hideOutput) will hide standard output for any # defined parameter "hideOutput" # runSqlOnSource(sqlScript) will print standard output sub runSqlOnSource { EMD_PERL_DEBUG("db_clone.runSqlOnSource(): *** START ***"); my ($sql_string) = @_[0]; my $sql_string_debug = &filterDBCredential($sql_string); EMD_PERL_DEBUG("db_clone.runSqlOnSource(): SQL:\n$sql_string_debug"); (my $fh, my $filename) = &create_temp_file(); if($NT) { $filename = "$TEMP\\"."dbclone.$$"; #$filename = "$TEMP\\".getBasename($filename); EMD_PERL_DEBUG("db_clone.runSqlOnSource(): temp file: $filename"); open(SQL_SCRIPT, "|$ENV{ORACLE_HOME}/bin/sqlplus /nolog >$filename") || die "Cannot open pipe for SQL_SCRIPT"; #print SQL_SCRIPT "spool $filename; \n"; print SQL_SCRIPT $sql_string; close SQL_SCRIPT || die "Bad SQL_SCRIPT"; } else { open(SQL_SCRIPT, "|$ENV{ORACLE_HOME}/bin/sqlplus /nolog >$filename") || die "Cannot open pipe for SQL_SCRIPT"; print SQL_SCRIPT $sql_string; close SQL_SCRIPT || die "Bad SQL_SCRIPT"; } #Open the temp file to print output to standard output and debug trace file open (OUT_PUT, "$filename") || die "Unable to open tempfile for OUT_PUT\n"; my @output_content = ; my $output_string = "@output_content"; close OUT_PUT; if(!defined(@_[1])) { print STDOUT $output_string; } EMD_PERL_DEBUG("db_clone.runSqlOnSource(): OUT_PUT:\n$output_string"); &parseOutput($output_string); EMD_PERL_DEBUG("db_clone.runSqlOnSource(): *** END ***"); return ($fh, $filename); } # Run given sql script on destination oracleHome # The caller is responsible to close the returned fileHandle # runSqlOnDestination(sqlScript, hideOutput) will hide standard output for any # defined parameter "hideOutput" # runSqlOnDestination(sqlScript, hideOutput, hideSQL) will hide standard # output for any defined parameter "hideOutput" and SQL script for any defined # parameter "hideSQL" # runSqlOnDestination(sqlScript) will print standard output sub runSqlOnDestination { EMD_PERL_DEBUG("db_clone.runSqlOnDestination(): *** START ***"); my ($sql_string) = @_[0]; if(!defined(@_[2])) { EMD_PERL_DEBUG("db_clone.runSqlOnDestination(): SQL:\n$sql_string"); } (my $fh, my $filename) = &create_temp_file(); if($NT) { $filename = "$TEMP\\"."dbclone.$$"; } # If a username/password had been specified, use it. Otherwise, # default to OS authentication if($userID){ EMD_PERL_DEBUG("db_clone.runSqlOnDestination(): Connecting with connect descriptor"); open(SQL_SCRIPT, "|$ENV{ORACLE_HOME}/bin/sqlplus /nolog >$filename") || die "Cannot open pipe for SQL_SCRIPT"; print SQL_SCRIPT "CONNECT $userID AS SYSDBA;\n"; } else{ EMD_PERL_DEBUG("db_clone.runSqlOnDestination(): Connecting with OS auth"); open(SQL_SCRIPT, "|$ENV{ORACLE_HOME}/bin/sqlplus '/ AS SYSDBA' >$filename") || die "Cannot open pipe for SQL_SCRIPT"; } print SQL_SCRIPT $sql_string; close SQL_SCRIPT; if($?) { die "ERROR: SQLPlus existed with status $?"; } #Open the temp file to print output to standard output and debug trace file open (OUT_PUT, "$filename") || die "Unable to open tempfile for OUT_PUT\n"; my @output_content = ; my $output_string = "@output_content"; close OUT_PUT; if(!defined(@_[1])) { print STDOUT $output_string; } EMD_PERL_DEBUG("db_clone.runSqlOnDestination(): OUT_PUT:\n$output_string"); &parseOutput($output_string); EMD_PERL_DEBUG("db_clone.runSqlOnDestination(): *** END ***"); return ($fh, $filename); } # Check the archivelog mode of source DB. If not in archivelog mode, switch it # to archivelog mode. # Call &set_env(oracleHome, oracleSid) before calling this method. # checkAndSwitchLogMode(pfileFullName, modifiedNames, modifiedValues) sub checkAndSwitchLogMode { EMD_PERL_DEBUG("db_clone.checkAndSwitchLogMode(): *** START ***"); #Check if the source database is in archivelog mode my $sql_string = ""; $sql_string .= "CONNECT $userID AS SYSDBA;\n"; $sql_string .= "select log_mode from v\$database;\n"; $sql_string .= "EXIT;\n"; (my $fh, my $filename) = &runSqlOnSource($sql_string,""); #Open the temp file to process the output open (LOG_MODE, "$filename") || die "Unable to open tempfile for LOG_MODE\n"; while () { if ((/ARCHIVELOG/) && !(/NOARCHIVELOG/)) { close LOG_MODE; close $fh; if($NT) { &removeFile($filename); } EMD_PERL_DEBUG("db_clone.checkAndSwitchLogMode(): LOG_MODE: ARCHIVELOG"); EMD_PERL_DEBUG("db_clone.checkAndSwitchLogMode(): *** END ***"); return "ARCHIVELOG"; } } close LOG_MODE; close $fh; if($NT) { &removeFile($filename); } EMD_PERL_DEBUG("db_clone.checkAndSwitchLogMode(): LOG_MODE: NOARCHIVELOG"); my ($pfileFullName, $modifiedNames, $modifiedValues) = @_; #Check if the source DB is using spfile my $spfile = &spfileUsed(); if($spfile eq "") #not using spfile { if($pfileFullName eq "$DELIMITER") #no pfile specified, create a temp one { my $pfile_fh; #not defined my $pfile_filename; #not defined ($pfile_fh, $pfile_filename) = &create_temp_file(); if($NT) { $pfile_filename = "$TEMP\\"."dbclone.$$"; } EMD_PERL_DEBUG("db_clone.checkAndSwitchLogMode(): Temp pfile: $pfile_filename"); &shutdownDB("immediate", $pfile_filename, $modifiedNames, $modifiedNames, $modifiedValues); &startupDB("mount", $pfile_filename); close $pfile_fh; if($NT) { &removeFile($pfile_filename); } } else #modify the original pfile { &modifyInitFile($modifiedNames, $modifiedValues, $pfileFullName); &shutdownDB("immediate"); &startupDB("mount", $pfileFullName); } } else { &modifySpfile($modifiedNames, $modifiedValues); &shutdownDB("immediate"); &startupDB("mount"); } #The source database is not in archivelog mode, switch it to archivelog mode. $sql_string = ""; $sql_string .= "CONNECT $userID AS SYSDBA;\n"; $sql_string .= "alter database ARCHIVELOG;\n"; $sql_string .= "alter database open;\n"; $sql_string .= "EXIT;\n"; ($fh, $filename) = &runSqlOnSource($sql_string,""); close $fh; if($NT) { &removeFile($filename); } EMD_PERL_DEBUG("db_clone.checkAndSwitchLogMode(): *** END ***"); } # Shutdown and mount DB for cold clone # Call &set_env(oracleHome, oracleSid) before calling this method. # closeDBForColdClone(pfile) sub closeDBForColdClone { EMD_PERL_DEBUG("db_clone.closeDBForColdClone(): *** START ***"); my ($pfile) = @_; #Check if the source DB is using spfile my $pfile_filename; #not defined my $spfile = &spfileUsed(); if($spfile eq "") #not using spfile { $pfile_filename = $pfile; EMD_PERL_DEBUG("db_clone.closeDBForColdClone(): pfile: $pfile_filename"); } #shutdown the source DB &shutdownDB("immediate"); #startup the source DB &startupDB("mount", $pfile_filename); EMD_PERL_DEBUG("db_clone.closeDBForColdClone(): *** END ***"); } # Open the mounted source DB # Call &set_env(oracleHome, oracleSid) before calling this method. # openDBAfterColdClone() sub openDBAfterColdClone { EMD_PERL_DEBUG("db_clone.openDBAfterColdClone(): *** START ***"); my $sql_string = ""; $sql_string .= "CONNECT $userID AS SYSDBA;\n"; $sql_string .= "alter database open;\n"; $sql_string .= "EXIT;\n"; (my $fh, my $filename) = &runSqlOnSource($sql_string); close $fh; if($NT) { &removeFile($filename); } EMD_PERL_DEBUG("db_clone.openDBAfterColdClone(): *** END ***"); } # Get datafile numbers # Call &set_env($oracleHome, $oracleSid) before calling this method. # getDatafileNum() sub getDatafileNum { EMD_PERL_DEBUG("db_clone.getDatafileNum(): *** START ***"); my $sql_string = ""; $sql_string .= "set echo off\n"; $sql_string .= "CONNECT $userID AS SYSDBA;\n"; $sql_string .= "set serveroutput on;\n"; $sql_string .= "declare\n"; $sql_string .= " i binary_integer := 1;\n"; $sql_string .= "BEGIN\n"; $sql_string .= " dbms_output.enable(2000000);\n"; $sql_string .= " FOR dfrec IN (SELECT file# FROM v\$datafile WHERE status <> 'OFFLINE'\n"; $sql_string .= " union \n"; $sql_string .= " SELECT file# FROM v\$datafile WHERE status = 'OFFLINE' and file# in (SELECT file# FROM v\$datafile_header))\n"; $sql_string .= " LOOP\n"; $sql_string .= " dbms_output.put_line('Checked file ' || i || ':');\n"; $sql_string .= " dbms_output.put_line('file# '||dfrec.file#);\n"; $sql_string .= " i := i + 1;\n"; $sql_string .= " END LOOP;\n"; $sql_string .= "END;\n"; $sql_string .= "/\n"; $sql_string .= "EXIT;\n"; my $hideOutput = ""; (my $fh, my $filename) = &runSqlOnSource($sql_string, $hideOutput); open (DATAFILE_NUM, "$filename") || die "Unable to open tempfile for DATAFILE_NUM\n"; while () { if (($_=~/\d/) && ($_=~/\bfile#/)) { chomp($_); print "$_ "; EMD_PERL_DEBUG("db_clone.getDatafileNum(): Datafile: $_"); } } close DATAFILE_NUM; close $fh; if($NT) { &removeFile($filename); } EMD_PERL_DEBUG("db_clone.getDatafileNum(): *** END ***"); } # Get max sequence number for archived log # Call &set_env($oracleHome, $oracleSid) before calling this method. # getMaxSeqNum() sub getMaxSeqNum { EMD_PERL_DEBUG("db_clone.getMaxSeqNum(): *** START ***"); my $sql_string = ""; $sql_string .= "set echo off\n"; $sql_string .= "CONNECT $userID AS SYSDBA;\n"; $sql_string .= "set serveroutput on;\n"; $sql_string .= "set linesize 512;\n"; ## SJC: Ensure archiving is on prior to switching log $sql_string .= "variable ver varchar2(256);\n"; $sql_string .= "BEGIN\n"; $sql_string .= " dbms_output.enable(2000000);\n"; $sql_string .= " select version INTO :ver from v\$instance;\n"; $sql_string .= " dbms_output.put_line('version# '||:ver);\n"; $sql_string .= " IF (SUBSTR(:ver, 1 ,INSTR(:ver, '.', 1, 1)+1) < 10.2) then\n"; $sql_string .= " EXECUTE IMMEDIATE 'ALTER SYSTEM ARCHIVE LOG START';\n"; $sql_string .= " end if;\n"; $sql_string .= "END;\n"; $sql_string .= "/\n"; $sql_string .= "ALTER SYSTEM ARCHIVE LOG CURRENT;\n"; $sql_string .= "variable max_seq number;\n"; $sql_string .= "variable rowcount number;\n"; $sql_string .= "variable spfile_val varchar2(256);\n"; $sql_string .= "BEGIN\n"; $sql_string .= " dbms_output.enable(2000000);\n"; #$sql_string .= " select max(sequence#) INTO :max_seq from v\$archived_log;\n"; $sql_string .= " select checkpoint_change# INTO :max_seq from v\$database;\n"; $sql_string .= " dbms_output.put_line('Start printing max_seq number: ');\n"; $sql_string .= " dbms_output.put_line('max_seq# '||:max_seq);\n"; $sql_string .= " select count(*) INTO :rowcount from v\$parameter where name = 'spfile';\n"; $sql_string .= " dbms_output.put_line('Row count: '||:rowcount);\n"; $sql_string .= " IF (:rowcount > 0) then\n"; $sql_string .= " select value INTO :spfile_val from v\$parameter where name = 'spfile';\n"; $sql_string .= " IF NOT (:spfile_val IS NULL) then\n"; $sql_string .= " dbms_output.put_line('Start printing spfile value: ');\n"; $sql_string .= " dbms_output.put_line('spfile# '||:spfile_val);\n"; $sql_string .= " end if;\n"; $sql_string .= " end if;\n"; $sql_string .= "END;\n"; $sql_string .= "/\n"; $sql_string .= "EXIT;\n"; my $hideOutput = ""; (my $fh, my $filename) = &runSqlOnSource($sql_string, $hideOutput); open (MAX_SEQUENCE_NUM, "$filename") || die "Unable to open tempfile for MAX_SEQUENCE_NUM\n"; while () { if (($_=~/\d/) && ($_=~/\bmax_seq#/)) { chomp($_); print "$_\n"; EMD_PERL_DEBUG("db_clone.getMaxSeqNum(): Max sequence number: $_"); } if ($_=~/\bspfile#/) { chomp($_); print "$_\n"; EMD_PERL_DEBUG("db_clone.getMaxSeqNum(): spfile name: $_"); } } close MAX_SEQUENCE_NUM; close $fh; if($NT) { &removeFile($filename); } EMD_PERL_DEBUG("db_clone.getMaxSeqNum(): *** END ***"); } # Get max info for creating control file # Call &set_env($oracleHome, $oracleSid) before calling this method. # getMaxInfo() sub getMaxInfo { EMD_PERL_DEBUG("db_clone.getMaxInfo(): *** START ***"); my $sql_string = ""; $sql_string .= "set echo off\n"; $sql_string .= "CONNECT $userID AS SYSDBA;\n"; $sql_string .= "set serveroutput on;\n"; $sql_string .= "declare\n"; $sql_string .= " mlogf binary_integer;\n"; $sql_string .= " mlogm binary_integer;\n"; $sql_string .= " mdatf binary_integer;\n"; $sql_string .= " minst binary_integer;\n"; $sql_string .= " mlogh binary_integer;\n"; $sql_string .= " chset varchar2(256);\n"; $sql_string .= "BEGIN\n"; $sql_string .= " dbms_output.enable(2000000);\n"; $sql_string .= " dbms_backup_restore.getMaxInfo(mlogf,\n"; $sql_string .= " mlogm,\n"; $sql_string .= " mdatf,\n"; $sql_string .= " minst,\n"; $sql_string .= " mlogh,\n"; $sql_string .= " chset);\n"; $sql_string .= " dbms_output.put_line('Start printing max info: ');\n"; $sql_string .= " dbms_output.put_line('mlogf# '||mlogf);\n"; $sql_string .= " dbms_output.put_line('mlogm# '||mlogm);\n"; $sql_string .= " dbms_output.put_line('mdatf# '||mdatf);\n"; $sql_string .= " dbms_output.put_line('minst# '||minst);\n"; $sql_string .= " dbms_output.put_line('mlogh# '||mlogh);\n"; $sql_string .= " dbms_output.put_line('chset# '||chset);\n"; $sql_string .= "END;\n"; $sql_string .= "/\n"; $sql_string .= "EXIT;\n"; my $hideOutput = ""; (my $fh, my $filename) = &runSqlOnSource($sql_string, $hideOutput); open (MAX_INFO, "$filename") || die "Unable to open tempfile for MAX_INFO\n"; while () { if (($_=~/\d/) && ($_=~/\mlogf#/)) { chomp($_); print "$_\n"; EMD_PERL_DEBUG("db_clone.getMaxInfo(): Max log files: $_"); } elsif (($_=~/\d/) && ($_=~/\mlogm#/)) { chomp($_); print "$_\n"; EMD_PERL_DEBUG("db_clone.getMaxInfo(): Max log members: $_"); } elsif (($_=~/\d/) && ($_=~/\mdatf#/)) { chomp($_); print "$_\n"; EMD_PERL_DEBUG("db_clone.getMaxInfo(): Max datafiles: $_"); } elsif (($_=~/\d/) && ($_=~/\minst#/)) { chomp($_); print "$_\n"; EMD_PERL_DEBUG("db_clone.getMaxInfo(): Max instances: $_"); } elsif (($_=~/\d/) && ($_=~/\mlogh#/)) { chomp($_); print "$_\n"; EMD_PERL_DEBUG("db_clone.getMaxInfo(): Max archived logs: $_"); } elsif ($_=~/chset#/) { chomp($_); print "$_\n"; EMD_PERL_DEBUG("db_clone.getMaxInfo(): Character set: $_"); } } close MAX_INFO; close $fh; if($NT) { &removeFile($filename); } EMD_PERL_DEBUG("db_clone.getMaxInfo(): *** END ***"); } # Create password file for cloned DB # This is specific to Unix, will be fixed later # Call &set_env($oracleHome, $oracleSid) before calling this method. # createPasswordFile() sub createPasswordFile { EMD_PERL_DEBUG("db_clone.createPasswordFile(): *** START ***"); my $oracleHome = $ENV{ORACLE_HOME}; my $dbName = $ENV{ORACLE_SID}; !system "$oracleHome/bin/orapwd file=$oracleHome/dbs/orapw.$dbName password=change_on_install entries=30" or die "Cannot create password file!"; EMD_PERL_DEBUG("db_clone.createPasswordFile(): Password file $oracleHome/dbs/orapw.$dbName has been created."); EMD_PERL_DEBUG("db_clone.createPasswordFile(): *** END ***"); } # Copy password file to temporary location, #1) Look for orapw. #2) If not found look for orapw, where is the inst name minus the trailing "_<#". This will cover the policy-managed RAC case, where the instance names are _<#>, and the pw file is orapw #3) If not found look for orapw. # For NT the password file format will be pwd.ora or pwd. Hence adding the .ora exists check. sub copyPasswordFile { EMD_PERL_DEBUG("db_clone.copyPasswordFile(): *** START ***"); my($srcfile, $tempfile, $instance, $passwordFileUsage) = @_; my $oraExists = "N"; if(rindex(lc(basename($srcfile)), ".ora") != -1) { $oraExists = "Y"; } EMD_PERL_DEBUG("db_clone.copyPasswordFile(): oraExists = $oraExists"); EMD_PERL_DEBUG("db_clone.copyPasswordFile(): srcfile = $srcfile"); EMD_PERL_DEBUG("db_clone.copyPasswordFile(): tempfile = $tempfile"); EMD_PERL_DEBUG("db_clone.copyPasswordFile(): instance = $instance"); EMD_PERL_DEBUG("db_clone.copyPasswordFile(): passwordFileUsage = $passwordFileUsage"); # $srcfile is of the form orapw; if((-e $srcfile) && (-f $srcfile) ) { if($passwordFileUsage eq "Y") { !copyFile($srcfile, $tempfile) || (EMD_PERL_DEBUG("Error copying $srcfile to $tempfile") && (die "Error copying $srcfile to $tempfile")); } else #try to copy { !copyFile2($srcfile, $tempfile) || (EMD_PERL_DEBUG("Error copying $srcfile to $tempfile") && (die "Error copying $srcfile to $tempfile")); } } else { EMD_PERL_DEBUG("db_clone.copyPasswordFile(): srcfile with orapw or pwd in case if it is NT is not found as it is $srcfile"); #2) If not found look for orapw, where is the inst name minus the trailing "_<#". This will catch policy-managed RAC case where inst names should be _<#>. my($index) = rindex(basename($srcfile), '_'); my $srcfileChopped; my $wasChopped = 0; if($index != -1) { $wasChopped = 1; my($nindex) = rindex($srcfile, '_'); $srcfileChopped = substr($srcfile,0,$nindex); if($oraExists eq "Y") { $srcfileChopped .= ".ora"; } } EMD_PERL_DEBUG("db_clone.copyPasswordFile(): srcfileChopped= $srcfileChopped"); if($wasChopped && (-e $srcfileChopped) && (-f $srcfileChopped) ) { EMD_PERL_DEBUG("db_clone.copyPasswordFile(): srcfileChopped exists and it is $srcfileChopped"); if($passwordFileUsage eq "Y") { !copyFile($srcfileChopped, $tempfile) || (EMD_PERL_DEBUG("Error copying $srcfile to $tempfile") && (die "Error copying $srcfile to $tempfile")); } else #try to copy { !copyFile2($srcfileChopped, $tempfile) || (EMD_PERL_DEBUG("Error copying $srcfile to $tempfile") && (die "Error copying $srcfile to $tempfile")); } } else { EMD_PERL_DEBUG("db_clone.copyPasswordFile(): srcfileChopped is also not found, srcfile is $srcfile"); #if it doesn't exist, truncate down to orapw and look for that my($idx) = rindex($srcfile,$instance); if($idx == -1) { EMD_PERL_DEBUG("Error parsing file name $srcfile"); die "Error parsing file name $srcfile"; } $srcfile = substr($srcfile,0,$idx); if($oraExists eq "Y") { $srcfile .= ".ora"; } EMD_PERL_DEBUG("db_clone.copyPasswordFile(): srcfile = $srcfile "); if($passwordFileUsage eq "Y") { !copyFile($srcfile, $tempfile) || (EMD_PERL_DEBUG("Error copying $srcfile to $tempfile") && (die "Error copying $srcfile to $tempfile")); } else { !copyFile2($srcfile, $tempfile) || (EMD_PERL_DEBUG("Error copying $srcfile to $tempfile") && (die "Error copying $srcfile to $tempfile")); } } } EMD_PERL_DEBUG("db_clone.copyPasswordFile(): *** END ***"); } # Prepare the destination instance for cloning # Call &set_env($oracleHome, $oracleSid) before calling this method. # clonePrepare(pfileFullName, spfile) sub clonePrepare { EMD_PERL_DEBUG("db_clone.clonePrepare(): *** START ***"); # if($clonePurpose eq "STANDBY_NO_RECOVERY") # { # EMD_PERL_DEBUG("db_clone.clonePrepare(): No clone prepare has been done since clone purpose is: $clonePurpose"); # EMD_PERL_DEBUG("db_clone.clonePrepare(): *** END ***"); # return; # } my ($pfileFullName, $spfile) = @_; #create password file #Currently we copy the source password file #createPasswordFile(); #kill the old instance my $sql_string = ""; $sql_string .= "shutdown abort;\n"; $sql_string .= "exit;\n"; (my $fh, my $filename) = &runSqlOnDestination($sql_string); close $fh; if($NT) { &removeFile($filename); } EMD_PERL_DEBUG("db_clone.clonePrepare(): Old instance has been killed."); #Create the server parameter file and start instance $sql_string = ""; $sql_string .= "shutdown abort\n"; if($spfile eq "Y") { $sql_string .= "create spfile from pfile='$pfileFullName';\n"; $sql_string .= "startup force nomount\n"; } else { $sql_string .= "startup force nomount pfile='$pfileFullName';\n"; } $sql_string .= "exit;\n"; ($fh, $filename) = &runSqlOnDestination($sql_string); close $fh; if($NT) { &removeFile($filename); } EMD_PERL_DEBUG("db_clone.clonePrepare(): *** END ***"); } # Create init parameter file based on source DB's v$parameter # Call &set_env($oracleHome, $oracleSid) before calling this method. # createSourceInitParamFile(pfileFullName, filteredParams) sub createSourceInitParamFile { EMD_PERL_DEBUG("db_clone.createSourceInitParamFile(): *** START ***"); my ($pfileFullName, $filteredParamNames) = @_; EMD_PERL_DEBUG("db_clone.createSourceInitParamFile(): pfileFullName: $pfileFullName"); EMD_PERL_DEBUG("db_clone.createSourceInitParamFile(): filteredParamNames: $filteredParamNames"); my @filteredParamNames = split /$DELIMITER/, $filteredParamNames; #get names and values from source DB v$parameter my $sql_string = ""; $sql_string .= "set echo off\n"; $sql_string .= "CONNECT $userID AS SYSDBA;\n"; $sql_string .= "set serveroutput on;\n"; $sql_string .= "set linesize 512;\n"; $sql_string .= "declare\n"; $sql_string .= " i binary_integer := 1;\n"; $sql_string .= " bigvalue varchar(4096);\n"; $sql_string .= " value varchar(255);\n"; $sql_string .= "BEGIN\n"; $sql_string .= " dbms_output.enable(2000000);\n"; $sql_string .= " FOR dfrec IN (SELECT name, value FROM V\$PARAMETER WHERE ISDEFAULT = 'FALSE' )\n"; $sql_string .= " LOOP\n"; $sql_string .= " IF (dfrec.value IS NULL) then\n"; $sql_string .= " dfrec.value := '\"\"';\n"; $sql_string .= " end if;\n"; $sql_string .= " dbms_output.put_line('Parameter ' || i || ':');\n"; $sql_string .= " bigvalue := 'param# ' || dfrec.name || '=' || dfrec.value;\n"; $sql_string .= " IF (length(bigvalue) > 255) then\n"; $sql_string .= " while (length(bigvalue) > 255) LOOP\n"; $sql_string .= " value := substr(bigvalue, 1, 255);\n"; $sql_string .= " bigvalue := substr(bigvalue, 256);\n"; $sql_string .= " dbms_output.put_line(value);\n"; $sql_string .= " END LOOP;\n"; $sql_string .= " dbms_output.put_line(bigvalue);\n"; $sql_string .= " else\n"; $sql_string .= " dbms_output.put_line(bigvalue);\n"; $sql_string .= " end if;\n"; $sql_string .= " i := i + 1;\n"; $sql_string .= " END LOOP;\n"; $sql_string .= "END;\n"; $sql_string .= "/\n"; $sql_string .= "EXIT;\n"; (my $fh, my $filename) = &runSqlOnSource($sql_string); #Make the $pfileFullName based on souce v$parameter and filter certain #parameters open (CREATE_SOURCE_INIT_PARAM, "$filename") || die "Unable to open tempfile for CREATE_SOURCE_INIT_PARAM\n"; open(INIT_ORA, ">$pfileFullName") || die "Cannot open $pfileFullName"; my $nameValue = ""; my $isPrint = "true"; while () { if ($_=~/\bparam#/) { chomp($_); $nameValue = substr $_, 7; #7 is the position name starts foreach $filteredParamNames (@filteredParamNames) { if ($nameValue =~ /\b$filteredParamNames/) { $isPrint = "false"; last; } } if($isPrint eq "true") { #nls_ parameters need quotes if($nameValue =~ /\bnls_/i) { my @values = split /=/, $nameValue; my $name = $values[0]; my $value = "='".$values[1]."'"; print INIT_ORA "$name"."$value\n"; } #handle empty os_authent_prefix elsif($nameValue =~ /\bos_authent_prefix/i) { my @values = split /=/, $nameValue; my $name = $values[0]; my $value = "=".$values[1]; if(&trim($values[1]) eq '') { $value = "='".$values[1]."'"; } print INIT_ORA "$name"."$value\n"; } #control files could be on multiple lines elsif($nameValue =~ /\bcontrol_files/i) { print INIT_ORA "$nameValue\n"; my $oneLine = ""; open (FOR_CONTROL_FILE, "$filename") || die "Unable to open tempfile for FOR_CONTROL_FILE\n"; my $value = ""; while ($oneLine = ) { if (($oneLine=~/\bparam#/) && ($oneLine=~/\bcontrol_files/i)) { my $oneControlFile = ""; while (($oneControlFile = ) && (!($oneControlFile =~ /\bparam#/) && !($oneControlFile =~ /\bParameter/))) { $value = $oneControlFile; print INIT_ORA "$value"; } last; } } close FOR_CONTROL_FILE; } #dispatchers could be on multiple lines elsif($nameValue =~ /\bdispatchers/i) { my $name = substr $nameValue, 0, 12; #position 0 to 11 contain "dispatchers=" my $value = substr $nameValue, 12; #12 is the position value starts if($value =~ /,/) { my @values = split /,/, $value; $value = "\"".$values[0]."\","; } else { $value = "\"".$value."\""; } print INIT_ORA "dispatchers=$value\n"; my $oneLine = ""; open (FOR_DISPATCHERS, "$filename") || die "Unable to open tempfile for FOR_DISPATCHERS\n"; my $value = ""; while ($oneLine = ) { if (($oneLine=~/\bparam#/) && ($oneLine=~/\bdispatchers/i)) { my $oneDispatchers = ""; while (($oneDispatchers = ) && (!($oneDispatchers =~ /\bparam#/) && !($oneDispatchers =~ /\bParameter/))) { $value = $oneDispatchers; if($value =~ /,/) { my @values = split /,/, $value; $value = "\"".$values[0]."\",".$values[1]; } else { my @values = split /\n/, $value; $value = "\"".$values[0]."\"\n"; } print INIT_ORA "$value"; } last; } } close FOR_DISPATCHERS; } #mts_dispatchers could be on multiple lines elsif($nameValue =~ /\bmts_dispatchers/i) { my $name = substr $nameValue, 0, 16; #position 0 to 15 contain "mts_dispatchers=" my $value = substr $nameValue, 16; #16 is the position value starts if($value =~ /,/) { my @values = split /,/, $value; $value = "\"".$values[0]."\","; } else { $value = "\"".$value."\""; } print INIT_ORA "mts_dispatchers=$value\n"; my $oneLine = ""; open (FOR_MTS_DISPATCHERS, "$filename") || die "Unable to open tempfile for FOR_MTS_DISPATCHERS\n"; my $value = ""; while ($oneLine = ) { if (($oneLine=~/\bparam#/) && ($oneLine=~/\bmts_dispatchers/i)) { my $oneDispatchers = ""; while (($oneDispatchers = ) && (!($oneDispatchers =~ /\bparam#/) && !($oneDispatchers =~ /\bParameter/))) { $value = $oneDispatchers; if($value =~ /,/) { my @values = split /,/, $value; $value = "\"".$values[0]."\",".$values[1]; } else { my @values = split /\n/, $value; $value = "\"".$values[0]."\"\n"; } print INIT_ORA "$value"; } last; } } close FOR_MTS_DISPATCHERS; } else { # generic look-ahead for parameters that are broken-up across # more than one line my $oneLine = ""; open (LOOK_AHEAD, "$filename") || die "Unable to open tempfile for LOOK_AHEAD\n"; while ($oneLine = ){ if(($oneLine=~/\bparam#/) && ($oneLine=~/\b$nameValue/i)){ my $addline = ""; while (($addline = ) && ($addline !~ /\bparam#/) && ($addline !~ /\bParameter/) && ($addline !~ /^\n$/)){ chomp($addline); $nameValue = $nameValue.$addline; } last; } } close LOOK_AHEAD; print INIT_ORA "$nameValue\n"; } } else { $isPrint = "true"; } } } close CREATE_SOURCE_INIT_PARAM; close INIT_ORA || die "Cannot close $pfileFullName"; #For debug if($ENV{EMAGENT_PERL_TRACE_LEVEL} >= 0) { open (INIT_ORA_FILE, "$pfileFullName") || die "Unable to open tempfile for INIT_ORA_FILE\n"; my @file_content = ; my $file_string = "@file_content"; close INIT_ORA_FILE; EMD_PERL_DEBUG("db_clone.createSourceInitParamFile(): INIT_ORA_FILE:\n $file_string"); } close $fh; if($NT) { &removeFile($filename); } EMD_PERL_DEBUG("db_clone.createSourceInitParamFile(): *** END ***"); } # Modify init parameter file based on destination host properties # modifyInitFile(modifiedParamNames, modifiedParamValues, pfileFullName) sub modifyInitFile { EMD_PERL_DEBUG("db_clone.modifyInitFile(): *** START ***"); my ($modifiedParamNames, $modifiedParamValues, $pfileFullName) = @_; ## For Data Guard; *_file_name_convert params are comma-separated ## lists; the commas will mess-up the array parsing above, so ## they come in from the java as # instead of commans. Need ## to put them back to commas here $modifiedParamValues =~ s/%%%/'/g; $modifiedParamValues =~ s/\'#\'/\',\'/g; $modifiedParamValues =~ s/ROLES#ONLINE/ROLES,ONLINE/g; $modifiedParamValues =~ s/ROLE#STANDBY/ROLE,STANDBY/g; if(($modifiedParamNames eq "") || ($modifiedParamNames eq "$DELIMITER")) { EMD_PERL_DEBUG("db_clone.modifyInitFile(): NO PARAMETER TO MODIFY, returning"); EMD_PERL_DEBUG("db_clone.modifyInitFile(): *** END ***"); return; } #Replace the "###" with "'" in $modifiedParamValues since job system reserves "," #This is for control file name delimiters #$modifiedParamValues =~ s/###/'/g; EMD_PERL_DEBUG("db_clone.modifyInitFile(): modifiedParamNames: $modifiedParamNames"); EMD_PERL_DEBUG("db_clone.modifyInitFile(): modifiedParamValues: $modifiedParamValues"); my @modifiedParamNames = split /$DELIMITER/, $modifiedParamNames; my @modifiedParamValues = split /$DELIMITER/, $modifiedParamValues; open(INIT_ORA, ">>$pfileFullName") || die "Cannot open $pfileFullName"; #Add modified init parameters (name=value) in the materialized init.ora foreach (0 .. $#modifiedParamNames) { print INIT_ORA "$modifiedParamNames[$_]=$modifiedParamValues[$_]\n"; } close INIT_ORA || die "Cannot close $pfileFullName"; #For debug if($ENV{EMAGENT_PERL_TRACE_LEVEL} >= 0) { open (INIT_ORA_FILE, "$pfileFullName") || die "Unable to open tempfile for INIT_ORA_FILE\n"; my @file_content = ; my $file_string = "@file_content"; close INIT_ORA_FILE; EMD_PERL_DEBUG("db_clone.modifyInitFile(): INIT_ORA_FILE:\n $file_string"); } EMD_PERL_DEBUG("db_clone.modifyInitFile(): *** END ***"); } # Check if an spfile is used # return spfile name: if empty, no spfile is being used. # Call &set_env($oracleHome, $oracleSid) before calling this method. # spfileUsed() sub spfileUsed { EMD_PERL_DEBUG("db_clone.spfileUsed(): *** START ***"); my $sql_string = ""; $sql_string .= "set echo off\n"; $sql_string .= "CONNECT $userID AS SYSDBA;\n"; $sql_string .= "set serveroutput on;\n"; $sql_string .= "set linesize 512;\n"; $sql_string .= "variable rowcount number;\n"; $sql_string .= "variable spfile_val varchar2(256);\n"; $sql_string .= "BEGIN\n"; $sql_string .= " dbms_output.enable(2000000);\n"; $sql_string .= " select count(*) INTO :rowcount from v\$parameter where name = 'spfile';\n"; $sql_string .= " dbms_output.put_line('Row count: '||:rowcount);\n"; $sql_string .= " IF (:rowcount > 0) then\n"; $sql_string .= " select value INTO :spfile_val from v\$parameter where name = 'spfile';\n"; $sql_string .= " IF NOT (:spfile_val IS NULL) then\n"; $sql_string .= " dbms_output.put_line('Start printing spfile value: ');\n"; $sql_string .= " dbms_output.put_line('spfile# '||:spfile_val);\n"; $sql_string .= " end if;\n"; $sql_string .= " end if;\n"; $sql_string .= "END;\n"; $sql_string .= "/\n"; $sql_string .= "EXIT;\n"; (my $fh, my $filename) = &runSqlOnSource($sql_string); my $spfileName = ""; open (SPFILE, "$filename") || die "Unable to open tempfile for SPFILE\n"; while () { if ($_=~/\bspfile#/) { chomp($_); $spfileName = substr $_, 8; #spfile name starts from position 8 } } close SPFILE; close $fh; if($NT) { &removeFile($filename); } EMD_PERL_DEBUG("db_clone.spfileUsed(): spfile name: $spfileName"); EMD_PERL_DEBUG("db_clone.spfileUsed(): *** END ***"); return $spfileName; } # Shutdown DB with specified option. # Option: normal, immediate, transactional, abort. # Create pfile based on DB v$parameter if "pfileFullName" is defined. # Call &set_env($oracleHome, $oracleSid) before calling this method. # shutdownDB(option, pfileFullName, filteredParams, modifiedNames, modifiedValues) sub shutdownDB { EMD_PERL_DEBUG("db_clone.shutdownDB(): *** START ***"); my ($option) = (@_[0]); EMD_PERL_DEBUG("db_clone.shutdownDB(): option: $option"); if(defined(@_[1])) { my ($filteredParams, $modifiedNames, $modifiedValues) = ("", "", ""); if(defined(@_[2])) { $filteredParams = @_[2]; } if(defined(@_[3])) { $modifiedNames = @_[3]; } if(defined(@_[4])) { $modifiedValues = @_[4]; } &createSourceInitParamFile(@_[1], $filteredParams); &modifyInitFile($modifiedNames, $modifiedValues, @_[1]); EMD_PERL_DEBUG("db_clone.shutdownDB(): created pfile: @_[1]"); } my $sql_string = ""; $sql_string .= "CONNECT $userID AS SYSDBA;\n"; $sql_string .= "shutdown $option\n"; $sql_string .= "EXIT;\n"; my ($fh, $filename) = &runSqlOnSource($sql_string); close $fh; if($NT) { &removeFile($filename); } EMD_PERL_DEBUG("db_clone.shutdownDB(): *** END ***"); } # Start DB to specified mode. # Mode: nomount, mount, open (""), restrict, force. # Use the specified pfile if "pfileFullName" is defined. # Call &set_env($oracleHome, $oracleSid) before calling this method. # startupDB(mode, pfileFullName) sub startupDB { EMD_PERL_DEBUG("db_clone.startupDB(): *** START ***"); my ($mode) = (@_[0]); if($mode =~ /open/i) { $mode = ""; } EMD_PERL_DEBUG("db_clone.startupDB(): mode: $mode"); my $pfile = ""; if(defined(@_[1])) { $pfile = "pfile='@_[1]'"; EMD_PERL_DEBUG("db_clone.startupDB(): pfile: @_[1]"); } my $sql_string = ""; $sql_string .= "CONNECT $userID AS SYSDBA;\n"; $sql_string .= "startup $mode $pfile;\n"; $sql_string .= "EXIT;\n"; my ($fh, $filename) = &runSqlOnSource($sql_string); close $fh; if($NT) { &removeFile($filename); } EMD_PERL_DEBUG("db_clone.startupDB(): *** END ***"); } # Modify spfile with given parameter names and values # Call &set_env($oracleHome, $oracleSid) before calling this method. # modifySpfile(modifiedNames, modifiedValues) sub modifySpfile { EMD_PERL_DEBUG("db_clone.modifySpfile(): *** START ***"); my ($modifiedNames, $modifiedValues) = @_; if(($modifiedNames eq "") || ($modifiedNames eq "$DELIMITER")) { EMD_PERL_DEBUG("db_clone.modifySpfile(): NO PARAMETER TO MODIFY, returning"); EMD_PERL_DEBUG("db_clone.modifySpfile(): *** END ***"); return; } #Replace the "###" with "'" in $modifiedParamValues since job system reserves "," #This is for control file name delimiters $modifiedValues =~ s/%%%/'/g; #Replace the ";" with "," in $modifiedValues since job system reserves "," #my $position = index($modifiedValues, ";"); #my $replacedLength = 1; #while($position > 0) #{ # substr($modifiedValues, $position, $replacedLength) = ","; # $position = index($modifiedValues, ";"); #} EMD_PERL_DEBUG("db_clone.modifySpfile(): parameter names: $modifiedNames"); EMD_PERL_DEBUG("db_clone.modifySpfile(): parameter values: $modifiedValues"); my @modifiedNames = split /$DELIMITER/, $modifiedNames; my @modifiedValues = split /$DELIMITER/, $modifiedValues; my $sql_string = ""; $sql_string .= "CONNECT $userID AS SYSDBA;\n"; foreach (0 .. $#modifiedNames) { $sql_string .= "ALTER SYSTEM SET $modifiedNames[$_]=$modifiedValues[$_] SCOPE=SPFILE; \n"; } $sql_string .= "EXIT;\n"; my ($fh, $filename) = &runSqlOnSource($sql_string); close $fh; if($NT) { &removeFile($filename); } EMD_PERL_DEBUG("db_clone.modifySpfile(): *** END ***"); } # Create init parameter file based on source DB's v$parameter # For "STANDBY_NO_RECOVERY or "STANDBY_RECOVERY, the control file is backed up # as ctlFileName for standyby. This option is for data guard to create # physical or logical standby database. To clone a normal database, the # control file is not backed up. # Call &set_env($oracleHome, $oracleSid) before calling this method. # createSourceInitParamFileAndStandbyCtlFile(pfileFullName, filteredParams, # ctlFileFullName) sub createSourceInitParamFileAndStandbyCtlFile { EMD_PERL_DEBUG("db_clone.createSourceInitParamFileAndStandbyCtlFile(): *** START ***"); my ($pfileFullName, $filteredParamNames, $ctlFileFullName) = @_; EMD_PERL_DEBUG("db_clone.createSourceInitParamFileAndStandbyCtlFile(): filteredParamNames: $filteredParamNames"); EMD_PERL_DEBUG("db_clone.createSourceInitParamFileAndStandbyCtlFile(): ctlFileName: $ctlFileFullName"); &createSourceInitParamFile("$pfileFullName", $filteredParamNames); EMD_PERL_DEBUG("db_clone.createSourceInitParamFileAndStandbyCtlFile(): *** END ***"); } # Create control file for standby (physical or logical) sub createStandbyCtlFile { EMD_PERL_DEBUG("db_clone.createStandbyCtlFile(): *** START ***"); my ($ctlFileFullName) = @_; EMD_PERL_DEBUG("db_clone.createStandbyCtlFile(): ctlFileName: $ctlFileFullName"); #Backup control file my $sql_string = ""; $sql_string .= "CONNECT $userID AS SYSDBA;\n"; if(($clonePurpose eq "STANDBY_NO_RECOVERY") || ($clonePurpose eq "STANDBY_RECOVERY")) { $sql_string .= "ALTER DATABASE CREATE STANDBY CONTROLFILE AS '$ctlFileFullName';\n"; } else { ## Different controlfile commands for logical standby 9i & 10i if($clonePurpose eq "LOGICAL_STANDBY") { $sql_string .= "ALTER DATABASE BACKUP CONTROLFILE TO '$ctlFileFullName';\n"; } elsif($clonePurpose =~ /^LOGICAL_STANDBY_10I$/i) { $sql_string .= "ALTER DATABASE CREATE LOGICAL STANDBY CONTROLFILE AS '$ctlFileFullName';\n"; } else #for normal DBClone { $sql_string .= "ALTER DATABASE BACKUP CONTROLFILE TO '$ctlFileFullName';\n"; } } $sql_string .= "EXIT;\n"; (my $fh, my $filename) = &runSqlOnSource($sql_string); close $fh; if($NT) { &removeFile($filename); } EMD_PERL_DEBUG("db_clone.createStandbyCtlFile(): *** END ***"); } # Backup one datafile and store it as given backupPieceName # Call &set_env($oracleHome, $oracleSid) before calling this method. # backupDatafile(backupPieceName, fileNum) sub backupDatafile { EMD_PERL_DEBUG("db_clone.backupDatafile(): *** START ***"); my ($backupPieceName, $fileNum) = @_; EMD_PERL_DEBUG("db_clone.backupDatafile(): temp file to store backup: $backupPieceName"); EMD_PERL_DEBUG("db_clone.backupDatafile(): fileNum to be backed up: $fileNum"); #backup one datafile my $sql_string = ""; $sql_string .= "set echo off\n"; $sql_string .= "CONNECT $userID AS SYSDBA;\n"; $sql_string .= "variable devicename varchar2(255);\n"; $sql_string .= "variable set_stamp number;\n"; $sql_string .= "variable set_count number;\n\n"; $sql_string .= "declare\n"; $sql_string .= " pieceno binary_integer;\n"; $sql_string .= " done boolean;\n"; $sql_string .= " handle varchar2(256);\n"; $sql_string .= " comment varchar2(256);\n"; $sql_string .= " media varchar2(256);\n"; $sql_string .= " concur boolean;\n"; $sql_string .= " params varchar2(256);\n\n"; $sql_string .= "BEGIN\n"; $sql_string .= " :devicename := dbms_backup_restore.deviceAllocate;\n"; $sql_string .= " dbms_backup_restore.backupSetDataFile(:set_stamp, :set_count);\n"; $sql_string .= " dbms_backup_restore.backupDataFile($fileNum);\n"; $sql_string .= " dbms_backup_restore.backupPieceCreate('$backupPieceName',\n"; $sql_string .= " pieceno, done,\n"; $sql_string .= " handle, comment,\n"; $sql_string .= " media, concur,\n"; $sql_string .= " params, reuse=>true);\n"; $sql_string .= "END;\n"; $sql_string .= "/\n"; $sql_string .= "EXIT;\n\n"; (my $fh, my $filename) = &runSqlOnSource($sql_string); close $fh; if($NT) { &removeFile($filename); } EMD_PERL_DEBUG("db_clone.backupDatafile(): *** END ***"); } # To replace backupDatafile() # Backup one datafile and store it as given backupPieceName # Call &set_env($oracleHome, $oracleSid) before calling this method. # backupOneDatafile(backupPieceName, fileNum) sub backupOneDatafile { EMD_PERL_DEBUG("db_clone.backupOneDatafile(): *** START ***"); my ($backupPieceName, $fileNum) = @_; EMD_PERL_DEBUG("db_clone.backupOneDatafile(): fileNum to be backed up: $fileNum"); EMD_PERL_DEBUG("db_clone.backupOneDatafile(): fileName to store backup piece: $backupPieceName"); #backup one datafile my $sql_string = ""; $sql_string .= "set echo off\n"; $sql_string .= "CONNECT $userID AS SYSDBA;\n"; $sql_string .= "variable devicename varchar2(255);\n"; $sql_string .= "variable set_stamp number;\n"; $sql_string .= "variable set_count number;\n\n"; $sql_string .= "declare\n"; $sql_string .= " pieceno binary_integer;\n"; $sql_string .= " done boolean;\n"; $sql_string .= " handle varchar2(256);\n"; $sql_string .= " comment varchar2(256);\n"; $sql_string .= " media varchar2(256);\n"; $sql_string .= " concur boolean;\n"; $sql_string .= " params varchar2(256);\n\n"; $sql_string .= "BEGIN\n"; $sql_string .= " :devicename := dbms_backup_restore.deviceAllocate;\n"; $sql_string .= " dbms_backup_restore.backupSetDataFile(:set_stamp, :set_count);\n"; $sql_string .= " dbms_backup_restore.backupDataFile($fileNum);\n"; $sql_string .= " dbms_backup_restore.backupPieceCreate('$backupPieceName',\n"; $sql_string .= " pieceno, done,\n"; $sql_string .= " handle, comment,\n"; $sql_string .= " media, concur,\n"; $sql_string .= " params, reuse=>true);\n"; $sql_string .= "END;\n"; $sql_string .= "/\n"; $sql_string .= "EXIT;\n\n"; (my $fh, my $filename) = &runSqlOnSource($sql_string); close $fh; if($NT) { &removeFile($filename); } EMD_PERL_DEBUG("db_clone.backupOneDatafile(): *** END ***"); } # Backup multiple datafiles and store it (name: PIECE) in a given temp location # Call &set_env($oracleHome, $oracleSid) before calling this method. # backupMultipleDatafiles(backupPieceName, fileNums) sub backupMultipleDatafiles { EMD_PERL_DEBUG("db_clone.backupMultipleDatafiles(): *** START ***"); my ($backupPieceName, $fileNums) = @_; EMD_PERL_DEBUG("db_clone.backupMultipleDatafiles(): fileNum to be backed up: $fileNums"); EMD_PERL_DEBUG("db_clone.backupMultipleDatafiles(): Backup piece name: $backupPieceName"); my @datafileNums = split /$DELIMITER/, $fileNums; #backup one datafile my $sql_string = ""; $sql_string .= "set echo off\n"; $sql_string .= "CONNECT $userID AS SYSDBA;\n"; $sql_string .= "variable devicename varchar2(255);\n"; $sql_string .= "variable set_stamp number;\n"; $sql_string .= "variable set_count number;\n\n"; $sql_string .= "declare\n"; $sql_string .= " pieceno binary_integer;\n"; $sql_string .= " done boolean;\n"; $sql_string .= " handle varchar2(256);\n"; $sql_string .= " comment varchar2(256);\n"; $sql_string .= " media varchar2(256);\n"; $sql_string .= " concur boolean;\n"; $sql_string .= " params varchar2(256);\n\n"; $sql_string .= "BEGIN\n"; $sql_string .= " :devicename := dbms_backup_restore.deviceAllocate;\n"; $sql_string .= " dbms_backup_restore.backupSetDataFile(:set_stamp, :set_count);\n"; my $datafileNums; foreach $datafileNums (@datafileNums) { $sql_string .= " dbms_backup_restore.backupDataFile($datafileNums);\n"; } $sql_string .= " dbms_backup_restore.backupPieceCreate('$backupPieceName',\n"; $sql_string .= " pieceno, done,\n"; $sql_string .= " handle, comment,\n"; $sql_string .= " media, concur,\n"; $sql_string .= " params, reuse=>true);\n"; $sql_string .= "END;\n"; $sql_string .= "/\n"; $sql_string .= "EXIT;\n\n"; (my $fh, my $filename) = &runSqlOnSource($sql_string); close $fh; if($NT) { &removeFile($filename); } EMD_PERL_DEBUG("db_clone.backupMultipleDatafiles(): *** END ***"); } # Backup archived logs in one piece and store it (name: ARCH) in a given temp # location # Call &set_env($oracleHome, $oracleSid) before calling this method. # backupLog(backupPieceName, maxSeqNum) sub backupLog { EMD_PERL_DEBUG("db_clone.backupLog(): *** START ***"); my ($backupPieceName, $maxSeqNum) = @_; EMD_PERL_DEBUG("db_clone.backupLog(): Log max sequence number: $maxSeqNum"); EMD_PERL_DEBUG("db_clone.backupLog(): Backup piece name: $backupPieceName"); ## Logical standby: build dictionary prior to log backup (9i only) if($clonePurpose eq "LOGICAL_STANDBY") { prepareLogical(); } #backup all archived logs to one piece my $sql_string = ""; $sql_string .= "set echo off\n"; $sql_string .= "CONNECT $userID AS SYSDBA;\n"; $sql_string .= "ALTER SYSTEM ARCHIVE LOG CURRENT;\n\n"; $sql_string .= "variable devicename varchar2(255);\n"; $sql_string .= "variable set_stamp number;\n"; $sql_string .= "variable set_count number;\n\n"; $sql_string .= "declare\n"; $sql_string .= " pieceno binary_integer;\n"; $sql_string .= " done boolean;\n"; $sql_string .= " handle varchar2(256);\n"; $sql_string .= " comment varchar2(256);\n"; $sql_string .= " media varchar2(256);\n"; $sql_string .= " concur boolean;\n"; $sql_string .= " params varchar2(256);\n\n"; $sql_string .= "BEGIN\n"; $sql_string .= " :devicename := dbms_backup_restore.deviceAllocate;\n"; $sql_string .= " dbms_backup_restore.backupSetArchivedLog(:set_stamp, :set_count);\n"; $sql_string .= " FOR alrec IN (SELECT name, recid, stamp FROM V\$ARCHIVED_LOG \n"; # $sql_string .= " WHERE sequence#>$maxSeqNum and standby_dest = 'NO')\n"; $sql_string .= " WHERE $maxSeqNum < next_change# and standby_dest = 'NO' and ARCHIVED = 'YES' and status = 'A' and RESETLOGS_CHANGE# = (select RESETLOGS_CHANGE# from V\$DATABASE))\n"; $sql_string .= " LOOP\n"; $sql_string .= " dbms_backup_restore.backupArchivedLog(alrec.recid, alrec.stamp);\n"; $sql_string .= " END LOOP;\n\n"; $sql_string .= " dbms_backup_restore.backupPieceCreate('$backupPieceName',\n"; $sql_string .= " pieceno, done,\n"; $sql_string .= " handle, comment,\n"; $sql_string .= " media, concur,\n"; $sql_string .= " params, reuse=>true);\n"; $sql_string .= "END;\n"; $sql_string .= "/\n"; $sql_string .= "EXIT;\n\n"; (my $fh, my $filename) = &runSqlOnSource($sql_string); close $fh; if($NT) { &removeFile($filename); } EMD_PERL_DEBUG("db_clone.backupLog(): *** END ***"); } # Run logical standby preparate procedure sub prepareLogical { EMD_PERL_DEBUG("db_clone.prepareLogical(): *** START ***"); my($sql_string) = ""; $sql_string .= "set echo off\n"; $sql_string .= "CONNECT $userID AS SYSDBA;\n"; $sql_string .= "set serveroutput on;\n"; $sql_string .= "variable state VARCHAR2(9);\n"; $sql_string .= "BEGIN\n"; ## Quiesce $sql_string .= "SELECT active_state into :state from v\$instance;\n"; $sql_string .= "IF (:state = 'NORMAL') then\n"; $sql_string .= " EXECUTE IMMEDIATE 'ALTER SYSTEM QUIESCE RESTRICTED';\n"; $sql_string .= " dbms_output.put_line('db is quiesced');\n"; $sql_string .= "END IF;\n"; ## Logical dictionary build $sql_string .= "dbms_output.put_line('Building data dictionary');\n"; $sql_string .= "DBMS_LOGMNR_D.BUILD(options => DBMS_LOGMNR_D.STORE_IN_REDO_LOGS);\n"; ## Switch a log to to seed the stream w/ the dictionary log $sql_string .= "EXECUTE IMMEDIATE 'ALTER SYSTEM ARCHIVE LOG CURRENT';\n"; ## Unquiesce $sql_string .= "SELECT active_state into :state from v\$instance;\n"; $sql_string .= "IF (:state != 'NORMAL') then\n"; $sql_string .= " EXECUTE IMMEDIATE 'ALTER SYSTEM UNQUIESCE';\n"; $sql_string .= " dbms_output.put_line('db is unquiesced');\n"; $sql_string .= "END IF;\n"; $sql_string .= "END;\n"; $sql_string .= "/\n"; (my $fh, my $filename) = &runSqlOnSource($sql_string); close $fh; if($NT) { &removeFile($filename); } EMD_PERL_DEBUG("db_clone.prepareLogical(): *** END ***"); } # Restore one datafile to the given working directory # Call &set_env($oracleHome, $oracleSid) before calling this method. # restoreDatafile(workDir, backupPieceName, fileNum) sub restoreDatafile { EMD_PERL_DEBUG("db_clone.restoreDatafile(): *** START ***"); my ($workDir, $backupPieceName, $fileNum) = @_; my $dbName = $ENV{ORACLE_SID}; my @tempAry = convertOneDatafileNum($dbName, $workDir, $fileNum); my $datafileName = $tempAry[0]; EMD_PERL_DEBUG("db_clone.restoreDatafile(): Backup piece name: $backupPieceName"); EMD_PERL_DEBUG("db_clone.restoreDatafile(): File number to be restored: $fileNum"); EMD_PERL_DEBUG("db_clone.restoreDatafile(): The restored file name: $datafileName"); #restore one datafile my $sql_string = ""; $sql_string .= "set echo off\n"; $sql_string .= "variable devicename varchar2(255);\n"; $sql_string .= "declare\n"; $sql_string .= " done boolean;\n"; $sql_string .= "BEGIN\n"; $sql_string .= " :devicename := dbms_backup_restore.deviceAllocate;\n"; $sql_string .= " dbms_backup_restore.restoreSetDataFile;\n"; $sql_string .= " dbms_backup_restore.restoreDataFileTo($fileNum, '$datafileName');\n"; $sql_string .= " dbms_backup_restore.restoreBackupPiece('$backupPieceName', done);\n\n"; $sql_string .= " IF done then\n"; $sql_string .= " dbms_backup_restore.deletefile('$backupPieceName');\n"; $sql_string .= " end if;\n"; $sql_string .= "END;\n"; $sql_string .= "/\n"; $sql_string .= "EXIT;\n\n"; (my $fh, my $filename) = &runSqlOnDestination($sql_string); close $fh; if($NT) { &removeFile($filename); } EMD_PERL_DEBUG("db_clone.restoreDatafile(): *** END ***"); } # Restore one datafile to the given working directory # Call &set_env($oracleHome, $oracleSid) before calling this method. # restoreDatafileSameHost(workDir, backupPieceName, fileNum) sub restoreDatafileSameHost { EMD_PERL_DEBUG("db_clone.restoreDatafileSameHost(): *** START ***"); my ($workDir, $backupPieceName, $fileNum) = @_; my $dbName = $ENV{ORACLE_SID}; my @tempAry = convertOneDatafileNum($dbName, $workDir, $fileNum); my $datafileName = $tempAry[0]; EMD_PERL_DEBUG("db_clone.restoreDatafileSameHost(): Backup piece name: $backupPieceName"); EMD_PERL_DEBUG("db_clone.restoreDatafileSameHost(): File number to be restored: $fileNum"); EMD_PERL_DEBUG("db_clone.restoreDatafileSameHost(): The restored file name: $datafileName"); #restore one datafile my $sql_string = ""; $sql_string .= "set echo off\n"; $sql_string .= "variable devicename varchar2(255);\n"; $sql_string .= "declare\n"; $sql_string .= " done boolean;\n"; $sql_string .= "BEGIN\n"; $sql_string .= " :devicename := dbms_backup_restore.deviceAllocate;\n"; $sql_string .= " dbms_backup_restore.restoreSetDataFile;\n"; $sql_string .= " dbms_backup_restore.restoreDataFileTo($fileNum, '$datafileName');\n"; $sql_string .= " dbms_backup_restore.restoreBackupPiece('$backupPieceName', done);\n\n"; $sql_string .= "END;\n"; $sql_string .= "/\n"; $sql_string .= "EXIT;\n\n"; (my $fh, my $filename) = &runSqlOnDestination($sql_string); close $fh; if($NT) { &removeFile($filename); } EMD_PERL_DEBUG("db_clone.restoreDatafileSameHost(): *** END ***"); } # To replace restoreDatafile() # Restore one datafile to the given working directory # Call &set_env($oracleHome, $oracleSid) before calling this method. # restoreOneDatafile(backupPieceName, fileNum, fileName) sub restoreOneDatafile { EMD_PERL_DEBUG("db_clone.restoreOneDatafile(): *** START ***"); my ($backupPieceName, $fileNum, $fileName) = @_; EMD_PERL_DEBUG("db_clone.restoreOneDatafile(): Backup piece name: $backupPieceName"); EMD_PERL_DEBUG("db_clone.restoreOneDatafile(): File number to be restored: $fileNum"); EMD_PERL_DEBUG("db_clone.restoreOneDatafile(): The restored file name: $fileName"); #restore one datafile my $sql_string = ""; $sql_string .= "set echo off\n"; $sql_string .= "variable devicename varchar2(255);\n"; $sql_string .= "declare\n"; $sql_string .= " done boolean;\n"; $sql_string .= "BEGIN\n"; $sql_string .= " :devicename := dbms_backup_restore.deviceAllocate;\n"; $sql_string .= " dbms_backup_restore.restoreSetDataFile;\n"; $sql_string .= " dbms_backup_restore.restoreDataFileTo($fileNum, '$fileName');\n"; $sql_string .= " dbms_backup_restore.restoreBackupPiece('$backupPieceName', done);\n\n"; $sql_string .= " IF done then\n"; $sql_string .= " dbms_backup_restore.deletefile('$backupPieceName');\n"; $sql_string .= " end if;\n"; $sql_string .= "END;\n"; $sql_string .= "/\n"; $sql_string .= "EXIT;\n\n"; (my $fh, my $filename) = &runSqlOnDestination($sql_string); close $fh; if($NT) { &removeFile($filename); } EMD_PERL_DEBUG("db_clone.restoreOneDatafile(): *** END ***"); } # To replace restoreDatafileSameHost() # Restore one datafile as the given fileName # Call &set_env($oracleHome, $oracleSid) before calling this method. # restoreOneDatafileSameHost(backupPieceName, fileNum, fileName) sub restoreOneDatafileSameHost { EMD_PERL_DEBUG("db_clone.restoreOneDatafile(): *** START ***"); my ($backupPieceName, $fileNum, $fileName) = @_; EMD_PERL_DEBUG("db_clone.restoreOneDatafile(): Backup piece name: $backupPieceName"); EMD_PERL_DEBUG("db_clone.restoreOneDatafile(): File number to be restored: $fileNum"); EMD_PERL_DEBUG("db_clone.restoreOneDatafile(): The restored file name: $fileName"); #restore one datafile my $sql_string = ""; $sql_string .= "set echo off\n"; $sql_string .= "variable devicename varchar2(255);\n"; $sql_string .= "declare\n"; $sql_string .= " done boolean;\n"; $sql_string .= "BEGIN\n"; $sql_string .= " :devicename := dbms_backup_restore.deviceAllocate;\n"; $sql_string .= " dbms_backup_restore.restoreSetDataFile;\n"; $sql_string .= " dbms_backup_restore.restoreDataFileTo($fileNum, '$fileName');\n"; $sql_string .= " dbms_backup_restore.restoreBackupPiece('$backupPieceName', done);\n\n"; $sql_string .= "END;\n"; $sql_string .= "/\n"; $sql_string .= "EXIT;\n\n"; (my $fh, my $filename) = &runSqlOnDestination($sql_string); close $fh; if($NT) { &removeFile($filename); } EMD_PERL_DEBUG("db_clone.restoreOneDatafile(): *** END ***"); } # Restore multiple datafiles to the given working directory # Call &set_env($oracleHome, $oracleSid) before calling this method. # restoreMultipleDatafiles(workDir, backupPieceName, fileNums) sub restoreMultipleDatafiles { EMD_PERL_DEBUG("db_clone.restoreMultipleDatafiles(): *** START ***"); my ($workDir, $backupPieceName, $fileNums) = @_; my $dbName = $ENV{ORACLE_SID}; my @datafileNums = split /$DELIMITER/, $fileNums; my @datafileNames = convertDatafileNums($dbName, $workDir, $fileNums); EMD_PERL_DEBUG("db_clone.restoreMultipleDatafiles(): Backup piece name: $backupPieceName"); EMD_PERL_DEBUG("db_clone.restoreMultipleDatafiles(): File number to be restored: $fileNums"); EMD_PERL_DEBUG("db_clone.restoreMultipleDatafiles(): The restored file names: @datafileNames"); #restore one datafile my $sql_string = ""; $sql_string .= "set echo off\n"; $sql_string .= "variable devicename varchar2(255);\n"; $sql_string .= "declare\n"; $sql_string .= " done boolean;\n"; $sql_string .= "BEGIN\n"; $sql_string .= " :devicename := dbms_backup_restore.deviceAllocate;\n"; $sql_string .= " dbms_backup_restore.restoreSetDataFile;\n"; my $datafileNum; my $index = 0; foreach $datafileNum (@datafileNums) { $sql_string .= " dbms_backup_restore.restoreDataFileTo($datafileNum, '$datafileNames[$index]');\n"; $index ++; } $sql_string .= " dbms_backup_restore.restoreBackupPiece('$backupPieceName', done);\n\n"; $sql_string .= " IF done then\n"; $sql_string .= " dbms_backup_restore.deletefile('$backupPieceName');\n"; $sql_string .= " end if;\n"; $sql_string .= "END;\n"; $sql_string .= "/\n"; $sql_string .= "EXIT;\n\n"; (my $fh, my $filename) = &runSqlOnDestination($sql_string); close $fh; if($NT) { &removeFile($filename); } EMD_PERL_DEBUG("db_clone.restoreMultipleDatafiles(): *** END ***"); } # Restore backup piece containing archived logs to the given working directory # Call &set_env($oracleHome, $oracleSid) before calling this method. # restoreLog(backupPieceName, restoreLogName) sub restoreLog { EMD_PERL_DEBUG("db_clone.restoreLog(): *** START ***"); my ($backupPieceName, $restoreLogName) = @_; EMD_PERL_DEBUG("db_clone.restoreLog(): Log backup piece name: $backupPieceName"); EMD_PERL_DEBUG("db_clone.restoreLog(): The prefix of file name to be restored: $restoreLogName"); #restore logs my $sql_string = ""; $sql_string .= "set echo off\n"; $sql_string .= "variable devicename varchar2(255);\n"; $sql_string .= "declare\n"; $sql_string .= " done boolean;\n"; $sql_string .= "BEGIN\n"; $sql_string .= " :devicename := dbms_backup_restore.deviceAllocate;\n"; $sql_string .= " dbms_backup_restore.restoreSetArchivedLog('$restoreLogName');\n"; $sql_string .= " dbms_backup_restore.restoreArchivedLogRange();\n"; $sql_string .= " dbms_backup_restore.restoreBackupPiece('$backupPieceName', done);\n\n"; $sql_string .= " IF done then\n"; $sql_string .= " dbms_backup_restore.deletefile('$backupPieceName');\n"; $sql_string .= " end if;\n"; $sql_string .= "END;\n"; $sql_string .= "/\n"; $sql_string .= "EXIT;\n\n"; (my $fh, my $filename) = &runSqlOnDestination($sql_string); close $fh; if($NT) { &removeFile($filename); } EMD_PERL_DEBUG("db_clone.restoreLog(): *** END ***"); } # Restore backup piece containing archived logs to the given working directory # Call &set_env($oracleHome, $oracleSid) before calling this method. # restoreLogSameHostExistingBS(backupPieceName, restoreLogName) sub restoreLogSameHostExistingBS { EMD_PERL_DEBUG("db_clone.restoreLogSameHostExistingBS(): *** START ***"); my ($backupPieceName, $restoreLogName) = @_; EMD_PERL_DEBUG("db_clone.restoreLogSameHostExistingBS(): Log backup piece name: $backupPieceName"); EMD_PERL_DEBUG("db_clone.restoreLogSameHostExistingBS(): The prefix of file name to be restored: $restoreLogName"); #restore logs my $sql_string = ""; $sql_string .= "set echo off\n"; $sql_string .= "variable devicename varchar2(255);\n"; $sql_string .= "declare\n"; $sql_string .= " done boolean;\n"; $sql_string .= "BEGIN\n"; $sql_string .= " :devicename := dbms_backup_restore.deviceAllocate;\n"; $sql_string .= " dbms_backup_restore.restoreSetArchivedLog('$restoreLogName');\n"; $sql_string .= " dbms_backup_restore.restoreArchivedLogRange();\n"; $sql_string .= " dbms_backup_restore.restoreBackupPiece('$backupPieceName', done);\n\n"; $sql_string .= "END;\n"; $sql_string .= "/\n"; $sql_string .= "EXIT;\n\n"; (my $fh, my $filename) = &runSqlOnDestination($sql_string); close $fh; if($NT) { &removeFile($filename); } EMD_PERL_DEBUG("db_clone.restoreLogSameHostExistingBS(): *** END ***"); } # Restore backup piece containing archived logs to the given working directory # Call &set_env($oracleHome, $oracleSid) before calling this method. # restoreLogSameHost(backupPieceName, restoreLogName) sub restoreLogSameHost { EMD_PERL_DEBUG("db_clone.restoreLogSameHost(): *** START ***"); my ($backupPieceName, $restoreLogName) = @_; EMD_PERL_DEBUG("db_clone.restoreLogSameHost(): Log backup piece name: $backupPieceName"); EMD_PERL_DEBUG("db_clone.restoreLogSameHost(): The prefix of file name to be restored: $restoreLogName"); #restore logs my $sql_string = ""; $sql_string .= "set echo off\n"; $sql_string .= "CONNECT $userID AS SYSDBA;\n"; $sql_string .= "variable devicename varchar2(255);\n"; $sql_string .= "declare\n"; $sql_string .= " done boolean;\n"; $sql_string .= "BEGIN\n"; $sql_string .= " :devicename := dbms_backup_restore.deviceAllocate;\n"; $sql_string .= " dbms_backup_restore.restoreSetArchivedLog('$restoreLogName');\n"; $sql_string .= " dbms_backup_restore.restoreArchivedLogRange();\n"; $sql_string .= " dbms_backup_restore.restoreBackupPiece('$backupPieceName', done);\n\n"; #$sql_string .= " IF done then\n"; #$sql_string .= " dbms_backup_restore.deletefile('$backupPieceName');\n"; #$sql_string .= " end if;\n"; $sql_string .= "END;\n"; $sql_string .= "/\n"; $sql_string .= "EXIT;\n\n"; (my $fh, my $filename) = &runSqlOnSource($sql_string); close $fh; if($NT) { &removeFile($filename); } EMD_PERL_DEBUG("db_clone.restoreLogSameHost(): *** END ***"); } # Depricated # Restore one datafile to the given working directory for remote operations # restoreDatafileWithWaiting(oracleHome, dbName, workDir, tempDir, fileNum) sub restoreDatafileWithWaiting { my ($oracleHome, $dbName, $workDir, $tempDir, $fileNum) = @_; $ENV{ORACLE_HOME} = "$oracleHome"; $ENV{ORACLE_SID} = "$dbName"; my $backupPieceName = "$tempDir/PIECE"; my $datafileName = "$workDir/$dbName"."_file"."$fileNum".".dbf"; my $dir = tempdir( CLEANUP => 1); (my $fh, my $filename) = tempfile( DIR => $dir ); #restore one datafile open(SQL_SCRIPT, "|$ENV{ORACLE_HOME}/bin/sqlplus /nolog >$filename") || die "Cannot open pipe for RESTORE_DATAFILE"; print SQL_SCRIPT "set echo off\n"; print SQL_SCRIPT "connect / as sysdba;\n"; print SQL_SCRIPT "set serveroutput on;\n\n"; print SQL_SCRIPT "variable devicename varchar2(255);\n"; print SQL_SCRIPT "declare\n"; print SQL_SCRIPT " done boolean;\n"; print SQL_SCRIPT " i binary_integer := 1;\n"; print SQL_SCRIPT " j binary_integer := 0;\n"; print SQL_SCRIPT " ora_19624 EXCEPTION;\n"; print SQL_SCRIPT " PRAGMA EXCEPTION_INIT(ora_19624, -19624);\n"; print SQL_SCRIPT "BEGIN\n"; print SQL_SCRIPT " dbms_output.enable(2000000);\n"; print SQL_SCRIPT " dbms_output.put_line(' ');\n"; print SQL_SCRIPT " dbms_output.put_line(' RESTORE: Allocating device... ');\n"; print SQL_SCRIPT " :devicename := dbms_backup_restore.deviceAllocate;\n"; print SQL_SCRIPT " dbms_output.put_line(' RESTORE: Specifing datafiles... ');\n"; print SQL_SCRIPT " dbms_backup_restore.restoreSetDataFile;\n"; print SQL_SCRIPT " dbms_backup_restore.restoreDataFileTo($fileNum, '$datafileName');\n"; print SQL_SCRIPT " LOOP\n"; print SQL_SCRIPT " j := 0;\n"; print SQL_SCRIPT " <>\n"; print SQL_SCRIPT " j := j + 1;\n"; print SQL_SCRIPT " dbms_output.put_line(' RESTORE: Restoring piece ');\n"; print SQL_SCRIPT " BEGIN\n"; print SQL_SCRIPT " dbms_backup_restore.restoreBackupPiece('$backupPieceName', done);\n\n"; print SQL_SCRIPT " EXCEPTION\n"; print SQL_SCRIPT " WHEN ora_19624 THEN\n"; print SQL_SCRIPT " IF j < 400\n"; print SQL_SCRIPT " THEN\n"; print SQL_SCRIPT " dbms_backup_restore.sleep(30);\n"; print SQL_SCRIPT " GOTO retry;\n"; print SQL_SCRIPT " ELSE\n"; print SQL_SCRIPT " dbms_output.put_line(' RESTORE: Too many retries. Giving up.');\n"; print SQL_SCRIPT " RAISE;\n"; print SQL_SCRIPT " END IF;\n"; print SQL_SCRIPT " RAISE;\n"; print SQL_SCRIPT " WHEN others THEN\n"; print SQL_SCRIPT " RAISE;\n"; print SQL_SCRIPT " END;\n"; print SQL_SCRIPT " IF done then\n"; print SQL_SCRIPT " dbms_backup_restore.deletefile('$backupPieceName');\n"; print SQL_SCRIPT " dbms_output.put_line(' RESTORE: Restore datafile done.');\n"; print SQL_SCRIPT " EXIT;\n"; print SQL_SCRIPT " else\n"; print SQL_SCRIPT " dbms_output.put_line(' RESTORE: Restore datafile failed');\n"; print SQL_SCRIPT " EXIT;\n"; print SQL_SCRIPT " end if;\n"; print SQL_SCRIPT " END LOOP;\n"; print SQL_SCRIPT "END;\n"; print SQL_SCRIPT "/\n"; print SQL_SCRIPT "EXIT;\n\n"; close SQL_SCRIPT || die "Bad SQL_SCRIPT_RESTORE_DATAFILE"; open (RESTORE_DATAFILE, "$filename") || die "Unable to open tempfile\n"; while () { print; } close RESTORE_DATAFILE; close $fh; if($NT) { &removeFile($filename); } } # Depricated # Restore backup piece containing archived logs to the given working directory # for remote operations # restoreLogWithWaiting(oracleHome, dbName, workDir, tempDir) sub restoreLogWithWaiting { my ($oracleHome, $dbName, $workDir, $tempDir) = @_; $ENV{ORACLE_HOME} = "$oracleHome"; $ENV{ORACLE_SID} = "$dbName"; #my $restoreLogName = "$workDir/ARCH"; my $restoreLogName = "$tempDir/ARCH"; my $backupPieceName = "$tempDir/ARCH"; my $dir = tempdir( CLEANUP => 1); (my $fh, my $filename) = tempfile( DIR => $dir ); #restore one datafile open(SQL_SCRIPT, "|$ENV{ORACLE_HOME}/bin/sqlplus /nolog >$filename") || die "Cannot open pipe for RESTORE_LOG"; print SQL_SCRIPT "set echo off\n"; print SQL_SCRIPT "connect / as sysdba;\n"; print SQL_SCRIPT "set serveroutput on;\n\n"; print SQL_SCRIPT "variable devicename varchar2(255);\n"; print SQL_SCRIPT "declare\n"; print SQL_SCRIPT " done boolean;\n"; print SQL_SCRIPT " i binary_integer := 1;\n"; print SQL_SCRIPT " j binary_integer := 0;\n"; print SQL_SCRIPT " ora_19624 EXCEPTION;\n"; print SQL_SCRIPT " PRAGMA EXCEPTION_INIT(ora_19624, -19624);\n"; print SQL_SCRIPT "BEGIN\n"; print SQL_SCRIPT " dbms_output.enable(2000000);\n"; print SQL_SCRIPT " dbms_output.put_line(' ');\n"; print SQL_SCRIPT " dbms_output.put_line(' RESTORE: Allocating device... ');\n"; print SQL_SCRIPT " :devicename := dbms_backup_restore.deviceAllocate;\n"; print SQL_SCRIPT " dbms_output.put_line(' RESTORE: Specifing archived logs... ');\n"; print SQL_SCRIPT " dbms_backup_restore.restoreSetArchivedLog('$restoreLogName');\n"; print SQL_SCRIPT " dbms_backup_restore.restoreArchivedLogRange();\n"; print SQL_SCRIPT " LOOP\n"; print SQL_SCRIPT " j := 0;\n"; print SQL_SCRIPT " <>\n"; print SQL_SCRIPT " j := j + 1;\n"; print SQL_SCRIPT " dbms_output.put_line(' RESTORE: Restoring piece ');\n"; print SQL_SCRIPT " BEGIN\n"; print SQL_SCRIPT " dbms_backup_restore.restoreBackupPiece('$backupPieceName', done);\n\n"; print SQL_SCRIPT " EXCEPTION\n"; print SQL_SCRIPT " WHEN ora_19624 THEN\n"; print SQL_SCRIPT " IF j < 400\n"; print SQL_SCRIPT " THEN\n"; print SQL_SCRIPT " dbms_backup_restore.sleep(30);\n"; print SQL_SCRIPT " GOTO retry;\n"; print SQL_SCRIPT " ELSE\n"; print SQL_SCRIPT " dbms_output.put_line(' RESTORE: Too many retries. Giving up.');\n"; print SQL_SCRIPT " RAISE;\n"; print SQL_SCRIPT " END IF;\n"; print SQL_SCRIPT " RAISE;\n"; print SQL_SCRIPT " WHEN others THEN\n"; print SQL_SCRIPT " RAISE;\n"; print SQL_SCRIPT " END;\n"; print SQL_SCRIPT " IF done then\n"; print SQL_SCRIPT " dbms_backup_restore.deletefile('$backupPieceName');\n"; print SQL_SCRIPT " dbms_output.put_line(' RESTORE: Restore archivedLogs done.');\n"; print SQL_SCRIPT " EXIT;\n"; print SQL_SCRIPT " else\n"; print SQL_SCRIPT " dbms_output.put_line(' RESTORE: Restore archivedLogs failed');\n"; print SQL_SCRIPT " EXIT;\n"; print SQL_SCRIPT " end if;\n"; print SQL_SCRIPT " END LOOP;\n"; print SQL_SCRIPT "END;\n"; print SQL_SCRIPT "/\n"; print SQL_SCRIPT "EXIT;\n\n"; close SQL_SCRIPT || die "Bad SQL_SCRIPT_RESTORE_LOG"; open (RESTORE_LOG, "$filename") || die "Unable to open tempfile\n"; while () { print; } close RESTORE_LOG; close $fh; if($NT) { &removeFile($filename); } } #convert datafile numbers to datafile names. #This will be done in java code later for more complicated cases. #convertDatafileNums(dbName, workDir, fileNumArray) sub convertDatafileNums() { my ($dbName, $workDir, $fileNumArray) = @_; my @datafileNums = split /$DELIMITER/, $fileNumArray; my @datafileNames = (); my $count = 0; my $datafileNums; foreach $datafileNums (@datafileNums) { $datafileNames[$count] = "$workDir/$dbName"."_file"."$datafileNums".".dbf"; $count ++; } return @datafileNames; } #convert one datafile number to datafile names. #Remove any white space around the datafile number #convertOneDatafileNum(dbName, workDir, fileNum) sub convertOneDatafileNum() { my ($dbName, $workDir, $fileNum) = @_; my @datafileNums = split /\s+/, $fileNum; my @datafileNames = (); my $count = 0; my $datafileNums; foreach $datafileNums (@datafileNums) { if($datafileNums ne ("")) { $datafileNames[$count] = "$workDir/$dbName"."_file"."$datafileNums".".dbf"; $count ++; } } return @datafileNames; } #convert logfile names #This will be done in java code later for more complicated cases. #convertLogfileName(dbName, workDir, logGroupArrray) sub convertLogfileName() { my ($dbName, $workDir, $logGroupArrray) = @_; my @logGroupNums = split /$DELIMITER/, $logGroupArrray; my @logfileNames = (); my $count = 0; my $logGroupNums; foreach $logGroupNums (@logGroupNums) { $logfileNames[$count] = "$workDir/$dbName"."_redo"."$logGroupNums".".log"; $count ++; } return @logfileNames; } # Create a new control file # Call &set_env($oracleHome, $oracleSid) before calling this method. # createControlFile(dfDir, lfDir, fileNumArray, pfileFullName, spfile) sub createControlFileBk { EMD_PERL_DEBUG("db_clone.createControlFile(): *** START ***"); my ($dfDir, $lfDir, $fileNumArray, $pfileFullName, $spfile) = @_; my $dbName = $ENV{ORACLE_SID}; my @datafileNames = &convertDatafileNums($dbName, $dfDir, $fileNumArray); #set two log groups for the moment my @logfileNames = &convertLogfileName($dbName, $lfDir, "1:2:"); EMD_PERL_DEBUG("db_clone.createControlFile(): Restored datafile names: @datafileNames"); EMD_PERL_DEBUG("db_clone.createControlFile(): Created online redo log files: @logfileNames"); #open the database with resetlogs my $sql_string = ""; if($spfile eq "Y") { $sql_string .= "startup force nomount;\n"; } else { $sql_string .= "startup force nomount pfile='$pfileFullName';\n"; } $sql_string .= "create controlfile reuse set database $dbName resetlogs archivelog\n"; $sql_string .= "datafile\n"; foreach (0 .. $#datafileNames - 1) { $sql_string .= " '$datafileNames[$_]' reuse,\n"; } $sql_string .= " '$datafileNames[$#datafileNames]' reuse\n"; $sql_string .= "logfile\n"; foreach (0 .. $#logfileNames - 1) { $sql_string .= " group $_ '$logfileNames[$_]' size 20M reuse,\n"; } $sql_string .= " group $#logfileNames '$logfileNames[$#logfileNames]' size 20M reuse\n"; $sql_string .= "maxinstances 2\n"; $sql_string .= "maxdatafiles 100\n"; $sql_string .= "maxlogfiles 10\n"; $sql_string .= "maxlogmembers 5\n"; $sql_string .= "maxarchlogs 500\n"; $sql_string .= "character set \"WE8DEC\";\n"; $sql_string .= "EXIT;\n\n"; (my $fh, my $filename) = &runSqlOnDestination($sql_string); close $fh; if($NT) { &removeFile($filename); } EMD_PERL_DEBUG("db_clone.createControlFile(): *** END ***"); } # To replace createControlFile() # Create a new control file # One file name in logfileArray represents one GROUP of log file, this will be # changed later. # Call &set_env($oracleHome, $oracleSid) before calling this method. # createControlFile(dbName, datafileArray, loggroupArray, logfileArray, maxInfoArray, pfileFullName, spfile) sub createControlFile { EMD_PERL_DEBUG("db_clone.createControlFile(): *** START ***"); my ($dbName, $datafileArray, $loggroupArray, $logfileArray, $maxInfoArray, $pfileFullName, $spfile) = @_; my @datafileNames = split /$DELIMITER/, $datafileArray; my @loggroupNames = split /$DELIMITER/, $loggroupArray; my @logfileNames = split /$DELIMITER/, $logfileArray; my @maxInfo = split /$DELIMITER/, $maxInfoArray; EMD_PERL_DEBUG("db_clone.createControlFile(): Database Name: $dbName"); EMD_PERL_DEBUG("db_clone.createControlFile(): Restored datafile names: @datafileNames"); EMD_PERL_DEBUG("db_clone.createControlFile(): Created online redo log groups: @loggroupNames"); EMD_PERL_DEBUG("db_clone.createControlFile(): Created online redo log files: @logfileNames"); EMD_PERL_DEBUG("db_clone.createControlFile(): Control file max info: @maxInfo"); #open the database with resetlogs my $sql_string = ""; #$sql_string .= "shutdown abort; \n"; if($spfile eq "Y") { $sql_string .= "startup force nomount;\n"; } else { $sql_string .= "startup force nomount pfile='$pfileFullName';\n"; } $sql_string .= "create controlfile reuse set database $dbName resetlogs archivelog\n"; $sql_string .= "datafile\n"; foreach (0 .. $#datafileNames - 1) { $sql_string .= " '$datafileNames[$_]' reuse,\n"; } $sql_string .= " '$datafileNames[$#datafileNames]' reuse\n"; my $prev_group = ""; my $curr_group = ""; my $next_group = ""; $sql_string .= "logfile\n"; foreach (0 .. $#loggroupNames - 1) { $curr_group = $loggroupNames[$_]; $next_group = $loggroupNames[$_ + 1]; if(($curr_group ne $prev_group) && ($curr_group ne $next_group)) { $sql_string .= " group $loggroupNames[$_] '$logfileNames[$_]' size 20M reuse,\n"; } elsif(($curr_group ne $prev_group) && ($curr_group eq $next_group)) { $sql_string .= " group $loggroupNames[$_] ('$logfileNames[$_]',\n"; } elsif(($curr_group eq $prev_group) && ($curr_group ne $next_group)) { $sql_string .= " '$logfileNames[$_]') size 20M reuse,\n"; } elsif(($curr_group eq $prev_group) && ($curr_group eq $next_group)) { $sql_string .= " '$logfileNames[$_]',\n"; } $prev_group = $curr_group; } $curr_group = $loggroupNames[$#loggroupNames]; if($curr_group ne $prev_group) { $sql_string .= " group $loggroupNames[$#loggroupNames] '$logfileNames[$#logfileNames]' size 20M reuse\n"; } else { $sql_string .= " '$logfileNames[$#logfileNames]') size 20M reuse\n"; } #$sql_string .= "maxinstances 2\n"; #$sql_string .= "maxdatafiles 100\n"; #$sql_string .= "maxlogfiles 10\n"; #$sql_string .= "maxlogmembers 5\n"; #$sql_string .= "maxarchlogs 500\n"; #$sql_string .= "character set \"WE8DEC\";\n"; $sql_string .= "maxlogfiles $maxInfo[0]\n"; $sql_string .= "maxlogmembers $maxInfo[1]\n"; $sql_string .= "maxdatafiles $maxInfo[2]\n"; $sql_string .= "maxinstances $maxInfo[3]\n"; $sql_string .= "maxarchlogs $maxInfo[4]\n"; $sql_string .= "character set $maxInfo[5];\n"; $sql_string .= "EXIT;\n\n"; (my $fh, my $filename) = &runSqlOnDestination($sql_string); close $fh; if($NT) { &removeFile($filename); } EMD_PERL_DEBUG("db_clone.createControlFile(): *** END ***"); } # Recover datafiles from log files generated during cloning process # Call &set_env($oracleHome, $oracleSid) before calling this method. # recoverDatafile(backupArchName, pfileFullName, spfile) sub recoverDatafile { EMD_PERL_DEBUG("db_clone.recoverDatafile(): *** START ***"); if($clodClone eq "Y") { EMD_PERL_DEBUG("db_clone.recoverDatafile(): No recovery has been done since cold clone flag is: $clodClone"); EMD_PERL_DEBUG("db_clone.recoverDatafile(): *** END ***"); return; } my ($backupArchName, $pfileFullName, $spfile) = @_; #get the archivedLog files used to recover the database my @logfileNames = glob "$backupArchName*"; #Handle NFS mount case my $startStr = substr($backupArchName, 0, 2); EMD_PERL_DEBUG("db_clone.recoverDatafile(): StartStr: $startStr"); if($NT && ($startStr eq "\\\\")) { my $length = length($backupArchName); my $path = substr($backupArchName, 0, $length-4); my $glob = "dir /b $backupArchName*"; @logfileNames = `$glob`; foreach (0 .. $#logfileNames) { $logfileNames[$_]=$path.$logfileNames[$_]; chomp($logfileNames[$_]); } } EMD_PERL_DEBUG("db_clone.recoverDatafile(): Transferred archived log files: @logfileNames"); #open the database with resetlogs my $sql_string = ""; if($spfile eq "Y") { $sql_string .= "startup force mount;\n"; } else { $sql_string .= "startup force mount pfile='$pfileFullName';\n"; } $sql_string .= "alter database recover database until cancel using backup controlfile;\n"; my $logfileNames; foreach $logfileNames (@logfileNames) { $sql_string .= "alter database recover logfile '$logfileNames';\n"; } $sql_string .= "alter database recover cancel;\n"; $sql_string .= "EXIT;\n\n"; (my $fh, my $filename) = &runSqlOnDestination($sql_string); close $fh; if($NT) { &removeFile($filename); } EMD_PERL_DEBUG("db_clone.recoverDatafile(): Ignore errors ORA-00278, ORA-00279, ORA-00280, and ORA-00289. These are normal messages."); EMD_PERL_DEBUG("db_clone.recoverDatafile(): *** END ***"); } # Nullify the dbid for all datafiles # Call &set_env($oracleHome, $oracleSid) before calling this method. # nullifyDBid() sub nullifyDBid { EMD_PERL_DEBUG("db_clone.nullifyDBid(): *** START ***"); #nullify the dbid for all datafiles my $sql_string = ""; $sql_string .= "set echo off\n"; $sql_string .= "variable devicename varchar2(255);\n"; $sql_string .= "begin\n"; $sql_string .= " :devicename := dbms_backup_restore.deviceAllocate;\n"; $sql_string .= " dbms_backup_restore.zeroDbid(0);\n"; $sql_string .= "END;\n"; $sql_string .= "/\n"; $sql_string .= "EXIT;\n\n"; (my $fh, my $filename) = &runSqlOnDestination($sql_string); close $fh; if($NT) { &removeFile($filename); } EMD_PERL_DEBUG("db_clone.nullifyDBid(): *** END ***"); } # Restore multiple datafiles to the given working directory # Call &set_env($oracleHome, $oracleSid) before calling this method. # nullifyDatafilesDBid(fileNums, fileNames) sub nullifyDatafilesDBid { EMD_PERL_DEBUG("db_clone.nullifyDatafilesDBid(): *** START ***"); my ($fileNums, $fileNames) = @_; my @datafileNums = split /$DELIMITER/, $fileNums; my @datafileNames = split /$DELIMITER/, $fileNames; EMD_PERL_DEBUG("db_clone.nullifyDatafilesDBid(): File number to be restored: $fileNums"); EMD_PERL_DEBUG("db_clone.nullifyDatafilesDBid(): The restored file names: @datafileNames"); my $sql_string = ""; $sql_string .= "variable devicename varchar2(255);\n"; $sql_string .= "BEGIN\n"; $sql_string .= " :devicename := dbms_backup_restore.deviceAllocate;\n"; $sql_string .= " dbms_backup_restore.restoreSetDataFile;\n"; my $datafileNum; my $index = 0; foreach $datafileNum (@datafileNums) { $sql_string .= " dbms_backup_restore.restoreDataFileTo($datafileNum, '$datafileNames[$index]');\n"; $sql_string .= " dbms_backup_restore.zeroDbid($datafileNum);\n"; $index ++; } $sql_string .= "END;\n"; $sql_string .= "/\n"; $sql_string .= "EXIT;\n\n"; (my $fh, my $filename) = &runSqlOnDestination($sql_string); close $fh; if($NT) { &removeFile($filename); } EMD_PERL_DEBUG("db_clone.nullifyDatafilesDBid(): *** END ***"); } # Open the cloned database with resetlogs # Call &set_env($oracleHome, $oracleSid) before calling this method. # openDBwithResetLog(pfileFullName, spfile) sub openDBwithResetLog { EMD_PERL_DEBUG("db_clone.openDBwithResetLog(): *** START ***"); my ($pfileFullName, $spfile) = @_; #open the database with resetlogs my $sql_string = ""; $sql_string .= "set echo off\n"; if($spfile eq "Y") { $sql_string .= "startup force mount;\n"; } else { $sql_string .= "startup force mount pfile='$pfileFullName';\n"; } $sql_string .= "alter database open resetlogs;\n"; $sql_string .= "SELECT STATUS FROM V\$INSTANCE;\n"; if($clodClone eq "Y") { $sql_string .= "shutdown immediate;\n"; if($spfile eq "Y") { $sql_string .= "startup mount;\n"; } else { $sql_string .= "startup mount pfile='$pfileFullName';\n"; } $sql_string .= "alter database NOARCHIVELOG;\n"; $sql_string .= "alter database open;\n"; $sql_string .= "SELECT STATUS FROM V\$INSTANCE;\n"; } $sql_string .= "exit;\n"; (my $fh, my $filename) = &runSqlOnDestination($sql_string); close $fh; if($NT) { &removeFile($filename); } EMD_PERL_DEBUG("db_clone.openDBwithResetLog(): *** END ***"); } # Add tempfiles after opening the cloned database # Call &set_env($oracleHome, $oracleSid) before calling this method. # addTempfiles(fileNames, tablespaces, sizes, autoextends) sub addTempfiles { EMD_PERL_DEBUG("db_clone.addTempfiles(): *** START ***"); my ($fileNames, $tablespaces, $sizes, $autoextends) = @_; my $db_version = ""; #check DB version my $sql_string = ""; $sql_string .= "set echo off\n"; $sql_string .= "set serveroutput on;\n\n"; $sql_string .= "variable db_version varchar2(20);\n"; $sql_string .= "BEGIN\n"; $sql_string .= " select version INTO :db_version from v\$instance;\n"; $sql_string .= " dbms_output.put_line('Get DB version');\n"; $sql_string .= " dbms_output.put_line('DBversion#: '||:db_version);\n"; $sql_string .= "END;\n"; $sql_string .= "/\n"; $sql_string .= "EXIT;\n"; (my $fh, my $filename) = &runSqlOnDestination($sql_string); open (DB_VERSION, "$filename") || die "Unable to open tempfile for DB_VERSION\n"; while () { if ($_=~/\bDBversion#/) { chomp($_); $db_version = substr $_, 12; #12 is the position that version starts EMD_PERL_DEBUG("db_clone.addTempfiles(): Database version: $db_version"); } } close DB_VERSION; close $fh; if($NT) { &removeFile($filename); } if($fileNames eq "$DELIMITER"){ cleanupDB($db_version); EMD_PERL_DEBUG("db_clone.addTempfiles(): NO TEMPFILES TO ADD, returning"); EMD_PERL_DEBUG("db_clone.addTempfiles(): *** END ***"); return; } #Tempfiles are only supported in DB above 9.0.1 version if(&compareVer($db_version, "9.0.1") < 0) { EMD_PERL_DEBUG("db_clone.addTempfiles(): DB version less than 9.0.1. No tempfiles added."); return; } EMD_PERL_DEBUG("db_clone.addTempfiles(): DB version is or above 9.0.1. Tempfiles are added."); my @tempTablespaces = split /$DELIMITER/, $tablespaces; my @tempFileNames = split /$DELIMITER/, $fileNames; my @tempSizes = split /$DELIMITER/, $sizes; my @tempAutoextends = split /$DELIMITER/, $autoextends; #add multiple tempfiles $sql_string = ""; my $tempFileNames; my $index = 0; foreach $tempFileNames (@tempFileNames) { $sql_string .= "ALTER TABLESPACE \"$tempTablespaces[$index]\" \n"; $sql_string .= "ADD TEMPFILE '$tempFileNames' \n"; $sql_string .= "SIZE $tempSizes[$index] REUSE $tempAutoextends[$index];\n"; $index ++; } $sql_string .= "EXIT;\n"; (my $fh, my $filename) = &runSqlOnDestination($sql_string); close $fh; if($NT) { &removeFile($filename); } cleanupDB($db_version); EMD_PERL_DEBUG("db_clone.addTempfiles(): *** END ***"); } # Cleanup the cloned database. # Call &set_env($oracleHome, $oracleSid) before calling this method. # cleanupDB(version) sub cleanupDB { EMD_PERL_DEBUG("db_clone.cleanupDB(): *** START ***"); my ($db_version) = @_; #Cleanup is only supported in DB above 10.1.0.2 version if(&compareVer($db_version, "10.1.0.2") < 0) { EMD_PERL_DEBUG("db_clone.cleanupDB(): DB version less than 10.1.0.2. No cleanup is done."); return; } EMD_PERL_DEBUG("db_clone.cleanupDB(): DB version is or above 10.1.0.2. Cleanup will be performed."); my $sql_string = ""; $sql_string .= "set echo off\n"; $sql_string .= "set serveroutput on;\n\n"; $sql_string .= "BEGIN\n"; $sql_string .= " dbms_swrf_internal.cleanup_database(cleanup_local => FALSE);\n"; $sql_string .= "END;\n"; $sql_string .= "/\n"; $sql_string .= "EXIT;\n"; (my $fh, my $filename) = &runSqlOnDestination($sql_string); close $fh; if($NT) { &removeFile($filename); } EMD_PERL_DEBUG("db_clone.cleanupDB(): *** END ***"); } # Set the password for DBSNMP user for the cloned database. # Call &set_env($oracleHome, $oracleSid) before calling this method. # setDBSNMPpassword(password) sub setDBSNMPpassword { EMD_PERL_DEBUG("db_clone.setDBSNMPpassword(): *** START ***"); my ($password) = @_; my $sql_string = ""; $sql_string .= "set echo off\n"; $sql_string .= "ALTER USER DBSNMP IDENTIFIED BY \"${password}\";\n"; $sql_string .= "EXIT;\n"; my $notHideOutput; (my $fh, my $filename) = &runSqlOnDestination($sql_string, $notHideOutput, "hideSQL"); close $fh; if($NT) { &removeFile($filename); } EMD_PERL_DEBUG("db_clone.setDBSNMPpassword(): *** END ***"); } # Recover the database # recoverDatabase(dbName, datafileArray, loggroupArray, logfileArray, maxInfoArray, backupArchName, pfileFullName, spfile) sub recoverDatabase { EMD_PERL_DEBUG("db_clone.recoverDatabase(): *** START ***"); if($clonePurpose eq "STANDBY_NO_RECOVERY" || $clonePurpose =~ /LOGICAL_STANDBY/) { EMD_PERL_DEBUG("db_clone.recoverDatabase(): No recovery has been done since clone purpose is: $clonePurpose"); EMD_PERL_DEBUG("db_clone.recoverDatabase(): *** END ***"); return; } my ($dbName, $datafileArray, $loggroupArray, $logfileArray, $maxInfoArray, $backupArchName, $pfileFullName, $spfile) = @_; &createControlFile($dbName, $datafileArray, $loggroupArray, $logfileArray, $maxInfoArray, $pfileFullName, $spfile); &recoverDatafile($backupArchName, $pfileFullName, $spfile); &nullifyDBid(); &createControlFile($dbName, $datafileArray, $loggroupArray, $logfileArray, $maxInfoArray, $pfileFullName, $spfile); &openDBwithResetLog($pfileFullName, $spfile); EMD_PERL_DEBUG("db_clone.recoverDatabase(): *** END ***"); } # Add tempfiles after the database is opened. # The temp files are sorted by file IDs before being passed in. # addTempFilesToDB(fileNames, tablespaces, sizes, autoextends) sub addTempFilesToDB { EMD_PERL_DEBUG("db_clone.addTempFilesToDB(): *** START ***"); if($clonePurpose eq "STANDBY_NO_RECOVERY" || $clonePurpose =~ /LOGICAL_STANDBY/) { EMD_PERL_DEBUG("db_clone.addTempFilesToDB(): No temp files will be added since clone purpose is: $clonePurpose"); EMD_PERL_DEBUG("db_clone.addTempFilesToDB(): *** END ***"); return; } my ($fileNames, $tablespaces, $sizes, $autoextends) = @_; &addTempfiles($fileNames, $tablespaces, $sizes, $autoextends); EMD_PERL_DEBUG("db_clone.addTempFilesToDB(): *** END ***"); } # Create multiple directoris for given dir names # If certain dir's exist, skip them. # Return an array containing OK (created) and NOK (exist or failed). # createNewDirs(onlineDatafileArray, offlineDatafileArray, controlfileArray, # logfileArray, tempfileArray, destTempDir, destBackupCtlFile, adminDumpDirs) sub createNewDirs { my ($onlineDatafileArray, $offlineDatafileArray, $controlfileArray, $logfileArray, $tempfileArray, $destTempDir, $destBackupCtlFile, $adminDumpDirs) = @_; my @onlineDatafiles = split /$DELIMITER/, $onlineDatafileArray; my @offlineDatafiles = split /$DELIMITER/, $offlineDatafileArray; my @controlfiles = split /$DELIMITER/, $controlfileArray; my @logfiles = split /$DELIMITER/, $logfileArray; my @tempfiles = split /$DELIMITER/, $tempfileArray; my @adminDirs = split /$DELIMITER/, $adminDumpDirs; my $onlineDatafiles; my $offlineDatafiles; my $controlfiles; my $logfiles; my $tempfiles; my $adminDirs; my $ret; #check and create datafile directories if not exist foreach $onlineDatafiles (@onlineDatafiles) { $ret = &mkDir(&getDirname($onlineDatafiles)); if($ret eq "NOK"){ exit(1); } } foreach $offlineDatafiles (@offlineDatafiles) { $ret = &mkDir(&getDirname($offlineDatafiles)); if($ret eq "NOK"){ exit(1); } } #check and create controlfiles directories if not exist foreach $controlfiles (@controlfiles) { $ret = &mkDir(&getDirname($controlfiles)); if($ret eq "NOK"){ exit(1); } } #check and create logfiles directories if not exist foreach $logfiles (@logfiles) { $ret = &mkDir(&getDirname($logfiles)); if($ret eq "NOK"){ exit(1); } } #check and create tempfiles directories if not exist foreach $tempfiles (@tempfiles) { $ret = &mkDir(&getDirname($tempfiles)); if($ret eq "NOK"){ exit(1); } } #check and create destTempDir directory if not exist $ret = &mkDir($destTempDir); if($ret eq "NOK"){ exit(1); } #check and create destBackupCtlFile directory if not exist $ret = &mkDir(&getDirname($destBackupCtlFile)); if($ret eq "NOK"){ exit(1); } #check and create admin dump directories if not exist foreach $adminDirs (@adminDirs) { $ret = &mkDir($adminDirs); if($ret eq "NOK"){ exit(1); } } } # This method is not being used currently. DG might be interested in this. # Add offline datafile to control file after opening the cloned database # missingFileNames are queried from v$datafile (sorted by id) # offlineFileNames are got from DatabaseFileAttributes (sorted by id) # Call &set_env($oracleHome, $oracleSid) before calling this method. # addOfflineDatafileToControlfile(missingFileNames, offlineFileNames) sub addOfflineDatafileToControlfile { EMD_PERL_DEBUG("db_clone.addOfflineDatafileToControlfile(): *** START ***"); my ($missingFileNames, $offlineFileNames) = @_; if(($missingFileNames eq "$DELIMITER") || ($offlineFileNames eq "$DELIMITER")){ EMD_PERL_DEBUG("db_clone.addOfflineDatafileToControlfile(): NO MISSING FILE TO ADD, returning"); EMD_PERL_DEBUG("db_clone.addOfflineDatafileToControlfile(): *** END ***"); return; } my @missingFiles = split /$DELIMITER/, $missingFileNames; my @offlineFiles = split /$DELIMITER/, $offlineFileNames; my $sql_string = ""; my $missingFiles; my $index = 0; foreach $missingFiles (@missingFiles) { $sql_string .= "ALTER DATABASE RENAME FILE '$missingFiles' TO '$offlineFiles[$index]'; \n"; $index ++; } $sql_string .= "EXIT;\n"; (my $fh, my $filename) = &runSqlOnDestination($sql_string); close $fh; if($NT) { &removeFile($filename); } EMD_PERL_DEBUG("db_clone.addOfflineDatafileToControlfile(): *** END ***"); } ########################################################## ## Start a listener ########################################################## sub startLsnr{ EMD_PERL_DEBUG("db_clone.startLsnr(): *** START ***"); my($lname, $lfile) = @_; my($lsnrexec) = "$ENV{ORACLE_HOME}${S}bin${S}lsnrctl"; ## Since the listener.ora file may not be the default one, ## set TNS_ADMIN to whatever directory the file is in my($idx) = rindex($lfile,"$S"); if($idx == -1){ EMD_PERL_DEBUG("db_clone.startLsnr(): Cannot determine TNS_ADMIN"); exit(1); } my($tns) = substr($lfile,0,$idx); $ENV{TNS_ADMIN} = "$tns"; EMD_PERL_DEBUG("db_clone.startLsnr(): TNS_ADMIN = $ENV{TNS_ADMIN}"); #remember the original OH my $original_oracle_home = $ENV{ORACLE_HOME}; my $reset_env = "FALSE"; my $lsnrctl = &getLsnrctl($tns); if($lsnrctl ne "") { $lsnrexec = $lsnrctl; $reset_env = "TRUE"; } #A temp solution for hanging: redirect output to a temp file (my $fh, my $filename) = &create_temp_file(); if($NT) { $filename = "$TEMP\\"."dbclone.$$"; } EMD_PERL_DEBUG("db_clone.startLsnr(): Temp pfile: $filename"); ## If status is bad, means it's not running my($cmd) = "$lsnrexec status $lname"; EMD_PERL_DEBUG("db_clone.startLsnr(): Command: $cmd"); my(@res) = `$cmd >$filename 2>&1`; my $mesg = getOutputFromFile($filename); print STDOUT $mesg; if(!$?){ EMD_PERL_DEBUG("db_clone.startLsnr(): listener $lname currently running"); $cmd = "$lsnrexec reload $lname"; EMD_PERL_DEBUG("db_clone.startLsnr(): Command: $cmd"); @res = `$cmd >$filename 2>&1`; $mesg = getOutputFromFile($filename); print STDOUT $mesg; #if(system($cmd)){ if($?){ EMD_PERL_DEBUG("db_clone.startLsnr(): lsnrctl reload failed"); exit(1); } close $fh; if($NT) { &removeFile($filename); } #set env back since getLsnrctl set it to a new home if($reset_env eq "TRUE") { &set_env($original_oracle_home, $ENV{ORACLE_SID}); } EMD_PERL_DEBUG("db_clone.startLsnr(): listener $lname reloaded"); EMD_PERL_DEBUG("db_clone.startLsnr(): *** END ***"); return; } ## Can't try the start too soon on NT if($NT){ sleep 5; } $cmd = "$lsnrexec start $lname"; EMD_PERL_DEBUG("db_clone.startLsnr(): Command: $cmd"); @res = `$cmd >$filename 2>&1`; $mesg = getOutputFromFile($filename); print STDOUT $mesg; #system($cmd); if($?){ EMD_PERL_DEBUG("db_clone.startLsnr(): ${cmd} failed: $!"); exit(1); } if($NT) { my $lsnrService = getLsnrService($mesg, $lname); if($lsnrService ne "") { $cmd = $ENV{ComSpec}." /c"; $cmd = "$cmd sc config ".${lsnrService}." start= auto"; EMD_PERL_DEBUG("db_clone.startLsnr(): config service: $cmd"); @res = `$cmd >$filename 2>&1`; EMD_PERL_DEBUG("db_clone.startLsnr(): config service output: @res"); $mesg = getOutputFromFile($filename); print STDOUT $mesg; } } close $fh; if($NT) { &removeFile($filename); } #set env back since getLsnrctl set it to a new home if($reset_env eq "TRUE") { &set_env($original_oracle_home, $ENV{ORACLE_SID}); } EMD_PERL_DEBUG("db_clone.startLsnr(): listener $lname started"); EMD_PERL_DEBUG("db_clone.startLsnr(): *** END ***"); } # Return "/abc/../bin/lsnrctl" if the given file path is "/abc/../network/admin/listener.ora" # getLsnrctl(tns) sub getLsnrctl { my ($tns) = @_; my $lsnrctl = ""; my $exe = ""; my $levelOneDirName = ""; my $levelTwoDirName = ""; my $levelOneDirPath = ""; my $levelTwoDirPath = ""; $levelOneDirName = &getBasename($tns); $levelOneDirPath = &getDirname($tns); if((uc $levelOneDirName) ne (uc "admin")) { EMD_PERL_DEBUG("db_clone.getLsnrctl(): $levelOneDirName is not 'admin'"); return $lsnrctl; } $levelTwoDirName = &getBasename($levelOneDirPath); $levelTwoDirPath = &getDirname($levelOneDirPath); if((uc $levelTwoDirName) ne (uc "network")) { EMD_PERL_DEBUG("db_clone.getLsnrctl(): $levelTwoDirName is not 'network'"); return $lsnrctl; } if($NT) { $exe = ".exe"; } $lsnrctl = "${levelTwoDirPath}${S}bin${S}lsnrctl${exe}"; if(! -e "$lsnrctl") { EMD_PERL_DEBUG("db_clone.getLsnrctl(): $lsnrctl does not exist!"); return ""; } &set_env($levelTwoDirPath, $ENV{ORACLE_SID}); EMD_PERL_DEBUG("db_clone.getLsnrctl(): lsrnctl is: $lsnrctl"); return $lsnrctl; } # Get listener service name by parsing the "lsnrctl start" output # getLsnrService(mesg, lname) sub getLsnrService { my $mesg = $_[0]; my $lname = $_[1]; EMD_PERL_DEBUG("db_clone.getLsnrService(): lsnrctl start output: $mesg"); EMD_PERL_DEBUG("db_clone.getLsnrService(): listener name: $lname"); if(uc($lname) eq "LISTENER") { $lname = ""; } my $firstPart = ""; my $startPos = index($mesg, $firstPart); my $endPos = index($mesg, $secondPart); if(($startPos < 0) || ($endPos < 0)) { EMD_PERL_DEBUG("db_clone.getLsnrService(): startPos: $startPos"); EMD_PERL_DEBUG("db_clone.getLsnrService(): endPos: $endPos"); return ""; } $firstPart = substr($mesg, $startPos + 1, ($endPos - $startPos - 1)); $secondPart = substr($secondPart, 0, (length($secondPart) -1)); my $lsnrService = ${firstPart}.${secondPart}; EMD_PERL_DEBUG("db_clone.getLsnrService(): listener service name: $lsnrService"); return $lsnrService; } # Run given ftp script on a local machine # The caller is responsible to close the returned fileHandle # runFTP(remoteHost, ftpScript, hideOutput) will hide standard output for any # defined parameter "hideOutput" # runFTP(remoteHost, ftpScript) will print standard output sub runFTP { EMD_PERL_DEBUG("db_clone.runFTP(): *** START ***"); my $ftp; if($OS eq "SOL2") { $ftp = '/bin/ftp'; } elsif($NT) { $ftp = 'ftp'; } else { $ftp = '/usr/bin/ftp'; } my ($remoteHost, $ftp_string) = @_; my $ftp_string_debug = &filterHostCredentials($ftp_string); EMD_PERL_DEBUG("db_clone.runFTP(): FTP:\n$ftp_string_debug"); EMD_PERL_DEBUG("db_clone.runFTP(): start creating temp file"); (my $fh, my $filename) = &create_temp_file(); if($NT) { $filename = "$TEMP\\"."dbclone.$$"; } EMD_PERL_DEBUG("db_clone.runFTP(): Start ftp ..."); open(FTP_SCRIPT, "|$ftp -n $remoteHost >$filename") || die "Cannot open pipe for FTP_SCRIPT"; print FTP_SCRIPT $ftp_string; close FTP_SCRIPT || die "Bad FTP_SCRIPT"; #Open the temp file to print output to standard output and debug trace file open (OUT_PUT, "$filename") || die "Unable to open tempfile for OUT_PUT\n"; my @output_content = ; my $output_string = "@output_content"; close OUT_PUT; if(!defined(@_[2])) { print STDOUT $output_string; } EMD_PERL_DEBUG("db_clone.runFTP(): OUT_PUT:\n$output_string"); #&parseOutput($output_string); EMD_PERL_DEBUG("db_clone.runFTP(): *** END ***"); return ($fh, $filename); } # Filter out host credentials from ftp script, which is written to the trace file # filterHostCredentials(ftpScript) sub filterHostCredentials { my ($ftpScript) = @_; my $position1 = index($ftpScript, "user"); my $position2 = index($ftpScript, "\n"); my $replacedLength = $position2 - $position1 - 5; substr($ftpScript, $position1 + 5, $replacedLength) = "username/password(Hiden intentionally)"; return $ftpScript; } # Get multiple files from a remote machine to the local directory. # Login to the remote machine, then cd to the directory of a specified file, # then do a remote get of that file. # ftpGet(remoteHost, username, password, remoteFileNames, localFileNames, formatTypes) sub ftpGet { EMD_PERL_DEBUG("db_clone.ftpGet(): *** START ***"); my ($remoteHost, $username, $password, $remoteFileNames, $localFileNames, $formatTypes) = @_; my @remoteFiles = split /$DELIMITER/, $remoteFileNames; my @localFiles = split /$DELIMITER/, $localFileNames; my @formats = split /$DELIMITER/, $formatTypes; my $ftp = 'ftp'; my $lcd = 'lcd'; my $cd = 'cd'; my $get = 'get'; my $remoteFile; my $localFile; my $remoteDirName; my $localDirName; my $format; my $index = 0; my $ftp_string = ""; #quote password to handle spaces within it $ftp_string .= "user $username \"$password\"\n"; foreach $remoteFile (@remoteFiles) { $remoteDirName = &getDirname($remoteFile); if($NT) { #the virtue directory name is hard-configured as a top dir "backup", #which is mapped to the source working directory (not including the dynamically #generated sub dir name #for testing ftp, which does not have sub dir created yet, this should be called #so, we should pass in one more defined argument in testing ftp connection case. if(defined($_[7])) { $remoteDirName = "/backup"; } #this should be called in clone job since we have a sub dir generated dynamically else { $remoteDirName = &getBasename($remoteDirName); #get the sub dir name $remoteDirName = "/backup/$remoteDirName"; } } $ftp_string .= "$cd $remoteDirName\n"; $localFile = $localFiles[$index]; $localDirName = &getDirname($localFile); $ftp_string .= "$lcd $localDirName\n"; $format = $formats[$index]; $ftp_string .= "$format\n"; $remoteFile = &getBasename($remoteFile); $localFile = &getBasename($localFile); $ftp_string .= "$get $remoteFile $localFile\n"; $index ++; } $ftp_string .= "close\n"; $ftp_string .= "bye\n"; (my $fh, my $filename) = &runFTP($remoteHost, $ftp_string, @_[6]); close $fh; if($NT) { &removeFile($filename); } EMD_PERL_DEBUG("db_clone.ftpGet(): *** END ***"); } # Copy init parameter file from OH/dbs to admin/pfile. # copyInitFile(srcfile, destfile, spfile) sub copyInitFile { EMD_PERL_DEBUG("db_clone.copyInitFile(): *** START ***"); my($srcfile, $destfile, $spfile) = @_; my $new_srcfile = $srcfile; my $new_destfile = $destfile; if(-e $srcfile) { ## Do not remove original init.ora file for Data Guard if(($spfile eq "Y") && ($clonePurpose ne "STANDBY_NO_RECOVERY") && ($clonePurpose !~ /LOGICAL_STANDBY/)) { $new_srcfile = $srcfile.".saved"; $new_destfile = $destfile.".saved"; !copyFile($srcfile, $new_srcfile) || (EMD_PERL_DEBUG("db_clone.copyInitFile(): Error copying $srcfile to $new_srcfile") && (die "db_clone.copyInitFile(): Error copying $srcfile to $new_srcfile")); removeFile($srcfile); } !copyFile($new_srcfile, $new_destfile) || (EMD_PERL_DEBUG("db_clone.copyInitFile(): Error copying $new_srcfile to $new_destfile") && (die "db_clone.copyInitFile(): Error copying $new_srcfile to $new_destfile")); } else { EMD_PERL_DEBUG("db_clone.copyInitFile(): File $srcfile does not exist!"); die "db_clone.copyInitFile(): File $srcfile does not exist!"; } if($clonePurpose =~ /STANDBY/) { EMD_PERL_DEBUG("db_clone.copyInitFile(): Data Guard: returning"); EMD_PERL_DEBUG("db_clone.copyInitFile(): *** END ***"); return; } #remove OH/dbs/init.ora and create a link to admin/pfile/init.ora if($OS ne "NT") { removeFile($new_srcfile); symlink $new_destfile, $new_srcfile or warn "db_clone.copyInitFile(): Can not symlink $new_destfile to $new_srcfile: $!"; } if($NT) { if (-w "$new_srcfile") { EMD_PERL_DEBUG("db_clone.copyInitFile(): Add ifile entry to $new_srcfile"); open(INIT_FILE, ">>$new_srcfile") || die "Cannot open $new_srcfile"; #Append ifile entry to avoid duplication print INIT_FILE "ifile = $new_destfile\n"; close INIT_FILE || die "Cannot close $new_srcfile"; } else { EMD_PERL_ERROR("db_clone.copyInitFile(): NO write permission to file $new_srcfile"); } } EMD_PERL_DEBUG("db_clone.copyInitFile(): *** END ***"); } # Add datafiles for testing purpose # Call &set_env($oracleHome, $oracleSid) before calling this method. # addDatafilesForTesting(fileNamePattern, tablespace, size, startSuffix, endSuffix) sub addDatafilesForTesting { EMD_PERL_DEBUG("db_clone.addDatafilesForTesting(): *** START ***"); my ($fileNamePattern, $tablespace, $size, $startSuffix, $endSuffix) = @_; #add multiple datafiles my $sql_string = ""; my $index = $startSuffix; my $fileName = ""; while ($index <= $endSuffix) { $fileName = "$fileNamePattern"."$index"; $sql_string .= "ALTER TABLESPACE \"$tablespace\" \n"; $sql_string .= "ADD DATAFILE '$fileName' \n"; $sql_string .= "SIZE $size REUSE;\n"; $index ++; } $sql_string .= "EXIT;\n"; (my $fh, my $filename) = &runSqlOnDestination($sql_string); close $fh; if($NT) { &removeFile($filename); } EMD_PERL_DEBUG("db_clone.addDatafilesForTesting(): *** END ***"); } # Get output from file # getOutputFromFile(filename) sub getOutputFromFile { EMD_PERL_DEBUG("db_clone.getOutputFromFile(): *** START ***"); my ($filename) = @_; #Open the file to get output open (OUT_PUT, "$filename") || die "Unable to open file $filename for OUT_PUT\n"; my @output_content = ; my $mesg = "@output_content"; close OUT_PUT; EMD_PERL_DEBUG("db_clone.getOutputFromFile(): *** END ***"); return $mesg; } # compareVer compares 2 version numbers (v1 and v2) # The match compares v1 to the number of significant # digits in v2; for example, assume the v1 is 8.0.2.0.1: # if v2 = 8.0 returns 0 # if v2 = 8.0.2 returns 0 # if v2 = 8.0.1 returns 1 # if v2 = 8.0.2.1 returns -1 # compareVer(version1, version2) sub compareVer { EMD_PERL_DEBUG("db_clone.compareVer(): *** START ***"); my ($version1, $version2) = @_; EMD_PERL_DEBUG("db_clone.compareVer(): version1: $version1"); EMD_PERL_DEBUG("db_clone.compareVer(): version2: $version2"); my $compareValue = 0; my $separator = '\.'; my @version1s = split /$separator/, $version1; my @version2s = split /$separator/, $version2; my ($v1, $v2); my $index = 0; while(($#version1s >= $index) && ($#version2s >= $index)) { $v1 = $version1s[$index]; $v2 = $version2s[$index]; if($v1 < $v2) { $compareValue = -1; last; } elsif ($v1 > $v2) { $compareValue = 1; last; } $index ++; } EMD_PERL_DEBUG("db_clone.compareVer(): compareValue: $compareValue"); EMD_PERL_DEBUG("db_clone.compareVer(): *** END ***"); return $compareValue; } 1; #Tests #removed main_dbclone