#!/usr/bin/perl # ------------------------------------------------------------------------ # coldclone -- create a clone of an existing Oracle Database # G. Patterson, Sep 2002 # Set the common parameters (contained in common.pl) use File::Basename; $MyPath = dirname($0); require "$MyPath\\common.pl"; use Time::Local; # ------------------------------------------------------------------------ # Write a log message to the logfile sub LogMsg{ my @t = localtime(time); printf LOG "%04d-%02d-%02d %02d:%02d %s\n", $t[5]+1900,$t[4]+1,$t[3],$t[2],$t[1],$_[0]; } # ------------------------------------------------------------------------ # We have a problem ... cleanup and exit sub problem{ LogMsg "ERROR: $_[0]"; die "$_[0]\n"; } # ------------------------------------------------------------------------ # Run the given SQL in svrmgrl sub run_svrmgr{ # construct an SQL file open SQL,">$sqltmp" || die "cannot open $sqltmp"; print SQL "connect internal/$passwd\n"; print SQL "$_[0]\n"; print SQL "exit\n"; close SQL || die "$!"; # EXEC as a backticks block and return the output in list context my @t = `$svrmgrl \@$sqltmp`; return( @t); } # ------------------------------------------------------------------------ # database MUST be DOWN! sub chk_nodb{ # Try and run a query ... any query my @t = run_svrmgr 'select * from dual;'; # If we did not get an error something is wrong! die "Cannot $MyName a running database. Shut It Down First!\n" unless (grep (/ORA-01034:/,@t) || grep (/ORA-09352:/,@t) || grep (/ORA-12154:/,@t) || grep (/ORA-12560:/,@t) || grep (/ORA-12535:/,@t) ); } # ------------------------------------------------------------------------ sub chkdir{ return unless $_[0]; problem "Missing Folder: $_[0]" unless (-d $_[0]); } # ------------------------------------------------------------------------ # retrieve parameter values from srcdir\\0header sub read_header{ # This parses the header file, created by coldarch open (HDR,"$srcdir\\0header") || die "Cannot open $srcdir\\0header\n"; while (){ s/\s+$//; s/^\s+//; if (/\w+ started at /){ $start_str = $'; @start = split(' ',$start_str); @start = reverse (split( /-/,$start[0]), split(/:/,$start[1])); $start[4]--; $start[5] -= 1900; } # If the string FILES: is at EOL, previous word is "type" elsif (/(\w+) FILES:$/){ # set up to push to an array $get_data $get_data = lc($1) . "_files"; } # Blank line means another block of data in the header file elsif (/^$/){ undef($get_data); } # Otherwise if there is a colon, it's an Oracle Parmameter elsif (/^(.+): /){ $oraparm{$1} = $'; } # Otherwise we must be gathering data elsif ($get_data){ # NOTE: $get_data is the name of the array ... NOT the array push(@$get_data,$_); } else{ # Umm, Houston ... Houston? print "$_"; problem "problem with 0header"; } } } # ------------------------------------------------------------------------ sub gen_ctrl_sql { # Generate the SQL to create the control files -- put it in @gen_ctrl my $line = 0; my $logf = 0; my $data = 0; my $x; my $y; my $ctl_trace = "$srcdir\\$srcsid" . "_CTRL.FILE.trc"; my $create_str = "CREATE CONTROLFILE REUSE"; open (CTL_TRACE,$ctl_trace) || problem "Cannot read $ctl_trace"; push ( @gen_ctrl, "connect internal/$passwd"); push ( @gen_ctrl, "shutdown"); push ( @gen_ctrl, "startup nomount pfile=$pfile{$orasid}"); while(){ $line++ if ( /^$create_str/); next unless ($line); s/\s*$//; if ( $line++ == 1){ $_ = "$create_str set DATABASE \"$orasid\" RESETLOGS"; } elsif ( /^LOGFILE$/){ $logf++; } elsif ( /^DATAFILE$/){ $data++; $logf = 0; push (@gen_ctrl,$_); push (@gen_ctrl,@data_files_str); next; } elsif ($logf){ if (s/$srcsid/$orasid/ig){ ($x,$logf,$y) = split( /'/, "x $_ y"); push @redo_files,$logf; } } elsif ($data){ $data = 0 unless ( /,$/); next; } push (@gen_ctrl,$_); last if (/^;$/); } close (CTL_TRACE) || problem "Cannot close $ctl_trace"; push (@gen_ctrl,"alter database open resetlogs;"); } # ------------------------------------------------------------------------ sub set_oraparm { my $key; my $data; my $t = $_[0]; ($key,$data) = split( /=/,$t,2); $key =~ s/\s//g; $data =~ s/\s*$//; $data =~ s/^\s*//; if ( $data =~ s/^"location=//i){ $data =~ s/"$//; } $oraparm{lc($key)} = $data; } # ------------------------------------------------------------------------ sub copy_pfile { # Copy the Oracle pfile and ifiles ... substitute SID. # Add to this list, if you want these lines altered ... $P_alter{ifile}++; $P_alter{db_name}++; $P_alter{instance_name}++; $P_alter{service_names}++; $P_alter{control_files}++; $P_alter{log_archive_dest_1} = 2; $P_alter{log_archive_dest} = 2; $P_alter{background_dump_dest} = 2; $P_alter{user_dump_dest} = 2; my $i; my $key; my $srcf = $_[0]; my $tgtf = $_[0]; $tgtf =~ s/$srcsid/$orasid/ig; my $tgtp = dirname($tgtf); my $src = basename($srcf); problem "Missing path: $tgtp" unless (-d $tgtp); my $t = `copy $srcdir\\$src $tgtf`; $t =~ s/\s+$//; problem "Cannot copy $srcdir\\$src to $tgtf ($t)" unless ($t =~ /1 file\(s\) copied/); open (PFILE,$tgtf) || problem "copy_pfile cannot open: $tgtf"; @pfile_data = (); for ( $i = 0; $i <= $#pfile_data; $i++){ $line = $i + 1; next if ( $pfile_data[$i] =~ /^\s*#/); if ( $pfile_data[$i] =~ /=/){ $key = lc((split(' ',$pfile_data[$i]))[0]); $key =~ s/\s*=.*$//; } next if ( $P_skip{$key} ); if ($pfile_data[$i] =~ s/$srcsid/$orasid/ig) { problem "Parameter: $key line $line in $tgtf" unless ($P_alter{$key}); } set_oraparm( $pfile_data[$i] ) if ($P_alter{$key} == 2); } open (PFILE,">$tgtf") || problem "copy_pfile cannot write to: $tgtf"; foreach $x(@pfile_data){ print PFILE $x; } close (PFILE) || problem "Closing $tgtf"; } # ------------------------------------------------------------------------ sub copy_dfile { my $src; my $tgt; my $cmd; my @df; my $t; my $d; my $i; # unlink (@data_files, @ctrl_files, @redo_files); foreach $src(@data_files){ $src =~ s/ .*$//; $d = dirname( $src); $d =~ s/$srcsid/$orasid/ig; push (@df, $tgt = "$d\\" . basename($src)); chdir $d || problem "copy_dfile folder: $d" ; $cmd = "$unzip $srcdir\\" . basename($src) . ".zip"; LogMsg $t = `$cmd`; } for $i (0 .. $#df){ $data_files_str[$i] = sprintf " '%s'",$df[$i]; $data_files_str[$i] .= "," unless ($i == $#df); } @data_files = @df; } # ------------------------------------------------------------------------ sub create_db { my $x; my $cmd; my $t; my $global_name; my $sqltmp = "$logdir\\ctrl_$orasid.sql"; LogMsg $t = `$oradim -shutdown -sid $orasid -usrpwd $passwd -shuttype srvc,inst -shutmode a`; run_svrmgr "shutdown abort"; LogMsg $t = `$oradim -delete -sid $orasid`; foreach $x(@data_files,@ctrl_files, @redo_files){ chkdir(dirname($x))} foreach $x($ctrl_files[0],@redo_files){ chdir dirname($x) || problem "Cannot create: $x"; $cmd = "$unzip $coldarc\\config.$srcsid.zip " . basename($x); LogMsg $t = `$cmd`; } LogMsg $t = `$oradim -new -sid $orasid -intpwd $passwd -startmode auto -pfile $pfile{$orasid}`; open SQL,">$sqltmp" || die "cannot open $sqltmp\n"; foreach $x(@gen_ctrl){ print SQL "$x\n"; } print SQL "exit"; close SQL || die "cannot close $sqltmp\n"; LogMsg $t = `$svrmgrl \@$sqltmp`; # problem "Failed to create db" if grep(/ORA-/,($t)); LogMsg $t = `$oradim -startup -sid $orasid -usrpwd $passwd -starttype srvc`; @t = run_svrmgr "select * from global_name;"; foreach $i (0 .. $#t){ if ($t[$i] =~ /^1 row/){ $global_name = $t[$i-1]; $global_name =~ s/\s*$//; } } if ( $global_name =~ /^$srcsid\./){ $global_name =~ s/$srcsid/$orasid/; @t = run_svrmgr "alter database rename global_name to $global_name;"; problem "Cannot rename db" if grep(/ORA-/,($t)); } } # ------------------------------------------------------------------------ sub chk_clone { # Is this a clone -- i.e. new SID? my $i; $srcsid = $oraparm{ORACLE_SID}; if ( uc($srcsid) eq uc($orasid) ){ $srcsid = $orasid; problem "$myname must not be used to restore a database"; } problem "No oradim specified for $srcsid\n" unless ($oradim = $ORADIM{$srcsid}); problem "No svrmgrl specified for $srcsid\n" unless ( $svrmgrl = $SVRMGRL{$srcsid}); problem "No sqlplus specified for $srcsid\n" unless ( $sqlplus = $SQLPLUS{$srcsid}); # Sanity Checks -- over-ride with NoSanity{} entries in common.pl my $x = $pfile{$srcsid}; $x =~ s/$srcsid/$orasid/ig; if ( uc($x) ne uc($pfile{$orasid}) ) { problem "Sanity: $x differs from $pfile{$orasid}" unless ($NoSanity{$pfile{$orasid}}); } if ( uc("$coldarc\\$srcsid") ne uc($srcdir)){ problem "Sanity: $coldarc\\$srcsid differs from $srcdir" unless ($NoSanity{$srcdir}); } for $i (0 .. $#ctrl_files){$ctrl_files[$i] =~ s/$srcsid/$orasid/ig}; } # ------------------------------------------------------------------------ sub chkdirs{ chkdir($oraparm{log_archive_dest_1}); chkdir($oraparm{log_archive_dest}); chkdir($oraparm{background_dump_dest}); chkdir($oraparm{user_dump_dest}); } # ------------------------------------------------------------------------ # Main ... start here: my $f; $MyName = basename $0,"\.pl"; die "usage: $MyName src_dir intrnl_passwd db_name\n" unless (@ARGV == 3); $userid = "sys"; $srcdir = $ARGV[0]; $passwd = $ARGV[1]; $orasid = $ARGV[2]; chk_env(); chk_nodb(); @start = localtime(time); $start_str = sprintf "%04d-%02d-%02d %02d:%02d:%02d",$start[5]+1900, $start[4]+1,$start[3],$start[2],$start[1],$start[0]; $logfile = sprintf "%s\\%s_%s_%02d%02d%02d.log",$logdir,$orasid,$MyName, $start[5]%100,$start[4]+1,$start[3]; open(LOG,">>$logfile") || die "error opening $logfile\n"; LogMsg "commencing coldrest"; read_header(); chk_clone(); $coldarc = "$coldarc\\$srcsid"; foreach $f ( "$pfile{$srcsid}",@i_files ){ copy_pfile($f); } chkdirs(); copy_dfile(); gen_ctrl_sql(); create_db(); LogMsg "$MyName completed normal"; close(LOG) || die "error closing $logfile\n";