# # dgcreate_o.pl # # Copyright (c) 2004, 2009, Oracle and/or its affiliates.All rights reserved. # # NAME # dgcreate_10_2.pl # # DESCRIPTION # Data Guard physical/logical post-DBClone standby creation # for 10.2 OMS # # NOTES # This file is used by the 10.2 OMS for DG create standby jobs. # It is streamed to the Agent from the OMS side, and cannot be # used alone, as it calls some routines in db_clone_10_2.pl. Since # it does not include this file, it is bundled with db_clone_10_2.pl # during the streaming process. Therefore, it cannot be compiled # by itself from the command line. # # MODIFIED (MM/DD/YY) # ngade 02/17/09 - Code slap 10.2.0.5.0 -> 11.2 round 2 # ngade 08/03/08 - Code Slap 10.2.0.5GC -> 11.2SA # sjconnol 10/20/08 - Support for no-broker creation # rimmidi 05/04/08 - Code slap from 11GC to 10.2.0.5 # rimmidi 06/11/07 - Password File Modification # sjconnol 03/21/07 - Backport sjconnol_bug5699797a from main # sjconnol 02/13/07 - # sjconnol 10/20/07 - Bug 6516731 # ngade 11/03/06 - RMAN backup support # sjconnol 11/02/06 - Bug 5624530 # ngade 09/19/06 - fix nls bug 5250491 # sjconnol 07/11/06 - Bug 5352565 # sjconnol 05/02/06 - Bug 5162803 # sjconnol 09/28/05 - Bug 4636417, 4652338 # sjconnol 09/14/05 - Bug 4607258 # sxzhu 09/06/05 - ASM spfile support # sjconnol 08/29/05 - Bug 4562587 # sjconnol 07/31/05 - Bug 4520686 # sjconnol 07/05/05 - Bug 4474555 # gallison 06/28/05 - Fix bug 4342419 # sjconnol 06/17/05 - Bug 4439779: revert to dgutil.pl # sjconnol 06/03/05 - Bug 4407481: include dgutil_core.pl # sjconnol 05/23/05 - Bug 4380906 # sjconnol 03/15/05 - Bug 4225517 # sjconnol 01/26/05 - Bug 4058815: support for ctrl file on raw # sjconnol 11/21/04 - Fix tempfile creation for OMF # sjconnol 11/12/04 - Stream rman.pl from OMS # sjconnol 09/29/04 - Cleanup old controlfile # sjconnol 09/08/04 - Incorporate all code from dgcreate.pl # sjconnol 08/10/04 - OMF support # sjconnol 08/02/04 - Creation # require "flush.pl"; require "$ENV{EMDROOT}/sysman/admin/scripts/db/dg/dgutil.pl"; use strict; #use warnings; use Encode; use vars qw/ $DGstbyLDA $DGprmyLDA $DGInstance $DGOracleHome $DGdevMode $DGsiteID $DGresName $DGdisplayName $DGdbObjID $DGautoDelete $DGuseBroker $DGcreateSnapshot $DGisLogical $DGdbVer $DGIfile $DGIfileForMV $DGIfiletmp1 $DGIfiletmp2 $DGis10gOrHigher $DGis102OrHigher $DGis11gOrHigher $DELIMITER $DGstbyUser $DGstbyPW $DGParam1 $DGstbyTNS $brokerConnDesc $DGpitSCN @OFFLINE_FILES $NT $S $MV $TEMP $target_home $target_sid $db_10_or_higher @SQLCMDS $rman_script $DGuseDuplicate $DGuseRMANBackup $DGConvertToLogical $db_username $rcvcat_username $rcvcat_connect_string $dbid $db_role $rman_command_id $is_repos_database $rin $rout $DGRecover /; #$ENV{EMAGENT_PERL_TRACE_LEVEL} = 0; #DEBUG level. $SIG{__DIE__} = \&handleError; $SIG{__WARN__} = \&handleCreateWarning; ## Handle DBI sql warnings sub handleCreateWarning{ debug("dgutil.handleCreateWarning()"); if(defined($DBI::errstr)){ ## Special handling for ORA-00283 debug("SQL Warning: $DBI::errstr"); if($DBI::errstr =~ /ORA-00283/){ debug("dgutil.handleCreateWarning(): Re-running recovery"); $DGRecover = 1; } } else{ debug("$_[0]"); } } sub DGcreateStandby_o{ debug("dgcreate.DGcreateStandby_o: *** START ***"); my($instance, $ohome, $lfile, $initfile, $islogical, $fromDatafiles, $toDatafiles, $fromLogfiles, $toLogfiles, $logGroups, $logsizes, $tempNames, $tablespaces, $sizes, $autoextends, $dgopts, $newdbname, $controlfile, $toControlFiles, $maxinfo, $isOMF, $isASM, $spfileName, $conndesc, $use_rman_backup, $use_duplicate) = @_; ## environment setup DGcreateAdmin($instance, $ohome, $lfile, $initfile, $islogical, $conndesc, $dgopts, $use_duplicate, $use_rman_backup); DGsetStbyCreds(); ## Remove old SPFILE DGrmInitFiles(); ## For 9i and non-OMF 10g, run the old procedure after installing ## 2nd controlfile if(!$DGis10gOrHigher || (($isOMF ne "Y") && ($isASM ne "Y"))){ ## Must substitute 2nd controlfile, created after db backup DGshutdownStby(); ## Bug 4058815: Always restore ctrl file to ultimate location debug("dgcreate.DGcreateStandby_o: Changing init file control_files param to $toControlFiles"); # Add ultimate ctrl file name to real init file # (Bug 4652338: handle multiple controlfiles) commentInitFileEntry("control_files", $DGIfile); addControlFileToInitFile("$toControlFiles", $DGIfile); # Restore ctrl file to ultimate location DGstartupStbyNomount(); if($DGuseDuplicate eq "N"){ restoreBackupControlFile("${controlfile}tmp"); unlink("${controlfile}tmp"); unlink("${controlfile}"); } DGshutdownStby(); DGstartupStby(); DGcreateStandby($instance, $ohome, $lfile, $initfile, $islogical, $fromDatafiles, $toDatafiles, $fromLogfiles, $toLogfiles, $logGroups, $tempNames, $tablespaces, $sizes, $autoextends, $dgopts, $newdbname, $isOMF, $isASM); } ## For 10g OMF, run the new procedure else{ ## For 10g, accomodate OMF by harvesting names from original ctrl file, ## and then using these names for the rename. debug("dgcreate.DGcreateStandby_o: Getting names from original ctrl file"); my($ids, $datafiles) = getRestoredDatafiles(); $toDatafiles = $datafiles; ## Duplicate: no datafiles restored, so add dummy entries my $realCtlFile = "${controlfile}tmp"; if($DGuseDuplicate eq "Y"){ debug("dgcreate.DGcreateStandby_o: Duplicate: no datafiles listed"); ## Use the 1st datafile as the dummy entry; just need to list a file $toDatafiles = DGgetDatafile(); $realCtlFile = DGgetCtlfile(); } ## Must substitute 2nd controlfile, created after db backup. Needs ## to be converted to OMF. Create dummy file to harvest OMF name, ## update init file, then copy the real ctrl file to OMF loc. ## Temporarily modify the db_name param in the init file; use of ## primary db_name will cause ctrl file creation to fail if done ## on same host DGshutdownStby(); # Remove control_file entry from real init file # (so create controlfile uses OMF) commentInitFileEntry("control_files", $DGIfile); # Create temp init file with original db_name copyFile("$DGIfile", "${DGIfile}tmp"); # Modify orig init file with temporary db_name modifyInitFile("db_name", "temp", $DGIfile); # Bug 4607258; remove pwfile entry from temporary file commentInitFileEntry("remote_login_passwordfile", $DGIfile); # Create the dummy controlfile using init file with temp db_name createControlFile_10_2("temp", $toDatafiles, $logGroups, $toLogfiles, $logsizes, $maxinfo, $DGIfile, "N", "Y", "N"); # Add OMF ctrl file name to real init file addControlFileNamesInInitFile("${DGIfile}tmp", "N"); # Substitute real (containing new OMF ctl file name) init file for orig DGshutdownStby(); copyFile("${DGIfile}tmp", "$DGIfile"); removeFile("${DGIfile}tmp"); # Restart db, restore real controlfile to OMF location DGstartupStbyNomount(); restoreBackupControlFile("$realCtlFile"); unlink("$realCtlFile"); unlink("${controlfile}"); # Restart db with correct OMF ctrl file DGshutdownStby(); DGstartupStby(); if($DGisLogical){ DGcreateLogical10iOMF($fromDatafiles, $toDatafiles, $fromLogfiles, $toLogfiles, $logGroups, $tempNames, $tablespaces, $sizes, $autoextends, $newdbname, $isASM, $spfileName); } else{ DGcreatePhysicalOMF($fromDatafiles, $toDatafiles, $fromLogfiles, $toLogfiles, $logGroups, $tempNames, $tablespaces, $sizes, $autoextends, $isASM, $spfileName); } ## Add the resource and enable if($DGis102OrHigher){ DGaddResource102($isOMF, $isASM, $tempNames, $tablespaces, $sizes, $autoextends, $newdbname); } else{ DGaddResource($isOMF, $isASM, $tempNames, $tablespaces, $sizes, $autoextends, $newdbname); } DGdisconnect(); } debug("dgcreate.DGcreateStandby_o: *** END ***"); } sub DGcreatePhysicalOMF{ debug("dgcreate.DGcreatePhysicalOMF: *** START ***"); my($fromDatafiles, $toDatafiles, $fromLogfiles, $toLogfiles, $logGroups, $tempNames, $tablespaces, $sizes, $autoextends, $isASM, $spfileName) = @_; ##init.ora files for physical DGputIfile(); ## bounce standby DGbounceStby(); ## Rename files DGrenameFiles($fromDatafiles, $toDatafiles); ## Add new OMF logfiles addLogfilesOmf(); #DGmigrateOnlineLogs(); #DGmigrateStandbyLogs(); ## Harvest the recovery SCN DGgetRecoverySCN(); ## Recover the standby if(! $DGis102OrHigher){ DGrecoverStby($tempNames); } ## Move the real init.ora file in place DGmvIfile(); ## Create tempfiles (call dbclone routine) if(! $DGis102OrHigher){ if($isASM eq "Y"){ addTempfilesOmf($tempNames, $tablespaces, $sizes, $autoextends); } else{ addTempfilesOmf("",$tablespaces, $sizes, $autoextends); } } ## Create the SPFILE if($isASM eq "Y"){ $DGstbyLDA->disconnect; copyFile("$DGIfile", "${DGIfile}.saved"); &createSpfile($DGIfile, $spfileName); } else{ DGcreateSpfile(); unlink($DGIfile); } ## bounce standby DGbounceStby(); ## Online offlined datafiles DGonlineDatafiles(); ## Setup autodeletion policy ## (Non-essential operation; do not process return val) DGsetAutoDelete(); debug("dgcreate.DGcreatePhysicalOMF: *** END ***"); } ## 10i Logical creation sub DGcreateLogical10iOMF{ debug("dgcreate.DGcreateLogical10iOMF: *** START ***"); my($fromDatafiles, $toDatafiles, $fromLogfiles, $toLogfiles, $logGroups, $tempNames, $tablespaces, $sizes, $autoextends, $newdbname, $isASM, $spfileName) = @_; ## Modify init.ora files for physical DGputIfile($newdbname); ## bounce standby, startup mount DGbounceStby(); ## Rename files DGrenameFiles($fromDatafiles, $toDatafiles); ## Add new OMF logfiles addLogfilesOmf(); #DGmigrateOnlineLogs(); #DGmigrateStandbyLogs(); ## Harvest the recovery SCN DGgetRecoverySCN(); ## Recover the standby, open resetlogs DGrecoverStby(); ## Create tempfiles (call dbclone routine) if($isASM eq "Y"){ addTempfilesOmf($tempNames, $tablespaces, $sizes, $autoextends); } else{ addTempfilesOmf("",$tablespaces, $sizes, $autoextends); } ## bounce, startup mount DGbounceStby(); ## DB ID/name change steps if(!$DGdevMode){ ## Turn off broker before running nid my($sql) = "alter system set dg_broker_start=false scope=memory"; printDebug("dgcreate.nid: $sql"); my($dbcur) = $DGstbyLDA->prepare($sql); $dbcur->execute; ## Disconnect before running nid $DGstbyLDA->disconnect; ## Run NID to change DB ID/name; 10i NID shuts down database DGnid($newdbname); ## Database is now shutdown; create new password file DGorapwd(); ## Move the real init.ora file in place DGmvIfile(); ## Restart DGstartupStby(); } ## Create the SPFILE if($isASM eq "Y"){ $DGstbyLDA->disconnect; copyFile("$DGIfile", "${DGIfile}.saved"); &createSpfile($DGIfile, $spfileName); } else{ DGcreateSpfile(); unlink($DGIfile); } ## Bounce DGbounceStby(); ## Open resetlogs (only if NID was run) if(!$DGdevMode){ DGopenStby(); } debug("dgcreate.DGcreateLogical10iOMF: *** START ***"); } sub DGsetPrmyCreds{ my ($username, $password, $tns) = @_; my $mode = 2; # SYSDBA if(!$tns){ EMD_PERL_DEBUG("dgcreate.DGsetPrmyCreds: not using connect desc"); $DGprmyLDA = DBI->connect('dbi:Oracle:', "$username", "$password", {ora_session_mode => $mode, PrintError => 0, RaiseError => 1}); } else{ EMD_PERL_DEBUG("dgcreate.DGsetPrmyCreds: using connect desc"); $DGprmyLDA = DBI->connect('dbi:Oracle:', "$username@".$tns , "$password", {ora_session_mode => $mode, PrintError => 0, RaiseError => 1}); } } sub DGsetStbyCredsNoConn{ ($DGstbyUser, $DGstbyPW, $DGstbyTNS) = @_; } sub DGsetStbyCreds{ if(@_){ EMD_PERL_DEBUG("dgcreate.DGsetStbyCreds: connect info passed"); ($DGstbyUser, $DGstbyPW, $DGstbyTNS) = @_; } my $mode = 2; # SYSDBA ## Bug 5699797: Always use full connect desc for Win if($DGstbyTNS && $NT){ EMD_PERL_DEBUG("dgcreate.DGsetStbyCreds: using connect desc"); $DGstbyLDA = DBI->connect('dbi:Oracle:', "$DGstbyUser@".$DGstbyTNS , "$DGstbyPW", {ora_session_mode => $mode, PrintError => 0, RaiseError => 1}); } else{ EMD_PERL_DEBUG("dgcreate.DGsetStbyCreds: not using connect desc"); $DGstbyLDA = DBI->connect('dbi:Oracle:', "$DGstbyUser", "$DGstbyPW", {ora_session_mode => $mode, PrintError => 0, RaiseError => 1}); } } sub DGdisconnect{ debug("dgcreate.DGdisconnect"); $DGprmyLDA->disconnect; $DGstbyLDA->disconnect; } sub DGcreateStandby{ debug("dgcreate.DGcreateStandby"); my($instance, $ohome, $lfile, $initfile, $islogical, $fromDatafiles, $toDatafiles, $fromLogfiles, $toLogfiles, $logGroups, $tempNames, $tablespaces, $sizes, $autoextends, $dgopts, $newdbname, $isOMF, $isASM) = @_; if($DGisLogical){ if($DGis10gOrHigher){ DGcreateLogical10i($fromDatafiles, $toDatafiles, $fromLogfiles, $toLogfiles, $logGroups, $tempNames, $tablespaces, $sizes, $autoextends, $newdbname); } else{ DGcreateLogical9i($fromDatafiles, $toDatafiles, $fromLogfiles, $toLogfiles, $logGroups, $tempNames, $tablespaces, $sizes, $autoextends, $newdbname); } } else{ DGcreatePhysical($fromDatafiles, $toDatafiles, $fromLogfiles, $toLogfiles, $logGroups, $tempNames, $tablespaces, $sizes, $autoextends); } ## Add the resource and enable if($DGis102OrHigher){ DGaddResource102($isOMF, $isASM, $tempNames, $tablespaces, $sizes, $autoextends, $newdbname); } else{ DGaddResource($isOMF, $isASM, $tempNames, $tablespaces, $sizes, $autoextends, $newdbname); } DGdisconnect(); unlink($DGIfile); } sub DGcreatePhysical{ debug("dgcreate.DGcreatePhysical"); my($fromDatafiles, $toDatafiles, $fromLogfiles, $toLogfiles, $logGroups, $tempNames, $tablespaces, $sizes, $autoextends) = @_; ##init.ora files for physical DGputIfile(); ## bounce standby DGbounceStby(); ## Rename files DGrenameFiles($fromDatafiles, $toDatafiles, $fromLogfiles, $toLogfiles, $logGroups); ## Harvest the recovery SCN DGgetRecoverySCN(); ## Recover the standby if(! $DGis102OrHigher){ DGrecoverStby($tempNames); } ## Move the real init.ora file in place DGmvIfile(); ## Create the SPFILE DGcreateSpfile(); ## Create tempfiles if(! $DGis102OrHigher){ DGaddTempfiles($tempNames, $tablespaces, $sizes, $autoextends); } ## bounce standby DGbounceStby(); ## Online offlined datafiles DGonlineDatafiles(); ## Setup autodeletion policy ## (Non-essential operation; do not process return val) DGsetAutoDelete(); } ## 9i Logical creation sub DGcreateLogical9i{ debug("dgcreate.DGcreateLogical9i"); my($fromDatafiles, $toDatafiles, $fromLogfiles, $toLogfiles, $logGroups, $tempNames, $tablespaces, $sizes, $autoextends, $newdbname) = @_; ## Modify init.ora files for physical DGputIfile($newdbname); ## bounce standby, startup mount DGbounceStby(); ## Rename files DGrenameFiles($fromDatafiles, $toDatafiles, $fromLogfiles, $toLogfiles, $logGroups); ## Harvest the recovery SCN DGgetRecoverySCN(); ## Recover the standby, open resetlogs DGrecoverStby(); ## Create tempfiles DGaddTempfiles($tempNames, $tablespaces, $sizes, $autoextends); ## Start logical apply (9i only) DGstartLogical(); ## bounce, startup mount DGbounceStby(); ## DB ID/name change steps if(!$DGdevMode){ ## Run NID to change DB ID/name DGnid($newdbname); ## Move the real init.ora file in place DGmvIfile(); } ## Create the SPFILE DGcreateSpfile(); ## shutdown DGshutdownStby(); if(!$DGdevMode){ ## Create new orapwd file DGorapwd(); } ## startup DGstartupStby(); ## Open resetlogs (only if NID was run) if(!$DGdevMode){ DGopenStby(); } } ## 10i Logical creation sub DGcreateLogical10i{ debug("dgcreate.DGcreateLogical9i"); my($fromDatafiles, $toDatafiles, $fromLogfiles, $toLogfiles, $logGroups, $tempNames, $tablespaces, $sizes, $autoextends, $newdbname) = @_; ## Modify init.ora files for physical DGputIfile($newdbname); ## bounce standby, startup mount DGbounceStby(); ## Rename files DGrenameFiles($fromDatafiles, $toDatafiles, $fromLogfiles, $toLogfiles, $logGroups); ## Harvest the recovery SCN DGgetRecoverySCN(); ## Recover the standby, open resetlogs DGrecoverStby(); ## Create tempfiles DGaddTempfiles($tempNames, $tablespaces, $sizes, $autoextends); ## bounce, startup mount DGbounceStby(); ## DB ID/name change steps if(!$DGdevMode){ ## Turn off broker before running nid my($sql) = "alter system set dg_broker_start=false scope=memory"; printDebug("dgcreate.nid: $sql"); my($dbcur) = $DGstbyLDA->prepare($sql); $dbcur->execute; ## Disconnect before running nid $DGstbyLDA->disconnect; ## Run NID to change DB ID/name; 10i NID shuts down database DGnid($newdbname); ## Database is now shutdown; create new password file DGorapwd(); ## Move the real init.ora file in place DGmvIfile(); ## Restart DGstartupStby(); } ## Create the SPFILE DGcreateSpfile(); ## Bounce DGbounceStby(); ## Open resetlogs (only if NID was run) if(!$DGdevMode){ DGopenStby(); } ## Setup autodeletion policy ## (Non-essential operation; do not process return val) DGsetAutoDelete(); } sub DGgetStbyDest{ my($stbydest); if($DGis10gOrHigher){ my $recarea = get_init_param($DGstbyLDA, "db_recovery_file_dest"); ## If the recovery area is in use, no need to return ## a value; recovery will automatically use it if(defined($recarea) && ($recarea =~ /\S/)){ debug("dgcreate.DGgetStbyDest: using recovery area: $recarea"); return undef; } ## Otherwise, get the value of standby archive dest my $sql = "select destination from v\$archive_dest where dest_name='STANDBY_ARCHIVE_DEST'"; my $dbcur = $DGstbyLDA->prepare($sql); $dbcur->execute; my @row = $dbcur->fetchrow_array(); $stbydest = $row[0]; } else{ $stbydest = get_init_param($DGstbyLDA, "standby_archive_dest"); } debug("dgcreate.DGgetStbyDest: standby_archive_dest: $stbydest"); return $stbydest; } sub DGrecoverStby102{ my($tempfiles) = @_; my($sql, $dbcur, $dbcur2); debug("dgcreate.DGrecoverStby102: tempfiles = $tempfiles"); ## For physical, only do recovery if there are tempfiles if(defined($tempfiles) && ($tempfiles eq "$DELIMITER")){ debug("dgcreate.DGrecoverStby102: no recovery to be done, returning"); return; } if($DGuseDuplicate eq "N"){ ## 10.2: tempfile entries are retained in stby controlfile; ## run drop cmd for as many entries as are known $sql = "alter system set standby_file_management='MANUAL'"; printDebug("dgcreate.DGrecoverStby102: $sql"); $dbcur2 = $DGstbyLDA->prepare($sql); $dbcur2->execute; $sql = "SELECT FILE#, NAME from v\$tempfile"; printDebug("dgcreate.DGrecoverStby102: $sql"); $dbcur = $DGstbyLDA->prepare($sql); $dbcur->execute; my @row; while(@row = $dbcur->fetchrow_array()){ ## Bug 4520686: For OMF, TEMPFILE DROP will attempt to remove ## the underlying OMF file, so always rename the file to a temporary ## name before removing it. my($tempname) = "${TEMP}${S}temp$$"; $sql = "ALTER DATABASE RENAME FILE '$row[1]' to '$tempname'"; printDebug("dgcreate.DGrecoverStby102: $sql"); $dbcur2 = $DGstbyLDA->prepare($sql); $dbcur2->execute; $sql = "ALTER DATABASE TEMPFILE $row[0] DROP"; printDebug("dgcreate.DGrecoverStby102: $sql"); $dbcur2 = $DGstbyLDA->prepare($sql); $dbcur2->execute; } $sql = "alter system set standby_file_management='AUTO'"; printDebug("dgcreate.DGrecoverStby102: $sql"); $dbcur2 = $DGstbyLDA->prepare($sql); $dbcur2->execute; } ## Harvest the recovery SCN DGgetRecoverySCN102(); $sql = "ALTER DATABASE RECOVER MANAGED STANDBY DATABASE UNTIL CHANGE $DGpitSCN"; ## Ignore potential ORA-283 error; just retry $DGstbyLDA->{RaiseError} = 0; $DGstbyLDA->{PrintError} = 1; ## $DGRecover will be reset by signal handler on ORA-283 $DGRecover = 1; while($DGRecover){ $DGRecover = 0; printDebug("dgcreate.DGrecoverStby102: $sql"); $dbcur = $DGstbyLDA->prepare($sql); $dbcur->execute; } ## Reactivate error handling $DGstbyLDA->{RaiseError} = 1; $DGstbyLDA->{PrintError} = 0; if(! ($DGConvertToLogical || $DGisLogical)){ $sql = "ALTER DATABASE OPEN READ ONLY"; printDebug("dgcreate.DGrecoverStby102: $sql"); $dbcur = $DGstbyLDA->prepare($sql); $dbcur->execute; } } sub DGrecoverStby{ my($tempfiles) = @_; my($sql, $dbcur, $dbcur2); debug("dgcreate.DGrecoverStby: tempfiles = $tempfiles"); ## For physical, only do recovery if there are tempfiles if(defined($tempfiles) && ($tempfiles eq "$DELIMITER")){ debug("dgcreate.DGrecoverStby: no recovery to be done, returning"); return; } my $fromclause = ""; my $stbydest = DGgetStbyDest(); ## If using recovery area (which will be true for OMF/ASM), no ## need to specify FROM clause if(defined($stbydest)){ $fromclause = "FROM '$stbydest'"; } $sql = "ALTER DATABASE RECOVER AUTOMATIC $fromclause STANDBY DATABASE UNTIL CHANGE $DGpitSCN"; ## Backup controlfile used only for 9i logical if($DGisLogical && !$DGis10gOrHigher){ $sql = "ALTER DATABASE RECOVER AUTOMATIC FROM '$stbydest' DATABASE UNTIL CHANGE $DGpitSCN USING BACKUP CONTROLFILE"; } ## Only disable exception handling for physical if(!$DGisLogical){ ## Want to ignore all ORA-00278, ORA-00279, ORA-00289, ORA-00280, ## ORA-00310 , ORA-00334 errors during this type of recovery, as they ## are normal ## Turn off all exception handling, turn on logging of errors; ## if there's real error (not one of the ones above), we'll ## catch it in the open $DGstbyLDA->{RaiseError} = 0; $DGstbyLDA->{PrintError} = 1; } printDebug("dgcreate.DGrecoverStby: $sql"); $dbcur = $DGstbyLDA->prepare($sql); $dbcur->execute; if($DGisLogical){ ## Bug 4636417: Always set logical to Max Perf (do for all db versions) $sql = "alter database set standby database to maximize performance"; printDebug("dgcreate.DGrecoverStby: $sql"); $dbcur = $DGstbyLDA->prepare($sql); $dbcur->execute; ## 10i logical: ACTIVATE (not open), bounce, open if($DGis10gOrHigher){ $sql = "alter database activate standby database"; printDebug("dgcreate.DGrecoverStby: $sql"); $dbcur = $DGstbyLDA->prepare($sql); $dbcur->execute; DGbounceStby(); $sql = "ALTER DATABASE OPEN"; printDebug("dgcreate.DGrecoverStby: $sql"); $dbcur = $DGstbyLDA->prepare($sql); $dbcur->execute; } ## 9i logical: Turn on guard prior to open else{ $sql = "ALTER DATABASE GUARD ALL"; printDebug("dgcreate.DGrecoverStby: $sql"); $dbcur = $DGstbyLDA->prepare($sql); $dbcur->execute; $sql = "ALTER DATABASE OPEN RESETLOGS"; printDebug("dgcreate.DGrecoverStby: $sql"); $dbcur = $DGstbyLDA->prepare($sql); $dbcur->execute; } } ## Physical else{ $sql = "ALTER DATABASE RECOVER CANCEL"; printDebug("dgcreate.DGrecoverStby: $sql"); $dbcur = $DGstbyLDA->prepare($sql); $dbcur->execute; ## Turn exception handling back on $DGstbyLDA->{RaiseError} = 1; $DGstbyLDA->{PrintError} = 0; $sql = "ALTER DATABASE OPEN READ ONLY"; printDebug("dgcreate.DGrecoverStby: $sql"); $dbcur = $DGstbyLDA->prepare($sql); $dbcur->execute; } debug("dgcreate.DGrecoverStby: recovery complete to $DGpitSCN"); } sub DGgetDatafile{ my($sql, $dbcur); $sql = "SELECT NAME FROM V\$DATAFILE"; $dbcur = $DGstbyLDA->prepare($sql); printDebug("dgcreate.DGgetDatafile: $sql"); $dbcur->execute; my @row = $dbcur->fetchrow_array(); my $file = $row[0]; debug("dgcreate.DGgetDatafile: $file"); return $file; } sub DGgetCtlfile{ my($sql, $dbcur); $sql = "SELECT NAME FROM V\$CONTROLFILE"; $dbcur = $DGstbyLDA->prepare($sql); printDebug("dgcreate.DGgetCtlfile: $sql"); $dbcur->execute; my @row = $dbcur->fetchrow_array(); my $file = $row[0]; debug("dgcreate.DGgetCtlfile: $file"); return $file; } sub DGgetRecoverySCN102{ my($sql, $dbcur); $sql = "SELECT CHECKPOINT_CHANGE# FROM V\$DATABASE"; $dbcur = $DGprmyLDA->prepare($sql); printDebug("dgcreate.DGgetRecoverySCN102: $sql"); $dbcur->execute; my @row = $dbcur->fetchrow_array(); $DGpitSCN = $row[0]; debug("dgcreate.DGgetRecoverySCN: pitSCN: $DGpitSCN"); } sub DGgetRecoverySCN{ my($sql, $dbcur); ## 10i Logical: Recovery SCN is found on the standby, not the primary if($DGisLogical && $DGis10gOrHigher){ $sql = "SELECT DI2LR_SCN FROM X\$KCCDI2"; $dbcur = $DGstbyLDA->prepare($sql); } ## Otherwise, recovery SCN found on primary else{ if($DGisLogical){ $sql = "SELECT MAX(FIRST_CHANGE#) FROM V\$ARCHIVED_LOG WHERE DICTIONARY_BEGIN = 'YES'"; } else{ $sql = "SELECT MAX(NEXT_CHANGE#) FROM V\$ARCHIVED_LOG"; #$sql = "SELECT CHECKPOINT_CHANGE# FROM V\$DATABASE"; } $dbcur = $DGprmyLDA->prepare($sql); } printDebug("dgcreate.DGgetRecoverySCN: $sql"); $dbcur->execute; my @row = $dbcur->fetchrow_array(); $DGpitSCN = $row[0]; debug("dgcreate.DGgetRecoverySCN: pitSCN: $DGpitSCN"); } sub DGgetPrmyID{ my $sql = "SELECT DBID from v\$database"; printDebug("dgcreate.getPrmyID: $sql"); my $dbcur = $DGprmyLDA->prepare($sql); $dbcur->execute; my @row = $dbcur->fetchrow_array(); my($dbID) = $row[0]; debug("dgcreate.getPrmyID: db ID: $dbID"); return($dbID); } sub DGstartLogical(){ debug("dgcreate.DGstartLogical: *** START ***"); my($dbID) = DGgetPrmyID(); my $sql = "Begin DBMS_LOGSTDBY.PRIMARY_DBID($dbID); End;"; printDebug("dgcreate.DGstartLogical: $sql"); my $dbcur = $DGstbyLDA->prepare($sql); $dbcur->execute; if($DGis10gOrHigher){ $sql = "ALTER DATABASE START LOGICAL STANDBY APPLY"; } else{ $sql = "ALTER DATABASE START LOGICAL STANDBY APPLY INITIAL $DGpitSCN"; } printDebug("dgcreate.DGstartLogical: $sql"); $dbcur = $DGstbyLDA->prepare($sql); $dbcur->execute; $sql = "ALTER DATABASE STOP LOGICAL STANDBY APPLY"; printDebug("dgcreate.DGstartLogical: $sql"); $dbcur = $DGstbyLDA->prepare($sql); $dbcur->execute; debug("dgcreate.startupLogical: *** END ***"); } sub DGonlineDatafiles{ my($file, $dbcur); foreach $file (@OFFLINE_FILES){ my($sql) = "ALTER DATABASE DATAFILE '$file' ONLINE"; printDebug("dgcreate.DGonlineDatafiles: $sql"); $dbcur = $DGstbyLDA->prepare($sql); $dbcur->execute; } } sub DGrenameFiles{ my ($fromDatafiles, $toDatafiles, $fromLogfiles, $toLogfiles, $logGroups) = @_; my(@fromDatafiles) = split /$DELIMITER/, $fromDatafiles; my(@toDatafiles) = split /$DELIMITER/, $toDatafiles; my(@fromLogfiles, @toLogfiles, @logGroups); if($fromLogfiles){ @fromLogfiles = split /$DELIMITER/, $fromLogfiles; } if($toLogfiles){ @toLogfiles = split /$DELIMITER/, $toLogfiles; } if($logGroups){ @logGroups = split /$DELIMITER/, $logGroups; } my($from, $dbcur); debug("dgcreate.DGrenameFiles: *** START ***"); my($count) = 0; ## rename datafiles if($DGuseDuplicate eq 'Y') { debug("dgcreate.DGrenameFiles: Use Duplicate - Skipping rename datafiles"); return; } foreach $from (@fromDatafiles){ my($to) = $toDatafiles[$count]; $count++; ## Skip rename if files are the same name ## (Case insensitive match for NT) if(($from eq $to) || ($NT && (lc($from) eq lc($to)))){ debug("dgcreate.DGrenameFiles: Skipping rename for same name: $from $to"); next; } my $sql = "ALTER DATABASE RENAME FILE '$from' to '$to'"; printDebug("dgcreate.DGrenameFiles: $sql"); $dbcur = $DGstbyLDA->prepare($sql); $dbcur->execute; ## Bug 3736070: If the file is offline on the primary, take it offline ## on the standby so recovery doesn't try to recover it $sql = "SELECT STATUS FROM V\$DATAFILE WHERE NAME = '$from'"; printDebug("dgcreate.DGrenameFiles: $sql"); $dbcur = $DGprmyLDA->prepare($sql); $dbcur->execute; my @row = $dbcur->fetchrow_array(); if($row[0] =~ /OFFLINE|RECOVER/i){ printDebug("dgcreate.DGrenameFiles: datafile $from is offline on primary; taking $to offline on standby"); push(@OFFLINE_FILES, $to); $sql = "ALTER DATABASE DATAFILE '$to' OFFLINE DROP"; printDebug("dgcreate.DGrenameFiles: $sql"); $dbcur = $DGstbyLDA->prepare($sql); $dbcur->execute; } } $count = 0; foreach $from (@fromLogfiles){ my($to) = $toLogfiles[$count]; $count++; ## Skip rename if files are the same name ## (Case insensitive match for NT) if(($from eq $to) || ($NT && (lc($from) eq lc($to)))){ debug("dgcreate.DGrenameFiles: Skipping rename for same name: $from $to"); next; } my $sql = "ALTER DATABASE RENAME FILE '$from' to '$to'"; printDebug("dgcreate.DGrenameFiles: $sql"); $dbcur = $DGstbyLDA->prepare($sql); $dbcur->execute; } debug("dgcreate.DGrenameFiles: Data/log file rename complete"); my($group, %donegroups); foreach $group (@logGroups){ if(defined($donegroups{$group})){ printDebug("dgcreate.DGrenameFiles: already processed group $group"); next; } $donegroups{$group} = 1; my $sql = "ALTER DATABASE CLEAR LOGFILE GROUP $group"; printDebug("dgcreate.DGrenameFiles: $sql"); $dbcur = $DGstbyLDA->prepare($sql); $dbcur->execute; } debug("dgcreate.DGrenameFiles: Log groups created"); } ## Shutdown the standby sub DGshutdownStby { debug("dgcreate.DGshutdownStby: *** START ***"); ## disconnect $DGstbyLDA->disconnect; push(@SQLCMDS, "shutdown immediate"); #executeSQLPlusSYSDBA($DGstbyUser, $DGstbyPW, $DGstbyTNS); executeSQLPlusSYSDBA($DGstbyUser, $DGstbyPW, ''); debug("dgcreate.DGshutdownStby: *** END ***"); undef @SQLCMDS; } ## Shutdown the standby abort sub DGshutdownStbyAbort { debug("dgcreate.DGshutdownStbyAbort: *** START ***"); ## disconnect $DGstbyLDA->disconnect; push(@SQLCMDS, "shutdown abort"); #executeSQLPlusSYSDBA($DGstbyUser, $DGstbyPW, $DGstbyTNS); executeSQLPlusSYSDBA($DGstbyUser, $DGstbyPW, ''); debug("dgcreate.DGshutdownStbyAbort: *** END ***"); undef @SQLCMDS; } ## Open resetlogs (logical only) sub DGopenStby { debug("dgcreate.DGopenStby: *** START ***"); push(@SQLCMDS, "ALTER DATABASE OPEN RESETLOGS"); #executeSQLPlusSYSDBA($DGstbyUser, $DGstbyPW, $DGstbyTNS); executeSQLPlusSYSDBA($DGstbyUser, $DGstbyPW, ''); debug("dgcreate.DGopenStby: *** END ***"); undef @SQLCMDS; } ## Startup the standby ## mount for logical ## nomount for physical sub DGstartupStby { debug("dgcreate.DGstartupStby: *** START ***"); if($DGis10gOrHigher || $DGisLogical){ push(@SQLCMDS, "startup mount"); } else{ push(@SQLCMDS, "startup nomount"); push(@SQLCMDS, "ALTER DATABASE MOUNT STANDBY DATABASE"); } #executeSQLPlusSYSDBA($DGstbyUser, $DGstbyPW, $DGstbyTNS); executeSQLPlusSYSDBA($DGstbyUser, $DGstbyPW, ''); ## Reconnect to standby DGsetStbyCreds(); debug("dgcreate.DGstartupStby: *** END ***"); undef @SQLCMDS; } ## Startup the standby nomount sub DGstartupStbyNomount{ debug("dgcreate.DGstartupStbyNomount: *** START ***"); push(@SQLCMDS, "startup nomount"); executeSQLPlusSYSDBA($DGstbyUser, $DGstbyPW, ''); ## Reconnect to standby DGsetStbyCreds(); debug("dgcreate.DGstartupStbyNomount: *** END ***"); undef @SQLCMDS; } sub DGbounceStby{ DGshutdownStby(); DGstartupStby(); } sub DGcreateSpfile(){ debug("dgcreate.DGcreateSpfile: *** START ***"); my $sql = "CREATE SPFILE FROM PFILE='$DGIfile'"; printDebug("dgcreate.DGcreateSpfile: $sql"); my $dbcur = $DGstbyLDA->prepare($sql); $dbcur->execute; debug("dgcreate.DGcreateSpfile: *** END ***"); } sub DGaddTempfiles{ debug("dgcreate.DGaddTempfiles: *** START ***"); my ($fileNames, $tablespaces, $sizes, $autoextends) = @_; if($fileNames eq "$DELIMITER"){ debug("dgcreate.DGaddTempfiles: NO TEMPFILES TO ADD, returning"); return; } my @tempTablespaces = split /$DELIMITER/, $tablespaces; my @tempFileNames = split /$DELIMITER/, $fileNames; my @tempSizes = split /$DELIMITER/, $sizes; my @tempAutoextends = split /$DELIMITER/, $autoextends; my $tempFileNames; my $index = 0; my($sql, $dbcur); foreach $tempFileNames (@tempFileNames) { $sql = "ALTER TABLESPACE $tempTablespaces[$index] ADD TEMPFILE '$tempFileNames' SIZE $tempSizes[$index] REUSE $tempAutoextends[$index]"; printDebug("dgcreate.DGaddTempfiles: $sql"); $dbcur = $DGstbyLDA->prepare($sql); $dbcur->execute; $index++; } debug("dgcreate.DGaddTempfiles: *** END ***"); } sub get_dbdisplayname { my ($lda, $object_id) = @_; my($indoc); my($outdoc); my($pos); $indoc = ""; $outdoc = get_dg_document($lda, $indoc); $pos = -1; my $retstr; ## First one is value_type= $retstr = get_dg_token($outdoc, "value", $pos); ## Second one is value= $retstr = get_dg_token($outdoc, "value", $pos); ## Need to remove %CREATING% (this does not get translated). $pos = -1; if (($pos = index($retstr, "%CREATING%", $pos)) = 0) { $retstr = substr($retstr, 10, length($retstr)); } debug("dgcreate.get_dbdisplayname: $retstr"); return $retstr; } sub getNewIDs { my $indoc=""; my $outdoc = get_dg_document($DGprmyLDA, $indoc); my $pos = -1; ## First one is object_id= $DGdbObjID = get_dg_token($outdoc, "object_id", $pos); $indoc=""; $outdoc = get_dg_document($DGprmyLDA, $indoc); ## First one is object_id= $pos = -1; $DGsiteID = get_dg_token($outdoc, "object_id", $pos); debug("dgcreate.getNewIDs: DGdbObjID: $DGdbObjID, DGsiteID: $DGsiteID"); } sub _DGremoveResource{ my $indoc = ""; debug("dgcreate._DGremoveRes: $indoc"); DGprocessStatus(get_dg_document($DGprmyLDA, $indoc)); debug("dgcreate._DGremoveRes: Database $DGresName removed"); } sub _DGaddResource{ my $state = $_[0]; my $indoc = ""; debug("dgcreate._DGaddResource: $indoc"); DGprocessStatus(get_dg_document($DGprmyLDA, $indoc)); debug("dgcreate._DGaddResource: Database $DGresName re-added"); } sub _DGfixName{ my $name = $_[0]; my $indoc = ""; debug("dgcreate._DGfixName: $indoc"); DGprocessStatus(get_dg_document($DGprmyLDA, $indoc)); debug("dgcreate._DGfixName: Display name set to $name for DB object"); } sub _DGenable{ my $indoc = ""; debug("dgcreate._DGenable: $indoc"); DGprocessStatus(get_dg_document($DGprmyLDA, $indoc)); debug("dgcreate._DGenable: Resource $DGresName enabled"); } sub _DGdisable{ my $indoc = ""; debug("dgcreate._DGdisable: $indoc"); DGprocessStatus(get_dg_document($DGprmyLDA, $indoc)); debug("dgcreate._DGdisable: $DGsiteID Disabled"); } sub DGaddResource102{ my($isOMF, $isASM, $tempNames, $tablespaces, $sizes, $autoextends, $newdbname) = @_; my $apply_ready = "PHYSICAL-APPLY-READY"; my $state = "PHYSICAL"; ## Start out in physical state my $initstate = "PHYSICAL"; if($DGConvertToLogical || $DGisLogical){ $state = "LOGICAL"; } my($indoc); ## For 10i, first remove the site (and database), then re-add it ## so that the property harvest happens ## Save the original displayname (NLS bug: 4342419) $DGdisplayName = get_dbdisplayname($DGprmyLDA, $DGdbObjID); debug("dgcreate.DGaddResource102: saving original displayname=$DGdisplayName"); ## Remove the resource _DGremoveResource(); ## Add it back _DGaddResource($initstate); ## Always get new site id, database id from database after you add a resource. &getNewIDs; ## set LogShipping OFF. $indoc = ""; debug("dgcreate.DGaddResource102: $indoc"); DGprocessStatus(get_dg_document($DGprmyLDA, $indoc)); debug("dgcreate.DGaddResource102: LogShipping = OFF"); ## Turn off all exception handling on the primary, turn on logging of errors; ## we don't want to error the whole job because of an enable problem $DGprmyLDA->{RaiseError} = 0; $DGprmyLDA->{PrintError} = 1; ## Give broker time to start on the standby waitForDG($DGstbyLDA); ## Enable _DGenable(); ## Fix display name (add CREATING tag so it appears to be still creating in UI) _DGfixName('%CREATING%'.$DGdisplayName); ## set GoOnline to PHYSICAL-APPLY-READY $indoc = ""; debug("dgcreate.DGaddResource102: $indoc"); DGprocessStatus(get_dg_document($DGprmyLDA, $indoc)); debug("dgcreate.DGaddResource102: GoOnline = $apply_ready."); ## set LogShipping ON. $indoc = ""; debug("dgcreate.DGaddResource102: $indoc"); DGprocessStatus(get_dg_document($DGprmyLDA, $indoc)); debug("dgcreate.DGaddResource102: LogShipping = ON"); ## Disable _DGdisable(); ## Do recovery DGrecoverStby102($tempNames); ## 10.2+ logical: convert from physical if($DGConvertToLogical){ ## Build dictionary on primary my $sql = "begin DBMS_LOGSTDBY.BUILD; end;"; printDebug("dgcreate.DGaddResource102: $sql"); my $dbcur = $DGprmyLDA->prepare($sql); $dbcur->execute; ## Recovery to current primary point already done, so this recovery ## will be garaunteed to go to the latest dict build done above $sql = "ALTER DATABASE RECOVER TO LOGICAL STANDBY $newdbname"; printDebug("dgcreate.DGaddResource102: $sql"); $dbcur = $DGstbyLDA->prepare($sql); $dbcur->execute; ## Bounce DGbounceStby(); ## Open resetlogs DGopenStby(); } ## Add temp files (not necessary for duplicate) if($DGuseDuplicate eq "N"){ if(($isOMF ne "Y") && ($isASM ne "Y")) { DGaddTempfiles($tempNames, $tablespaces, $sizes, $autoextends); } else{ if($isASM eq "Y"){ addTempfilesOmf($tempNames, $tablespaces, $sizes, $autoextends); } else{ addTempfilesOmf("",$tablespaces, $sizes, $autoextends); } } } ## Bounce DGbounceStby(); ## Open logical if($DGConvertToLogical){ my $sql = "ALTER DATABASE OPEN"; printDebug("dgcreate.DGrecoverStby: $sql"); my $dbcur = $DGstbyLDA->prepare($sql); $dbcur->execute; } ## Remove the resource _DGremoveResource(); ## Add it back _DGaddResource($state); ## Always get new site id, database id from database after you add a resource. &getNewIDs; ## Give broker time to start on the standby waitForDG($DGstbyLDA); ## Enable _DGenable(); ## Fix up the display name _DGfixName($DGdisplayName); ## Convert to snapshot if specified if($DGcreateSnapshot){ debug("dgcreate.DGaddResource102: Converting to snapshot"); $indoc = ""; debug("dgcreate.DGaddResource102: $indoc"); DGprocessStatus(get_dg_document($DGprmyLDA, $indoc)); debug("dgcreate.DGaddResource102: Conversion to snapshot complete"); } ## Disable broker of no-broker option selected if(!$DGuseBroker){ debug("dgcreate.DGaddResource102: Disabling broker"); $indoc = ""; debug("dgcreate.DGaddResource102: $indoc"); DGprocessStatus(get_dg_document($DGprmyLDA, $indoc)); debug("dgcreate.DGaddResource102: Broker disabled"); my($sql) = "alter system set dg_broker_start=false"; printDebug("dgcreate.DGrecoverStby102: $sql"); my $dbcur = $DGprmyLDA->prepare($sql); $dbcur->execute; $dbcur = $DGstbyLDA->prepare($sql); $dbcur->execute; } } sub DGaddResource{ my($isOMF, $isASM, $tempNames, $tablespaces, $sizes, $autoextends, $newdbname) = @_; my $apply_ready = "PHYSICAL-APPLY-READY"; my $state = "PHYSICAL"; ## Start out in physical state my $initstate = "PHYSICAL"; if($DGis10gOrHigher && ($DGConvertToLogical || $DGisLogical)){ $state = "LOGICAL"; } else{ $state = "PHYSICAL-APPLY-ON"; if($DGisLogical){ $state = "LOGICAL-APPLY-ON"; } } my($indoc); ## For 10i, first remove the site (and database), then re-add it ## so that the property harvest happens if($DGis10gOrHigher){ ## Save the original displayname (NLS bug: 4342419) $DGdisplayName = get_dbdisplayname($DGprmyLDA, $DGdbObjID); debug("dgcreate.DGaddResource: saving original displayname=$DGdisplayName"); ## Remove the resource _DGremoveResource(); ## Add it back _DGaddResource($initstate); ## Always get new site id, database id from database after you add a resource. &getNewIDs; } ## For 9i, add the resource else{ $DGresName = encode("UTF-8", $DGresName); debug("converted to utf8: $DGresName"); $indoc = ""; debug("dgcreate.DGaddResource: $indoc"); DGprocessStatus(get_dg_document($DGprmyLDA, $indoc)); debug("dgcreate.DGaddResource: Resource $DGresName added"); } ## Turn off all exception handling on the primary, turn on logging of errors; ## we don't want to error the whole job because of an enable problem $DGprmyLDA->{RaiseError} = 0; $DGprmyLDA->{PrintError} = 1; ## Give broker time to start on the standby waitForDG($DGstbyLDA); ## Enable _DGenable(); ## For 10i, fix up the display name if($DGis10gOrHigher){ ## Fix display name _DGfixName($DGdisplayName); } } sub DGprocessStatus{ my($status) = @_; my @dbres_status_tokens = split(/[><]+/, $status); # Token 0 is "", token 1 is "RESULT ", token 2 is "MESSAGE " my($dbres_status) = $dbres_status_tokens[3]; my @temp_tokens = split(/\s+/, $dbres_status); $dbres_status = $temp_tokens[0]; EMD_PERL_DEBUG("dbres_status=$dbres_status."); if ($dbres_status eq "SUCCESS") { debug("dgcreate.DGprocessStatus: DG operation successful"); return; } else { # Parse out the error from the big status. my $dbres_status_token; my $token_count = 0; my($dbres_status_text); foreach $dbres_status_token (@dbres_status_tokens) { # EMD_PERL_DEBUG("dbres_status_token=$dbres_status_token."); if ($dbres_status_token eq "ERROR_TEXT ") { $dbres_status_text = $dbres_status_tokens[$token_count + 1]; last; } $token_count++; } if ($dbres_status eq "WARNING"){ debug("dgcreate.DGprocessStatus: DG operation produced warning: $dbres_status_text"); } elsif ($dbres_status eq "FAILURE"){ die("$dbres_status_text"); } } } ########################################################## ## Setup for standby creation ########################################################## sub DGcreateAdmin{ debug("dgcreate.DGcreateAdmin: @_"); ## globals my($inst, $ohome, $listenerfile, $initfile, $clonetype, $conndesc, $dgopts, $use_duplicate, $use_rman_backup) = @_; $DGInstance = $inst; $DGOracleHome = $ohome; ## Used for broker add site connect string (initial connect desc) $brokerConnDesc = $conndesc; ## ensure global clonePurpose is set in db_clone_10_2 setClonePurpose($clonetype); ## Harvest DG options ## 1) dev mode ## 2) site ID ## 3) DB resource name my(@dgopts) = split(/$DELIMITER/, $dgopts); # SJC 9/18/03: Disable development mode #$DGdevMode = $dgopts[0]; $DGdevMode = 0; $DGsiteID = $dgopts[1]; $DGresName = $dgopts[2]; ## displayName is 10i only $DGdisplayName = $dgopts[3]; ## dbObjID is 10i only; distinct from siteID $DGdbObjID = $dgopts[4]; ## autoDelete is 10i only; determines if rec area uses auto deletion $DGautoDelete = $dgopts[5]; debug("dgcreate.DGcreateAdmin: Auto delete = $DGautoDelete"); ## Create logical by converting phys (10.2 and up) $DGConvertToLogical = $dgopts[6]; if($DGConvertToLogical){ debug("dgcreate.DGcreateAdmin: Logical Standby 10.2+"); } ## Disable broker after creation (10.2 and up) $DGuseBroker = $dgopts[7]; ## Convert to snapshot after creation (11.1 and up) $DGcreateSnapshot = $dgopts[8]; ## Use RMAN duplicate (11g feature) $DGuseDuplicate = $use_duplicate; ## Use RMAN existing backup (11g feature) $DGuseRMANBackup = $use_rman_backup; if($DGdevMode){ debug("dgcreate.DGcreateAdmin: Development mode"); } elsif(-e "/tmp/dgNoNewID"){ debug("dgcreate.DGcreateAdmin: No NID mode"); $DGdevMode = 1; } $DGisLogical = 0; if($clonetype =~ /LOGICAL_STANDBY/){ debug("dgcreate.DGcreateAdmin: Logical Standby"); $DGisLogical = 1; } else{ debug("dgcreate.DGcreateAdmin: Physical Standby"); } debug("dgcreate.DGcreateAdmin: site ID: $DGsiteID"); debug("dgcreate.DGcreateAdmin: resource name: $DGresName"); ## Figure out DB Version (primary and standby will be same) my $dbver = get_dbversion($DGprmyLDA); if($dbver =~ /^1[1-9]/){ debug("dgcreate.DGcreateAdmin: Database version: 11gR1 (or higher)"); $DGis11gOrHigher = 1; $DGis102OrHigher = 1; $DGis10gOrHigher = 1; $DGdbVer = "11.1"; } elsif($dbver =~ /^102/){ debug("dgcreate.DGcreateAdmin: Database version: 10gR2"); $DGis11gOrHigher = 0; $DGis10gOrHigher = 1; $DGis102OrHigher = 1; $DGdbVer = "10.2"; } elsif($dbver =~ /^(101|100)/){ debug("dgcreate.DGcreateAdmin: Database version: 10gR1"); $DGis11gOrHigher = 0; $DGis102OrHigher = 0; $DGis10gOrHigher = 1; $DGdbVer = "10.1"; } else{ debug("dgcreate.DGcreateAdmin: Database version: 9iR2"); $DGis11gOrHigher = 0; $DGis102OrHigher = 0; $DGis10gOrHigher = 0; $DGdbVer = "9.2"; } ## Environmentals ## These should already have been reset from the in-job Perl debug("ORACLE_HOME = $ENV{ORACLE_HOME}"); debug("PATH = $ENV{PATH}"); ## Since the listener.ora file may not be the default one, ## set TNS_ADMIN to whatever directory the file is in if(defined($listenerfile) && $listenerfile){ my($idx) = rindex($listenerfile,"$S"); if($idx == -1){ die("ERROR_TNS_ADMIN:$listenerfile"); return; } my($tns) = substr($listenerfile,0,$idx); $ENV{TNS_ADMIN} = "$tns"; debug("TNS_ADMIN = $ENV{TNS_ADMIN}"); } if(defined($initfile) && $initfile){ $DGIfile = $initfile; $DGIfileForMV = substr($DGIfile, rindex($DGIfile,"$S") + 1); ## Beware that the value of $DGIfiletmp will be inferred as ## "${Ifile}tmp" by the client, so any changes here must be performed ## in the client code, too. $DGIfiletmp1 = "${DGIfile}tmp1"; $DGIfiletmp2 = "${DGIfile}tmp2"; } } ########################################################## ## Move the "real" init.ora file to the correct name ########################################################## sub DGmvIfile{ local($SIG{'CHLD'}) = 'DEFAULT'; my($file) = $_[0]; ## default: move temp file #1 to permanent slot if(!defined($file)){ $file = $DGIfiletmp1; } debug("dgcreate.DGmvIfile: $_[0]"); my($cmd) = "$MV $file $DGIfile"; if($NT){ # For NT, first delete the destination file. my($delcmd) = "del $DGIfile"; debug("\t$delcmd"); &DGrunCmd($delcmd); $cmd = "$MV $file $DGIfileForMV"; } debug("$cmd"); if(&DGrunCmd($cmd)){ return(1); } } ########################################################## ## Install a new init.ora file ########################################################## sub DGputIfile{ debug("dgcreate.DGputIfile"); my($db_name) = @_; my($line); ## Create main init.ora file for standby ($DGIfiletmp1). It will actually be ## created under the temporary file name, initially. open(IFILETMP1,">$DGIfiletmp1") || die("ERROR_FILE_OPEN:$DGIfiletmp1"); ## Create temp init.ora ($DGIfiletmp2) file w/o db_file_name_convert, ## log_file_name_convert, or standby_file_management. This is ## so standby can start w/o these params set in order to do the ## file renaming. Also, strip log_archive_format parameter, so ## that if a manual recovery is done by the create process, a ## user specified format doesn't get in the way (log_archive_format ## will be preserved in the real spfile). open(IFILETMP2,">$DGIfiletmp2") || die("ERROR_FILE_OPEN:$DGIfiletmp2"); ## Open the file copied over by DB Clone for reading, from which ## the above two files will be created. open(IFILE,"$DGIfile") || die("ERROR_FILE_OPEN:$DGIfile"); while($line = ){ chomp($line); ## For Logical, substitute the new db_name, so it will match ## the name set by the NID utility if((defined($db_name)) && $line =~ /^\s*(db_name)/i){ debug("\tadding new db_name=$db_name for logical stby"); print IFILETMP1 "db_name=$db_name\n"; ## Keep old db_name in tempfile print IFILETMP2 "$line\n"; next; } ## remove lock_name_space from main file; don't need it after db name change elsif((defined($db_name)) && $line =~ /^\s*lock_name_space/i){ print IFILETMP2 "$line\n"; next; } ## remove db/log_file_name_convert from temp file if($line =~ /^\s*(db_file_name_convert|log_file_name_convert|standby_file_management)/i){ print IFILETMP1 "$line\n"; next; } ## for 11g or higher audit_trail default is set to DB. ## In order to open the database in read only mode, we need to set this parameter to NONE/FALSE. if($line =~ /^\s*(audit_trail)/i){ print IFILETMP1 "audit_trail=NONE\n"; next; } print IFILETMP1 "$line\n"; print IFILETMP2 "$line\n"; } ## Bug 5162803: Set log_file_name_convert to empty string in the temp ## init file that will be used during the file rename. This is necessary ## to avoid ORA-19527 during CLEAR LOGFILE if the "keep primary names" ## option was used to create the standby. ## Bug 5624530: Empty strings cause ORA-09134 on Windows; use a dummy string print IFILETMP2 "log_file_name_convert=\'a\'\,\'a\'\n"; close(IFILE); close(IFILETMP1); close(IFILETMP2); ## Now that we're done producing the two init.ora files, move ## the temp one into place. ## Return with no output if error; mvIfile will output an error messages if(&DGmvIfile("$DGIfiletmp2")){ return; } } ########################################################## ## Create a remote login passwordfile ########################################################## sub DGorapwd{ local($SIG{'CHLD'}) = 'DEFAULT'; debug("dgcreate.DGorapwd"); my($password); my($pwfile) = "${DGOracleHome}${S}dbs${S}orapw${DGInstance}"; my($orapwexec) = "${DGOracleHome}${S}bin${S}orapwd"; if($NT){ $pwfile = "${DGOracleHome}${S}database${S}PWD${DGInstance}.ora"; $orapwexec = "${DGOracleHome}${S}bin${S}orapwd.exe"; } my($pwcmd); if($DGis102OrHigher){ $password = $DGParam1; $pwcmd = "$orapwexec file=$pwfile password=$password entries=10 -ob"; } else{ $password = $DGstbyPW; $pwcmd = "$orapwexec file=$pwfile password=$password entries=10"; } ## If the executable is there, and the file isn't, create it if((-e "$orapwexec") && !(-e "$pwfile")){ if(&DGrunCmd($pwcmd)){ exit(1); } debug("Created file $pwfile"); } ## else, if file is there, remove it and recreate elsif(-e "$pwfile"){ debug("Removing file $pwfile"); if(unlink($pwfile)){ if(&DGrunCmd($pwcmd)){ exit(1); } debug("Created file $pwfile"); } ## If remove fails, that's an error else{ die("ERROR_RM_FILE:$pwfile"); } } ## ## else, there's a problem (probably no orapwd exec) else{ die("ERROR_EXEC:orapwd"); } } ########################################################## ## Run the NID utility to change the database name. (Called ## only to change dbname for a logical standby.) ########################################################## sub DGnid{ local($SIG{'CHLD'}) = 'DEFAULT'; debug("dgcreate.DGnid: $_[0]"); my($dbname) = @_; my($nidexec) = "${DGOracleHome}${S}bin${S}nid"; if($NT){ $nidexec = "${DGOracleHome}${S}bin${S}nid.exe"; } ## If a connect alias isn't specified, NID will connect ## to whatever ORACLE_SID is set to $ENV{ORACLE_SID} = "$DGInstance"; my($cmd) = "echo Y | $nidexec TARGET=/ DBNAME=$dbname"; debug("$cmd"); ## If the executable is there, and the file isn't, create it if(-e "$nidexec"){ if(&DGrunCmd($cmd)){ exit(1); } } else{ die("ERROR_EXEC:nid"); } } ########################################################## ## Remove the SPFILE file for an instance ## ## Since this is run at create time, the globals $DGInstance and ## $DGOracleHome will be set when it's called. ########################################################## sub DGrmInitFiles{ debug("dgcreate.DGrmInitFiles"); my($file) = "${DGOracleHome}${S}dbs${S}spfile${DGInstance}.ora"; if($NT){ $file = "${DGOracleHome}${S}database${S}spfile${DGInstance}.ora"; } debug("\tlooking for $file"); if(-e "$file"){ debug("\tremoving file for for ${DGInstance}: $file"); unlink($file) || die("ERROR_RM_FILE:$file"); } } ########################################################## ## Setup the arch log file auto deletion policy using RMAN ## $DGautoDelete global set in createAdmin ########################################################## sub DGsetAutoDelete{ local($SIG{__WARN__}) = 'DEFAULT'; debug("dgcreate.DGsetAutoDelete"); ## Logical auto delete feature (10.2 only); always set this ## (Actually not necessary since server will do by default, but leave it) if($DGisLogical && ($DGis102OrHigher)){ my $sql = "Begin DBMS_LOGSTDBY.APPLY_SET('LOG_AUTO_DELETE','TRUE'); End;"; printDebug("dgcreate.DGsetAutoDelete: $sql"); my $dbcur = $DGstbyLDA->prepare($sql); $dbcur->execute; } ## Until setting-up rec area deletion policy makes sense for logical, ## skip it. (Not done at all pre-10g.) if($DGisLogical || $DGConvertToLogical || !$DGis10gOrHigher || !$DGautoDelete){ return; } ## Set these globals to avoid compile warnings in rman_o.pl $db_username = ""; $rcvcat_username = ""; $rcvcat_connect_string = ""; $dbid = 0; $db_role = ""; $rman_command_id = ""; $is_repos_database = ""; $rin = ""; $rout = ""; $target_home = $DGOracleHome; $target_sid = $DGInstance; $db_10_or_higher = "YES"; $rman_script = "CONFIGURE ARCHIVELOG DELETION POLICY TO APPLIED ON STANDBY;"; ## Don't want fatal error here to fail the job; trap any errors my($status) = eval{br_rman("NO", "");}; if($@){ debug("dgcreate.DGsetAutoDelete: RMAN script failed: $@"); } debug("dgcreate.DGsetAutoDelete: RMAN returned $status"); } sub DGrunCmd{ local($SIG{'CHLD'}) = 'DEFAULT'; my($cmd) = $_[0]; my(@res) = `$cmd 2>&1`; debug("@res "); if($?){ my($err) = $!; ## If there is no system error, just put out the output if($err eq undef || ($err eq "")){ $err = "@res"; ## To make multiline errors fit on one line (so it ## makes it back to the client) replace all \n with tabs. ## The client will in turn replace the tabs with newlines ## before presenting the error to the user. $err =~ s/\n/\t/g; } chomp($err); ## Send back only the OS components of error to client ## to minimize translation issues if($cmd !~ /password/i){ printDebug("${cmd}: $err"); } return(1); } return(0); } # Migrate online logs to ASM with hot migration approach # Call &set_env($oracleHome, $oracleSid) before calling this method. # migrateOnlineLogsHot() sub DGmigrateOnlineLogs { EMD_PERL_DEBUG("dgcreate_o.DGMigrateOnlineLogs(): *** START ***"); my $sql_string = ""; $sql_string .= "set echo off\n"; $sql_string .= "set serveroutput on;\n"; $sql_string .= "declare\n"; $sql_string .= " cursor logfile_cur is\n"; $sql_string .= " select l.thread#, l.group#, l.bytes\n"; $sql_string .= " from v\$log l;\n"; $sql_string .= " type numTab_t is table of number index by binary_integer;\n"; $sql_string .= " grouplist numTab_t;\n"; $sql_string .= " threadlist numTab_t;\n"; $sql_string .= " byteslist numTab_t;\n"; $sql_string .= " procedure migrateorl ( thread# IN number, group# IN number, bytes IN number) is\n"; $sql_string .= " retry number := 5;\n"; $sql_string .= " stmt varchar2(1024);\n"; $sql_string .= " als varchar2(1024) := 'alter system archive log current';\n"; $sql_string .= " chk varchar2(1024) := 'alter system checkpoint global';\n"; $sql_string .= " begin\n"; $sql_string .= " stmt := 'alter database add logfile thread '||thread#||' size '||bytes;\n"; $sql_string .= " dbms_output.put_line('Execute stmt: ' || stmt);\n"; $sql_string .= " execute immediate stmt;\n"; $sql_string .= " stmt := 'alter database drop logfile group '||group#;\n"; $sql_string .= " for I in 1 .. retry loop\n"; $sql_string .= " begin\n"; $sql_string .= " dbms_output.put_line('Execute stmt: ' || stmt);\n"; $sql_string .= " execute immediate stmt;\n"; $sql_string .= " exit;\n"; $sql_string .= " exception\n"; $sql_string .= " when others then\n"; $sql_string .= " if I > retry then\n"; $sql_string .= " raise;\n"; $sql_string .= " end if;\n"; $sql_string .= " dbms_output.put_line('Execute stmt: ' || als);\n"; $sql_string .= " execute immediate als;\n"; $sql_string .= " dbms_output.put_line('Execute stmt: ' || chk);\n"; $sql_string .= " execute immediate chk;\n"; $sql_string .= " end;\n"; $sql_string .= " end loop;\n"; $sql_string .= " end;\n"; $sql_string .= "begin\n"; $sql_string .= " open logfile_cur;\n"; $sql_string .= " fetch logfile_cur bulk collect into threadlist, grouplist, byteslist;\n"; $sql_string .= " close logfile_cur;\n"; $sql_string .= " for I in 1 .. threadlist.count loop\n"; $sql_string .= " migrateorl (threadlist(I), grouplist(I), byteslist(I) );\n"; $sql_string .= " end loop;\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("dgcreate_o.DGMigrateOnlineLogs(): *** END ***"); } # Migrate online logs to ASM for standby database with hot migration approach # Call &set_env($oracleHome, $oracleSid) before calling this method. # migrateOnlineLogsStandbyHot() sub DGmigrateStandbyLogs { EMD_PERL_DEBUG("dgcreate_o.DGmigrateStandbyLogs(): *** START ***"); my $sql_string = ""; $sql_string .= "set echo off\n"; $sql_string .= "set serveroutput on;\n"; $sql_string .= "declare\n"; $sql_string .= " cursor logfile_cur is\n"; $sql_string .= " select l.thread#, l.group#, l.bytes\n"; $sql_string .= " from v\$standby_log l;\n"; $sql_string .= " type numTab_t is table of number index by binary_integer;\n"; $sql_string .= " grouplist numTab_t;\n"; $sql_string .= " threadlist numTab_t;\n"; $sql_string .= " byteslist numTab_t;\n"; $sql_string .= " procedure migratesrl ( thread# IN number, group# IN number, bytes IN number) is\n"; $sql_string .= " retry number := 5;\n"; $sql_string .= " stmt varchar2(1024);\n"; $sql_string .= " begin\n"; $sql_string .= " if thread# = 0 then\n"; $sql_string .= " stmt := 'alter database add standby logfile size '||bytes;\n"; $sql_string .= " else\n"; $sql_string .= " stmt := 'alter database add standby logfile thread '||thread#||' size '||bytes;\n"; $sql_string .= " end if;\n"; $sql_string .= " dbms_output.put_line('Execute stmt: ' || stmt);\n"; $sql_string .= " execute immediate stmt;\n"; $sql_string .= " stmt := 'alter database drop standby logfile group '||group#;\n"; $sql_string .= " dbms_output.put_line('Execute stmt: ' || stmt);\n"; $sql_string .= " execute immediate stmt;\n"; $sql_string .= " dbms_backup_restore.sleep(30);\n"; $sql_string .= " end;\n"; $sql_string .= "begin\n"; $sql_string .= " open logfile_cur;\n"; $sql_string .= " fetch logfile_cur bulk collect into threadlist, grouplist, byteslist;\n"; $sql_string .= " close logfile_cur;\n"; $sql_string .= " for I in 1 .. threadlist.count loop\n"; $sql_string .= " migratesrl (threadlist(I), grouplist(I), byteslist(I) );\n"; $sql_string .= " end loop;\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("dgcreate_o.DGmigrateStandbyLogs(): *** END ***"); }