本文共 71404 字,大约阅读时间需要 238 分钟。
use Getopt::Long qw(:config no_ignore_case);
use POSIX ":sys_wait_h";use strict;use vars qw/ %opt /;our %transfer = ();our $context = %opt;our @tablespaces = ();our @properties = ();our %props;our $tmp;our $xttpath;our $rmantfrdf;our $rmandstdf;our $xttprep;our $rmanpath;our $connectstring;our $sqlSettings;our $errfile;our $getfile;our $xtts_incr_backup = "xib";our @rolltbsArray = ();my %tbsHash;my $fixCnvSql;my $fixRollSql;our @forkArray = ();my $xttrollforwardp;my $rollParallel = 0;my $getParallel = 0;my $metaTransfer = 0;my $scpParms = 0;our $xttprop;our $cnvrSql = "xttcnvrtbkupdest.sql";our $stageondest;our $backupondest;our $platformid;our $connectstringcnvinst;our $myversion = "2.0";our %dirHash;our $dirmaxVal = 100;use constant PREPARE => 1;
use constant BCKPINCR => 2;use constant NEWPLAN => 3;use constant GETFILE => 5;use constant BACKUP => 6;use constant RESTORE => 7;use constant RECOVER => 8;use constant ROLLFORWARD => 9;use constant RESINCRDMP => 10;use constant LOCALFILE => 1;
use constant LINK => 2;use constant VER11 => 1;
use constant VER12 => 2;sub parseArg
{ GetOptions ($context,'backup|b', 'bkpincr|B', 'convert|c', 'debug|d:i', 'bkpexport|E', 'generate|e', 'incremental|i', 'orahome|o=s', 'prepare|p', 'rollforward|r', 'determinescn|s', 'xttdir|D=s', 'propfile|F=s', 'getfile|G', 'propdir|I', 'clearerrorfile|L', 'orasid|O=s', 'restore|R', 'recover|X', 'resincrdmp|M', 'rolltbs|T=s', 'setupgetfile|S', 'ignoreerrors', 'version|v', 'help|h' ) or usage();
if ($context->{"help"})
{usage();
}
if ($context->{"version"})
{PrintMessage ("Version is $myversion"); exit (1);
}
if (defined ($context->{"debug"}) && ($context->{"debug"} == 0))
{$context->{"debug"} = 1;
}
if ($ENV{'XTTDEBUG'}) {$context->{"debug"} = $ENV{'XTTDEBUG'};
}
# User provided a directory, start from there to pick up files
if ($context->{"propdir"}) {use File::Basename; chdir (dirname($0));
}
if ($context->{"propfile"})
{$xttprop = $context->{"propfile"};
}
else {$xttprop = "xtt.properties";
}
}sub touchErrFile
{ my $message = $_[0];open ERRFILE, ">>$errfile";
print ERRFILE "$messagen"; close ERRFILE;}sub Unlink
{ my $delFile = $_[0]; my $force = $_[1];if ($force || ($context->{"debug"} < 2))
{unlink ($delFile);
}
}sub GetRMANTrace
{ my $fileAppend = $_[0]; my $trace = ""; my $traceFile;if ($context->{"debug"})
{$traceFile = "$tmp/rmantrc_".GetTimeStamp(). "_".$fileAppend.".trc"; $trace = "debug trace $traceFile";
}
return ($trace, $traceFile);}sub deleteErrFile
{ PrintMessage ("Deleting error file"); if (-e $errfile) {Unlink ($errfile, 1);
}
}sub checkErrFile
{ if (-e $errfile) {{
die "
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!Some failure occurred. Check $errfile for more details If you have fixed the issue, please delete $errfile and run it again OR run xttdriver.pl with -L option
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
";}
}
}sub debugprint
{ my $message = $_[0]; my $debuglevel = $_[1];if ($context -> {"debug"} >= $debuglevel)
{print $_[0] . "\n";
}
}sub debug
{ debugprint ($_[0], 1);}sub debug3
{ debugprint ($_[0], 3);}sub debug4
{ debugprint ($_[0], 4);}sub Die
{my $message = $_[0];touchErrFile($message);
die "
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!$message
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!";}sub trim
{ my $string = $_[0]; $string =~ s/^s+//; $string =~ s/\s+$//; return $string;}sub checkError
{ my ($errorMessage, $output) = @_;#No need to check for error when user does not want to
if (defined($context->{"ignoreerrors"})) {return;
}
chomp ($output);
$output = trim ($output);if (($output =~ /ORA[-][0-9]/) || ($output =~ /SP2-.*/))
{debug $output; Die("$errorMessage");
}
debug $output;}sub PrintMessage
{my $message = $_[0];
";
}sub parseProperties
{ PrintMessage ("Parsing properties");# Check if any failure occured and stop exection
checkErrFile();@properties = qw(tablespaces stageondest storageondest dfcopydir
backupondest backupformat platformid oracle_home_cnvinst oracle_sid_cnvinst asm_home asm_sid dstlink srcdir dstdir srclink rollparallel getfileparallel metatransfer desthost desttmpdir destuser);
open my $in, "$xttprop" or Die "$xttprop not found: $!";
while(<$in>)
{next if /^#/; $props{$1}=$2 while m/(\S+)=(.+)/g;
}
close $in;if ($context -> {"debug"})
{foreach my $pkey (keys %props) { print "Key: $pkey\n"; print "Values: $props{$pkey}\n"; }
}
@tablespaces = split(/,/, $props{'tablespaces'});PrintMessage ("Done parsing properties");
}sub check
{ my $arg = $_[0]; my $key = $_[1];debug "ARGUMENT $key";
if (!defined ($arg))
{Die ("Please define xtt.properties:$key");
}
}sub checkNoPwdSSHEnabled
{ my $outFile = "";if ($metaTransfer == 0)
{return;
}
$outFile = "$tmp/transferFile".GetTimeStamp().".log";
unless (system ("ssh $scpParms \"echo host\" 2> $outFile") == 0)
{my @failArray = (); open FAIL, "$outFile"; @failArray =; close FAIL; Die ("Passwordless SSH not enabled to machine\n@failArray");
}
}sub transferFiles
{ my @trnsfrFiles = @_; my $output = 0; my $files = ""; my $scpParmsL = ""; my $outFile = "$tmp/transferFile".GetTimeStamp().".log";if ($metaTransfer == 0)
{return;
}
foreach my $x (@trnsfrFiles) {chomp($x); $files = $files."$x ";
}
$scpParmsL ="-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ". "NumberOfPasswordPrompts=0 ";
unless (system ("scp $scpParmsL $files ".$context -> {"remoteConnect"}.
":".$context->{'desttmp'}." 2> $outFile") == 0)
{
print "scp $scpParmsL $files ".$context -> {"remoteConnect"}. ":".$context->{'desttmp'}." 2> $outFile"; my @failArray = (); open FAIL, "$outFile"; @failArray =; close FAIL; Die ("Unable to transfer files to destination machine\n@failArray");
}
Unlink ($outFile);}sub getParallelProp
{ my $propValue = $_[0];# We need to find how how many cores are there in the machine and then use
# that number to see if what the user has provided is less than that # For now we assume that max is 8 # use Sys::Info; # use Sys::Info::Constants qw( :device_cpu ); # my $info = Sys::Info->new; # my $cpu = $info->device( CPU => %options ); # # printf "CPU: %sn", scalar($cpu->identify) || 'N/A'; # printf "CPU speed is %s MHzn", $cpu->speed || 'N/A'; # printf "There are %d CPUsn" , $cpu->count || 1; # printf "CPU load: %sn" , $cpu->load || 0; if ($propValue > 8) {$propValue = 8; PrintMessage ("Maximum $propValue files will be fetched in parallel");
}
return $propValue;
}sub checkProps
{ PrintMessage ("Checking properties");# Check if any failure occured and stop exection
checkErrFile();# The following are required irrespective of what option is used
check $props{'tablespaces'}, $properties[0]; check $props{'platformid'}, $properties[6]; check $props{'backupformat'}, $properties[5]; check $props{'stageondest'}, $properties[1];if (!defined($props{'parallel'}) ||
$props{'parallel'} <= 0)
{
$props{'parallel'} = 8;
}
if (defined($props{'metatransfer'}))
{if (!defined($props{'desthost'})) { Die ("Files transfered requested, but remote host not defined"); } if (!defined($props{'desttmpdir'})) { Die ("Files transfered requested, but remote dir not defined"); } if (defined($props{'destuser'})) { $context -> {"remoteConnect"} = $props{'destuser'}."@".$props{'desthost'}; } else { $context -> {"remoteConnect"} = $props{'desthost'}; } $context -> {"desttmp"} = $props{'desttmpdir'}; $metaTransfer = 1; $scpParms = "-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ". "NumberOfPasswordPrompts=0 ".$props{'desthost'};
}
if (!defined($props{'getfileparallel'}) ||
$props{'getfileparallel'} <= 0)
{
$getParallel = 1;
}
else {$getParallel = getParallelProp($props{'getfileparallel'});
}
# Depending on option the checks are done
if (defined($context->{"setupgetfile"}) ||defined($context->{"getfile"}))
{
check $props{'srcdir'}, $properties[12]; check $props{'dstdir'}, $properties[13]; check $props{'srclink'}, $properties[14];
}
if (defined($context->{"prepare"}))
{check $props{'dfcopydir'}, $properties[3];
}
if (defined($context->{"rollforward"}))
{check $props{'backupondest'}, $properties[4];
}
if (defined($context->{"srcdir"}) &&
defined($context->{"dstdir"}))
{
my @srcDir = split(/,/, $props{'srcdir'}); my @dstDir = split(/,/, $props{'dstdir'}); my $dstCount = scalar (@dstDir); my $srcCount = scalar (@srcDir); if (($dstCount > 1) && ($srcCount != $dstCount)) { Die ("checkProps: Number of source objects does not match ". "destination objects"); } if ($dstCount >= $dirmaxVal) { Die ("checkProps: Number of dir objects cannot exceed $dirmaxVal"); }
}
PrintMessage ("Done checking properties");
}sub warnMesg
{my $message = $_[0];
print "
$message
";
}sub checkDbLink
{ my $sqlOutput ; my $dblink = $_[0]; my $sqlQuery ="select 'Count=' || count(*) from dual@"."$dblink;";
$sqlOutput = `sqlplus -L -s $connectstring <
$sqlSettings $sqlQuery quit; EOF `;
chomp ($sqlOutput);
$sqlOutput = trim ($sqlOutput);if ($sqlOutput =~ m/^Count=(.*)/)
{if ($1 <= 0) { Die ("Database link $props{'dstlink'} is not working"); }
}
else {Die ($sqlOutput);
}
}sub checkDBState
{ my $sqlOutput ; my $dblink = $_[0]; my $sqlQuery ='select \'STATUS=\' || status FROM v\$instance;';
$sqlOutput = `sqlplus -L -s $connectstring <
$sqlSettings $sqlQuery quit;
EOF
`;
chomp ($sqlOutput);
$sqlOutput = trim ($sqlOutput);if ($sqlOutput =~ m/^STATUS=(.*)/)
{my $startStatus = trim ($1); if ($startStatus ne "STARTED") { Die ("Open database in nomount mode and try rollforward again"); }
}
else {Die ($sqlOutput);
}
}sub checkCompat
{ my $sqlOutput ; my $sqlQuery ='select to_number(replace(RPAD(value, 10, \'.0\'), \'.\')) from '. 'v\$parameter WHERE name = \'compatible\';';
$sqlOutput = `sqlplus -s -L $connectstring <
$sqlSettings $sqlQuery quit;
EOF
`;chomp ($sqlOutput);
$sqlOutput = trim ($sqlOutput);debug "$sqlOutputn";
if ($sqlOutput =~ m/.ORA-./)
{Die ("Unable to check database compatibility");
}
if ($sqlOutput >= 121000)
{return VER12;
}
else {return VER11;
}
}sub getPlatName
{ my $sqlOutput ; my $platid = $props{'platformid'}; my $sqlQuery ='select platform_name from v\$transportable_platform '. 'where platform_id = '.$platid.';';
$sqlOutput = `sqlplus -s -L $connectstring <
$sqlSettings $sqlQuery quit;
EOF
`;chomp ($sqlOutput);
$sqlOutput = trim ($sqlOutput);if ($sqlOutput =~ m/.ORA-./)
{Die ("Unable to fetch platform name");
}
return $sqlOutput;
}sub fetchDirEntry
{ my $dirtoCheck = $_[0]; my $remoteLink = $_[1]; my @sqlOutput ; my $sqlQuery ;debug3 "fetchDirEntry: parms $remoteLink, $dirtoCheckn";
if ($remoteLink eq "")
{debug "fetchDirEntry: remotelink not present\n"; $sqlQuery = "SELECT 'DIRECTORY_NAME=' || DIRECTORY_NAME, 'DIRECTORY_PATH=' || DIRECTORY_PATH FROM ". "ALL_DIRECTORIES WHERE DIRECTORY_NAME in ($dirtoCheck);";
}
else {$sqlQuery = "SELECT 'DIRECTORY_NAME=' || DIRECTORY_NAME, 'DIRECTORY_PATH=' || DIRECTORY_PATH FROM ". "ALL_DIRECTORIES\@". "$remoteLink WHERE DIRECTORY_NAME in ($dirtoCheck);";
}
@sqlOutput = `sqlplus -L -s $connectstring <
$sqlSettings $sqlQuery quit; EOF `;
chomp (@sqlOutput);
return @sqlOutput;
}sub fetchCheckDirObjectsDST
{ my $dirtoCheck = $_[0]; my $remoteLink = $_[1]; my $dirObjects = $_[2]; my $dirtoCheckLink; my $dirObjPath; my @sqlOutput ; my $sqlQuery ; my $found ; my %dstHash = ();debug "fetchCheckDirObjectsDST: Check dir path $remoteLinkn";
my @srcDir = split(/,/, $props{'srcdir'});
my @dstDir = split(/,/, $props{'dstdir'}); my $tempDir = ''; my $idx = 0; my $dstCount = scalar (@dstDir); my $srcCount = scalar (@srcDir);if (($dstCount > 1) && ($srcCount != $dstCount))
{Die ("fetchCheckDirObjectsDST: Number of source objects does not match ". "destination objects");
}
foreach my $var (@dstDir)
{$var = trim($var); $tempDir = $tempDir."\'$var\',"; $dstHash{"$var"} = trim($dstDir[$idx]); debug3 "fetchCheckDirObjectsDST: DestDir: ".$dstHash{"$var"}."\n"; if ($dstCount > 1) { $idx = $idx + 1; }
}
if ($tempDir =~ m/(.*),/)
{$tempDir = $1;
}
@sqlOutput = fetchDirEntry ($tempDir, $remoteLink);
foreach my $x (@sqlOutput)
{$x = trim ($x); if ($x =~ m/^DIRECTORY_NAME=(.*)DIRECTORY_PATH=(.*)/) { my $nameTemp = $1; $dirObjPath = $2; $nameTemp = trim($nameTemp); if ($dirObjPath =~ m/(.*)\/$/) # Removes trailing spaces { $dirObjPath = $1; } $dirObjects->{$nameTemp} = $dirObjPath; }
}
if ((keys%$dirObjects) == 0)
{print "fetchCheckDirObjectsDST: Directory ". "object \"$dirtoCheck\" does not exist\n"; Die (@sqlOutput);
}
return $dirObjPath;
}sub fetchCheckDirObjectsSRC
{ my $dirtoCheck = $_[0]; my $remoteLink = $_[1]; my $dirObjects = $_[2]; my $dirObjPath; my @sqlOutput ; my $sqlQuery ; my $found ; my %srcHash = ();debug "fetchCheckDirObjectsSRC: Check dir path $remoteLinkn";
my @srcDir = split(/,/, $props{'srcdir'});
my @dstDir = split(/,/, $props{'dstdir'}); my $tempDir = ''; my $idx = 0; my $dstCount = scalar (@dstDir); my $srcCount = scalar (@srcDir);if (($dstCount > 1) && ($srcCount != $dstCount))
{Die ("fetchCheckDirObjectsSRC: Number of source objects does not match ". "destination objects");
}
foreach my $var (@srcDir)
{$var = trim($var); $tempDir = $tempDir."\'$var\',"; $srcHash{"$var"} = trim($dstDir[$idx]); debug3 "fetchCheckDirObjectsSRC: SRCDIR: ".$srcHash{"$var"}."\n"; if ($dstCount > 1) { $idx = $idx + 1; }
}
if ($tempDir =~ m/(.*),/)
{$tempDir = $1;
}
@sqlOutput = fetchDirEntry ($tempDir, $remoteLink);
foreach my $x (@sqlOutput)
{$x = trim ($x); if ($x =~ m/^DIRECTORY_NAME=(.*)DIRECTORY_PATH=(.*)/) { my $nameTemp = $1; $dirObjPath = $2; $nameTemp = trim($nameTemp); if ($dirObjPath =~ m/(.*)\/$/) { $dirObjPath = $1; } # Create a hash with SRC directory and the DST directory object names. $dirObjects->{$dirObjPath}->{"SRC"} = $nameTemp; $dirObjects->{$dirObjPath}->{"DST"} = $srcHash{"$nameTemp"}; }
}
if ((keys%$dirObjects) == 0)
{print "fetchCheckDirObjectsSRC: Directory ". "object \"$dirtoCheck\" does not exist\n"; Die (@sqlOutput);
}
return;
}sub fixXTTNewDatafiles
{ my $dirObjects = $_[0]; my $replaceFile = 0; my @destArray = ();debug "fixXTTNewDatafiles: Entered";
open(RMANDSTDF, "$rmandstdf") ||
Die "Cant find $rmandstdf";
@destArray = ;
close RMANDSTDF;foreach my $line (@destArray)
{if ($line =~ /.*DESTDIR:.*/) { $replaceFile = 1; last; }
}
if ($replaceFile)
{open(RMANDSTDF1, ">$rmandstdf"."temp") || Die "Cant find $rmandstdf"; foreach my $line (@destArray) { if ($line =~ m/(.*),DESTDIR:(.*)\,(.*)/) { debug3 "fixXTTNewDatafiles: $1\n"; my $dest = $dirObjects->{$2}->{"DST"}; print RMANDSTDF1 "$1,$dest".":$3\n"; } else { print RMANDSTDF1 "$line"; } } close RMANDSTDF1; Unlink ("$rmandstdf", 1); system("\\cp $rmandstdf"."temp $rmandstdf");
}
}sub verifySrcdirDatafiles
{ my $dfPath = "$tmp/xttprepare.cmd"; my @dfList = (); my @unidfList = (); my %seen; my $name;debug ("verifySrcdirDatafiles: Entered");
open DFLIST, $dfPath ;
@dfList = ; close DFLIST;foreach my $df (@dfList)
{chomp ($df); debug3 "verifySrcdirDatafiles: $df"; if ($df =~ /^#DNAME:(.*)/) { $name = $1; if (! (grep { $_ eq $name } @unidfList)) { push (@unidfList, $1); } }
}
foreach my $key (keys %dirHash)
{debug4 "verifySrcdirDatafiles: Hash key".$key."\n"; debug4 "verifySrcdirDatafiles: Hash value".$dirHash{"$key"}."\n";
}
foreach my $df (@unidfList)
{debug3 "verifySrcdirDatafiles: $df\n"; if (!defined($dirHash{$df})) { Die ("Datafile path $df and source directory object ". "path ".$dirHash{$df}." doest not match"); }
}
}sub generate_batch_tsoutput()
{ my $option = $_[0]; my $stageondest = $props{'stageondest'}; my $platform = "'$props{'platform'}'"; my $storageondest = $props{'storageondest'}; my $dfcopydir = $props{'dfcopydir'}; my $backupformat = $props{'backupformat'}; my $script_type = "PREPARE"; my $output; my $tsbkupmap = "$tmp/tsbkupmap.txt"; my $incrbkups = "$tmp/incrbackups.txt";my $number_of_tablespaces_per_batch =
int(($#tablespaces + 1)/$props{'parallel'});
# Check if any failure occured and stop exection
checkErrFile();$number_of_tablespaces_per_batch = ($number_of_tablespaces_per_batch == 0)
? 1 : $number_of_tablespaces_per_batch;
my $total_sets = $number_of_tablespaces_per_batch * $props{'parallel'};
debug3 $connectstring;
debug3 "size of tablespace " . scalar(@tablespaces); debug3 "No. of tablespaces per batch " . $number_of_tablespaces_per_batch;my $countno = 0;
my $tablespace_str = ""; my $old_tablespace_str = "";if ($option == BACKUP)
{checkMove ($tsbkupmap); checkMove ($incrbkups);
}
## Generate the rmanconvert.cmd and xttplan.txt from the output
## for Prepare if (($option == PREPARE) || ($option == BACKUP) || ($option == GETFILE)) {open(XTTPLAN, ">$xttpath") || die 'Cant find xttplan.txt, TMPDIR undefined'; if ($option == PREPARE) { open(RMANCONVERT, ">$rmanpath") || die 'Cant find rmanconvert.cmd, TMPDIR undefined'; } # Generate new datafiles for 12c backup elsif ($option == BACKUP) { $script_type = "BACKUP"; open(RMANDSTDF, ">$rmandstdf") || die 'Cant find $rmandstdf, TMPDIR undefined'; } else { $script_type = "TRANSFER"; open(RMANDSTDF, ">$rmandstdf") || die 'Cant find $rmandstdf, TMPDIR undefined'; }
}
elsif ($option == BCKPINCR) {open XTTNEWPLAN, ">$tmp/xttplan.txt.new" or die $!; $script_type = 'DETNEW';
}
elsif ($option == NEWPLAN) {$script_type = 'PREPNEXT';
}
## Based on parallelism, do so many tablespaces at a time.
my $ind = 0; while ($ind < $total_sets ||$ind < scalar(@tablespaces))
{
## Remove leading and trailing spaces. $tablespaces[$ind] =~ s/^\s+//; $tablespaces[$ind] =~ s/\s+$//; ## grab the rest while ($ind < scalar(@tablespaces) && $ind >= $total_sets) { $tablespace_str .= "'" . $tablespaces[$ind] . "'"; if ($ind != $#tablespaces) { $tablespace_str .= ","; } $ind++; } if ($ind < $total_sets) { $tablespace_str .= "'" . $tablespaces[$ind] . "'"; if (($ind % $number_of_tablespaces_per_batch) != ($number_of_tablespaces_per_batch-1)) { $tablespace_str .= ","; } $countno++; $ind++; } if ($countno == $number_of_tablespaces_per_batch || $ind == scalar(@tablespaces)) { debug "TABLESPACE STRING :" . $tablespace_str; $countno = 0; ## Subst. for tablespace names open my $in, "xttprep.tmpl" or die $!; if (($option == PREPARE) || ($option == BACKUP) || ($option == GETFILE)) { print "Prepare source for Tablespaces: $tablespace_str $stageondest\n"; ## Open the pareparesrc.sql file and substitute all the parameters open my $prepout, ">xttpreparesrc.sql" or die $!; while(<$in>) { s/%%stageondest%%/$stageondest/; s/%%storageondest%%/$storageondest/; s/%%dfcopydir%%/$dfcopydir/; s/%%tmp%%/$tmp/; s/%%parallel%%/$props{'parallel'}/; s/%%type%%/$script_type/; s/%%backupformat%%/$backupformat/; if ($_ =~ /%%TABLESPACES%%/) { my @tbs = split(/,/, $tablespace_str); foreach (@tbs) { print $prepout "$_,\n"; } print $prepout "NULL\n"; } else { print $prepout "$_"; } } close $in; close $prepout; ## timestamp here -- start print "xttpreparesrc.sql for $tablespace_str started at ". (localtime) . "\n"; ## Reset tablespace string $old_tablespace_str = $tablespace_str; $tablespace_str = ""; ## Substitution ends here and xttpreparesrc.sql generated my $output = `sqlplus -L -s $connectstring \@xttpreparesrc.sql`; ## timestamp here -- end print "xttpreparesrc.sql for $tablespace_str ended at ". (localtime) . "\n"; checkError ("Error in executing xttpreparesrc.sql", $output); my @line = split /\n/, $output; if (($option == BACKUP) || ($option == GETFILE)) { foreach my $line (@line) { if ($line =~ /^#PLAN:/) { $line =~ s/^#PLAN://; print XTTPLAN "$line\n"; if ($line =~ /(\S+)::::(\S+)/) { print RMANDSTDF "::$1\n"; } } elsif ($line =~ /^#TRANSFER:/) { $line =~ s/^#TRANSFER://; if ($line =~ /.*source_file_name=(.*?),(.*?),(.*)/) { my $tsname = $1; my $fullPath = trim($2); my $fname = trim($3); # Remove the trailing "/" if ($fullPath =~ m/(.*)\/$/) { $fullPath = $1; } my $fullPath = $fullPath."/".$fname; push(@{$transfer{$tsname}}, $fullPath); } } elsif ($line =~ /^#NEWDESTDF:/) { $line =~ s/^#NEWDESTDF://; print RMANDSTDF "$line\n"; } } # Verify that the files are in proper directory. &verifySrcdirDatafiles(); } if (($option == BACKUP) || ($option == PREPARE)) { foreach my $line (@line) { if ($line =~ /^#PLAN:/) { $line =~ s/^#PLAN://; print XTTPLAN "$line\n"; } elsif ($line =~ /^#CONVERT:/) { $line =~ s/^#CONVERT://; print RMANCONVERT "$line \n"; } } ## Invoke the backup as copy routine once generated as the last ## step my $rmancopycmd = "$tmp/xttprepare.cmd"; my ($rmanTrace, $traceFile) = GetRMANTrace("prepare"); $output = `rman target \/ $rmanTrace cmdfile $rmancopycmd`; if ($output =~ /ERROR MESSAGE/) { Die("$output $!"); } Unlink ($traceFile); if ($option == BACKUP) { # We need to generate tsbkpupmap.txt for &gentablespace_backupmap( $output ) ; } } } elsif ($option == BCKPINCR) { ## Subst. for tablespace names print "Prepare newscn for Tablespaces: $tablespace_str \n"; ## Open the pareparesrc.sql file and substitute all the parameters open my $prepout, ">xttdetnewfromscnsrc.sql" or die $!; while(<$in>) { s/%%tmp%%/$tmp/; s/%%type%%/$script_type/; if ($_ =~ /%%TABLESPACES%%/) { my @tbs = split(/,/, $tablespace_str); foreach (@tbs) { print $prepout "$_,\n"; } print $prepout "NULL\n"; } else { print $prepout "$_"; } } close $in; close $prepout; ## Reset tablespace string $tablespace_str = ""; ## Substitution ends here and xttdetnewfromscnsrc.sql generated $output = `sqlplus -L -s $connectstring \@xttdetnewfromscnsrc.sql`; checkError ("Error in executing xttdetnewfromscnsrc.sql", $output); my @line = split /\n/, $output; foreach my $line (@line) { print XTTNEWPLAN $line . "\n"; } } elsif ($option == NEWPLAN) { ## Subst. for tablespace names print "Prepare newscn for Tablespaces: $tablespace_str \n"; ## Open the pareparesrc.sql file and substitute all the parameters open my $prepout, ">xttpreparenextiter.sql" or die $!; while(<$in>) { s/%%tmp%%/$tmp/; s/%%type%%/$script_type/; if ($_ =~ /%%TABLESPACES%%/) { my @tbs = split(/,/, $tablespace_str); foreach (@tbs) { print $prepout "$_,\n"; } print $prepout "NULL\n"; } else { print $prepout "$_"; } } close $in; close $prepout; ## Reset tablespace string $tablespace_str = ""; ## Substitution ends here and xttpreparenextiter.sql generated $output = `sqlplus -L -s $connectstring \@xttpreparenextiter.sql`; checkError ("Error in executing xttpreparenextiter.sql", $output); } }
}
if (($option == PREPARE) || ($option == BACKUP) || ($option == GETFILE)) {close (XTTPLAN); if ($option == PREPARE) { close (RMANCONVERT); } elsif ($option == GETFILE) { close (RMANDSTDF); # Replace the paths with the directory destination objects. fixXTTNewDatafiles (\%dirHash); # Ver: 1.4.1 We will generate the SQL file in the destination. my $transferCount = 0; open GETFILE1, ">$getfile"; foreach my $user (sort keys %transfer) { debug "$user: @{$transfer{$user}}\n"; foreach my $file (@{$transfer{$user}}) { # Bug 17673476: If the destination file is for ASM, it does not # work with automated filenames. So we convert the names here. # The automated filenames will be of format "x.y.z". This will # converted to "x_y_z" my $srcFile; my $destFile; my $srcPath; my $dstPath; if ($file =~ m/(.*)\/(.*)/) { $srcFile = $2; $srcPath = $dirHash{"$1"}->{"SRC"}; $dstPath = $dirHash{"$1"}->{"DST"}; } if ($srcFile =~ m/(.*)\.([0-9]+)\.([0-9]+)/) { $destFile = "$1_$2_$3"; } else { $destFile = $srcFile; } print GETFILE1 "$transferCount,$srcPath,$srcFile,$dstPath,$destFile\n"; } $transferCount = $transferCount + 1; } close GETFILE1; } elsif ($option == BACKUP) { close (RMANDSTDF); }
}
elsif ($option == BCKPINCR) {close (XTTNEWPLAN);
}
if ($metaTransfer)
{my @trnsfrFiles = (); if ($option == GETFILE) { push (@trnsfrFiles, "$tmp/xttnewdatafiles.txt"); push (@trnsfrFiles, "$tmp/getfile.sql"); transferFiles (@trnsfrFiles); } if ($option == BCKPINCR) { push (@trnsfrFiles, "$tmp/xttplan.txt"); transferFiles (@trnsfrFiles); } if ($option == PREPARE) { push (@trnsfrFiles, "$tmp/rmanconvert.cmd"); transferFiles (@trnsfrFiles); @trnsfrFiles = (); $context->{"desttmp"} = $props{'stageondest'}; push (@trnsfrFiles, $props{'dfcopydir'}."/*"); transferFiles (@trnsfrFiles); $context -> {"desttmp"} = $props{'desttmpdir'}; } if ($option == BACKUP) { @trnsfrFiles = (); push (@trnsfrFiles, "$tmp/tsbkupmap.txt"); push (@trnsfrFiles, "$tmp/xttnewdatafiles.txt"); transferFiles (@trnsfrFiles); open FILE, "$tmp/incrbackups.txt"; @trnsfrFiles =; close FILE; $context->{"desttmp"} = $props{'stageondest'}; transferFiles (@trnsfrFiles); $context -> {"desttmp"} = $props{'desttmpdir'}; }
}
}sub assignGlobVars
{ if (! defined($context->{"xttdir"})) {if (defined($ENV{'TMPDIR'})) { $tmp = $ENV{'TMPDIR'}; } else { Die ("TMPDIR not defined"); }
}
else {$tmp = $context->{"xttdir"};
}
$xttpath = "$tmp/xttplan.txt";
$rmantfrdf = "$tmp/rmantfrdf.sql"; $rmandstdf = "$tmp/xttnewdatafiles.txt"; $xttprep = "$tmp/xttprepare.cmd"; $rmanpath = "$tmp/rmanconvert.cmd"; $errfile = "$tmp/FAILED"; $getfile = "$tmp/getfile.sql"; $connectstring = "/ as sysdba"; $sqlSettings = "SET TRIMSPOOL OFF LINES 32767 PAGES 0 FEEDBACK OFF ". "VERIFY OFF DEFINE "&" TERMOUT OFF";}sub checkMove
{ my $srcPath = $_[0]; my $dstPath = $_[1];if (!$dstPath)
{my $timenow = time; $dstPath = $srcPath.$timenow;
}
if (-e $srcPath)
{system("\\mv $srcPath $dstPath");
}
}sub fixXTTDestFile
{ my $replaceFile = 0; my @destArray = (); my %dirHash;open(RMANDSTDF, "$rmandstdf") ||
Die "Cant find $rmandstdf";
@destArray = ;
close RMANDSTDF;foreach my $line (@destArray)
{if (($line !~ m/::.*/) && ($line =~ m/.*\:.*/)) { $replaceFile = 1; last; }
}
if ($replaceFile)
{&fetchCheckDirObjectsDST($props{'dstdir'}, "", \%dirHash); open(RMANDSTDF1, ">$rmandstdf"."temp") || Die "Cant find $rmandstdf"; foreach my $line (@destArray) { if (($line !~ m/::.*/) && ($line =~ /.*,(.*):.*/)) { my $tempDir = $1; $line =~ s/$tempDir:/$dirHash{"$tempDir"}/; } if ($line =~ m/(.*)\/(.*)\.([0-9]+)\.(.*)/) { $line = "$1/$2_$3_$4\n"; } print RMANDSTDF1 "$line"; } close RMANDSTDF1;
}
if ($replaceFile)
{Unlink ("$rmandstdf", 1); system("\\cp $rmandstdf"."temp $rmandstdf");
}
}sub getFilesSource
{ PrintMessage ("Getting datafiles from source");my $output;
my $connectstringcnvinst; my $pid; my @getArray = (); my $getFileTemp;&checkDbLink($props{'srclink'});
fixXTTDestFile();
$connectstringcnvinst = "/ as sysdba";
open FILE, "$getfile" or Die ("Cannot open $getfile");;
@getArray = ; close FILE;foreach my $x (@getArray)
{chomp ($x); if ($x =~ m/(.*?),(.*?),(.*?),(.*?),(.*)/) { my $sqlQuery = "BEGIN DBMS_FILE_TRANSFER.GET_FILE( source_directory_object => '".$2."', source_file_name => '".$3."', source_database => '".$props{'srclink'}."', destination_directory_object => '".$4."', destination_file_name => '".$5."'); END; / "; $getFileTemp = "getfile"."_$2"."_$3"."_$1".".sql"; $getFileTemp = lc ($getFileTemp); open FILE, ">$getFileTemp"; print FILE "$sqlQuery\nquit\n"; close FILE; ChecktoProceed($getParallel); $pid = fork(); if ($pid == 0) { PrintMessage ("Executing getfile for $getFileTemp"); $output = `sqlplus -L -s \"$connectstringcnvinst\" \@$getFileTemp`; checkError ("Error in executing $getFileTemp", $output); Unlink ($getFileTemp); exit (0); } else { UpdateForkArray ($pid, $getParallel); } }
}
while((my $pid = wait()) > 0)
{#sleep (1);
}
PrintMessage ("Completed getting datafiles from source");
}sub prepare
{ ### ### Prepare starts here ###PrintMessage ("Starting prepare phase");
# Check if any failure occured and stop exection
checkErrFile();my $timenow = time;
## Perform cleanup of files by renaming the xttplan.txt, rmanconvert.cmd
## and xttprep.cmd (datafile copy) checkMove($xttpath, $xttpath.$timenow); checkMove($rmanpath, $rmanpath.$timenow); checkMove($xttprep, $xttprep.$timenow);## For each tablespace, lets generate the scripts
my $stageondest = $props{'stageondest'}; my $platform = "'$props{'platform'}'"; my $storageondest = $props{'storageondest'}; my $dfcopydir = $props{'dfcopydir'};debug "Parallel:" . $props{'parallel'};
## Remove trailing '/'s
$dfcopydir = $1 if($dfcopydir=~/(.*)\/$/); ## Check if previous ".tf" files present. ## my @present = glob("$dfcopydir/*.tf");if (@present)
{## Try deleting any existing copied datafiles system("\\rm -f $dfcopydir/*.tf"); sleep 5;
}
if ($context->{"setupgetfile"})
{&fetchCheckDirObjectsSRC($props{'srcdir'}, "", \%dirHash); foreach my $keys (keys %dirHash) { debug4 "prepare: Key is $keys\n"; debug4 "prepare: Value is ".$dirHash{"$keys"}."\n"; } &generate_batch_tsoutput(GETFILE);
}
elsif ($context->{"backup"}) {&generate_batch_tsoutput(BACKUP);
}
else {&generate_batch_tsoutput(PREPARE);
}
PrintMessage ("Done with prepare phase");
}sub checkExec
{ if (defined ($context->{"orahome"})) {$ENV{'ORACLE_HOME'} = $context->{"orahome"};
}
else {if (defined ($ENV{'ORACLE_HOME'})) { $context->{"orahome"} = $ENV{'ORACLE_HOME'}; } else { Die ("Niether ORACLE_HOME defined or orahome passed to script") ; }
}
if (defined ($context->{"orasid"}))
{$ENV{'ORACLE_SID'} = $context->{"orasid"};
}
else {if (defined ($ENV{'ORACLE_SID'})) { $context->{"orasid"} = $ENV{'ORACLE_SID'}; } else { Die ("Niether ORACLE_SID defined or orasid passed to script") ; }
}
debug "ORACLE_SID : $context->{"orasid"}";
debug "ORACLE_HOME : $context->{"orahome"}";$ENV{'PATH'} = "$context->{"orahome"}/bin".":".$ENV{'PATH'};
$context->{'sqlexec'} = "$context->{"orahome"}/bin/sqlplus"; $context->{'rmanexec'} = "$context->{"orahome"}/bin/rman";if (! -x $context->{"rmanexec"})
{Die ("RMAN executable not found in path");
}
if (! -x $context->{"sqlexec"})
{Die ("SQLPLUS executable not found in path");
}
}sub checkArg
{ if ($context->{"backup"} || $context->{"bkpincr"} ||$context->{"bkpexport"} || $context->{"restore"} || $context->{"recover"} || $context->{"resincrdmp"})
{
my $ver = checkCompat(); if ($ver < VER12) { Die ("Unable to use given option with this version of DB. Min ver". " required is 12.1.0.0"); }
}
}sub Main
{ parseArg(); assignGlobVars(); checkArg();# User wanted to clear file, do it so here
if ($context->{"clearerrorfile"}) {Unlink ($errfile, 1);
}
parseProperties();
checkProps(); checkExec();$context->{"platname"} = getPlatName();
#If we want specify tablespaces to be rolled forward, then store them in
#a hash if ($context->{"rolltbs"}) {@rolltbsArray = split (',', $context->{"rolltbs"}); foreach my $x (@rolltbsArray) { $tbsHash{"$x"}= 1; }
}
if (($context->{"incremental"}) ||
($context->{"bkpincr"}) || ($context->{"bkpexport"}))
{
backincr();
}
elsif ($context->{"rollforward"}) {fixXTTDestFile(); rollforward();
}
elsif ($context->{"restore"}) {fixXTTDestFile(); resrecBkp(RESTORE);
}
elsif (($context->{"recover"}) ||($context->{"resincrdmp"}))
{
fixXTTDestFile(); resrecBkp(RECOVER); if ($context->{"resincrdmp"}) { createDumpFile(); plugin(LOCALFILE); }
}
elsif ($context->{"determinescn"}) {newplan();
}
elsif ($context->{"convert"}) {#If the transfer is across the same endian then platformid will be #set as 0. if ($props{'platformid'} == 0) { genConvertDFNames(); } else { convert(); }
}
elsif ($context->{"generate"}) {plugin(LINK);
}
elsif (($context->{"prepare"}) || ($context->{"backup"}) ||($context->{"setupgetfile"}))
{
checkNoPwdSSHEnabled (); prepare();
}
elsif ($context->{"getfile"}) {getFilesSource();
}
else {usage();
}
}sub gentablespace_backupmap
{ my @line = split /n/, $_[0];my $tsbkupmap = "$tmp/tsbkupmap.txt";
my $incrbkups = "$tmp/incrbackups.txt";my $backupformat = $props{'backupformat'};
my $i = 0; my @fnums = (); my @fnumorder = (); my $chan; my %bpfn; my %bpts; my %chfn; my $tsname; my $fno; my $order = 0; my $dmpFile = 0;open(TSBKMAP, ">>$tsbkupmap") ||
Die("Cant open tablespace backup map file $!");
open(INCRBKUPS, ">>$incrbkups") || Die("Cant open incr backups file $!");
while ($i <= $#line)
{my $str = $line[$i]; if ($str =~ /^ts::(.*)$/) { $tsname = $1; $order = 0; } elsif ($str =~ /channel (.*): specifying datafile/) { $chan = $1; } elsif ($str =~ /.*input Data Pump dump file.*/) { ## piece name is on the next line $i++; $str = $line[$i]; $chan = $1; $dmpFile = 1; } ## In HP, an issue was reported where in the RMAN output was ## shown as 'input datafile fno' instead of 'input datafile file number'. ## So parse for both here. elsif (($str =~ /input datafile file number=(\d+) name/) || ($str =~ /input datafile fno=(\d+) name/)) { $fno = $1 + 0; push(@fnums, $fno); $i++; next; } elsif (defined($chan) && $#fnums >= 0 ) { my @new = @fnums; $chfn{$chan} -> {"fnumarray"} = \@new; @fnums = (); } elsif ($str =~ /channel (.*): finished piece/) { ## piece name is on the next line $i++; $str = $line[$i]; $chan = $1; if ($str =~ /piece handle=(.+)[\/](\S+) tag/) { if ($dmpFile) { my $piece = $2; debug "backup piece:" . $piece . "\n" ; debug "TSNAME:" . $tsname; print TSBKMAP "DMPEXP::$piece\n"; print INCRBKUPS $backupformat."/$piece \n"; } else { $bpfn{$2} -> {"fnumarray"} = $chfn{$chan} -> {"fnumarray"}; $order = $order + 1; $bpfn{$2} -> {"fnumorder"} = $order; debug "TSNAME:" . $tsname; $bpts{$2} = $tsname; } } } elsif ($str =~ /including current control file in backup set/) { do { $i++; $str = $line[$i]; } while ($str !~ /Finished backup/); } $i++;
}
foreach my $pkey (keys %bpfn)
{print INCRBKUPS $backupformat . "/$pkey\n"; my @fns = @{$bpfn{$pkey} -> {"fnumarray"}}; my $sizefns = @fns; print TSBKMAP $bpts{$pkey} . "::"; my $orderx = $bpfn{$pkey} -> {"fnumorder"}; foreach my $v (@fns) { $sizefns--; print TSBKMAP $v; print TSBKMAP "," if ($sizefns > 0); } print TSBKMAP ":::". $orderx; print TSBKMAP "=" . $pkey; print TSBKMAP "\n";
}
close(TSBKMAP); close(INCRBKUPS);}sub backincr()
{ PrintMessage ("Backup incremental"); checkErrFile(); ## Move out any existing xttplan.txt.new my $xttpathnew = "$tmp/xttplan.txt.new"; my @trnsfrFiles = (); my $tsbkupmap = "$tmp/tsbkupmap.txt"; my $incrbkups = "$tmp/incrbackups.txt";if ( -e $xttpathnew )
{my $timenow = time; system("\\mv $xttpathnew $xttpath" . $timenow);
}
## Generate the next round xttplan.txt
&generate_batch_tsoutput(BCKPINCR);## At this point, we must have a new xttplan.txt.new
## populated with required from_scn's for next incremental backup.debug "Start backup incremental" ;
# Clean up any rmanincr.cmd from TMPDIR area.
my $rmanincrpath = "$tmp/rmanincr.cmd"; my $timenow = time; if ( -e $rmanincrpath ) {system("\\mv $rmanincrpath $rmanincrpath" . $timenow);
}
debug "Crossed mv " ;my $backupformat = $props{'backupformat'};
debug "Crossed mv $backupformat" ; ## Remove trailing '/'s $backupformat = $1 if($backupformat=~/(.*)\/$/);if ($context->{"bkpexport"})
{open(XTTPLAN, $xttpath) || die 'Cant find xttplan.txt, TMPDIR undefined'; my $rman_str1 = "set nocfau;"; my $rman_str; my @scnArray = (); my @scnSortedArray = (); my $scn; my $rmCmd; while () { if (/(\S+)::::(\S+)/) { push (@scnArray, $2); } } close XTTPLAN; if (!@scnArray) { ErrorMessage ("BACKUPINCR: Could not find lowest SCN"); } @scnSortedArray = sort (@scnArray); $scn = $scnSortedArray[0]; $rmCmd = "BACKUP FOR TRANSPORT INCREMENTAL from scn $scn TABLESPACE ". " $props{'tablespaces'}". " FORMAT '$backupformat/%U'". " DATAPUMP FORMAT '$backupformat/%U';"; open(RMANINCR, ">$tmp/rmanincr.cmd") || Die("Cant open rmanincr.cmd $!"); print RMANINCR "$rmCmd"; close RMANINCR;
}
else {## Generate $tmp/rmanincr.cmd debug "Generate $tmp/rmanincr.cmd"; open(RMANINCR, ">$tmp/rmanincr.cmd") || Die("Cant open rmanincr.cmd $!"); ## Parse out xttplan.txt and build the rman command open(XTTPLAN, $xttpath) || die 'Cant find xttplan.txt, TMPDIR undefined'; my $rman_str1 = "set nocfau;"; my $rman_str; while () { if (/(\S+)::::(\S+)/) { my $tablespace = $1; my $scn = $2; my $rman_str; print RMANINCR $rman_str1 . "\n"; $rman_str = "host 'echo ts::$tablespace';"; print RMANINCR $rman_str . "\n"; if ($context->{"bkpincr"}) { $rman_str = "backup for transport allow INCONSISTENT ". "incremental from scn $scn "; } else { $rman_str = "backup incremental from scn $scn "; } print RMANINCR $rman_str . "\n"; if ($context->{"incremental"}) { $rman_str = " tag tts_incr_update tablespace '$tablespace' ". " format"; print RMANINCR $rman_str . "\n"; } else { $rman_str = " tablespace '$tablespace' format"; print RMANINCR $rman_str . "\n"; } $rman_str = " '$backupformat/%U';"; print RMANINCR $rman_str . "\n"; } } close(XTTPLAN); close(RMANINCR);
}
## Execute the rman command here
my ($rmanTrace, $traceFile) = GetRMANTrace("incrbackup");
my $output_str = "rman target \/ $rmanTrace cmdfile $rmanincrpath"; print $output_str . "n";my $output = rman target \/ $rmanTrace cmdfile $rmanincrpath
;
checkMove ($tsbkupmap);
checkMove ($incrbkups);&gentablespace_backupmap( $output ) ;
if ($metaTransfer)
{@trnsfrFiles = (); push (@trnsfrFiles, "$tmp/tsbkupmap.txt"); transferFiles (@trnsfrFiles); open FILE, "$tmp/incrbackups.txt"; @trnsfrFiles =; close FILE; $context->{"desttmp"} = $props{'stageondest'}; transferFiles (@trnsfrFiles); $context -> {"desttmp"} = $props{'desttmpdir'};
}
Unlink ($traceFile);
PrintMessage ("Done backing up incrementals");
exit;}sub checkInArray
{ my $element = $_[0];if (@rolltbsArray)
{if ($tbsHash{"$element"}) { return 1; } else { return 0; }
}
else {return 1;
}
}sub rmconvertedincr
{ # Check if any failure occured and stop exection checkErrFile();my $backupondest = $props{'backupondest'};
my $asmhome = $props{'asm_home'}; my $asmsid = $props{'asm_sid'}; my $rmcmd = "";if (defined $props{'asm_home'})
{## Lets first delete the incremental backup if any ## this will happen through asmcmd my $oh_saved = $ENV{'ORACLE_HOME'}; my $ohsid_saved = $ENV{'ORACLE_SID'}; my $asmcmd = 0 ; my $asmcmd_str; $ENV{'ORACLE_HOME'} = $asmhome; $ENV{'ORACLE_SID'} = $asmsid; # Remove trailing spaces $backupondest =~ s/\s+$//; $asmsid =~ s/\s+$//; $asmcmd_str = "asmcmd rm $backupondest/$xtts_incr_backup"; debug $asmcmd_str . " $asmhome .. $asmsid \n" ; $asmcmd = `asmcmd rm $backupondest/$xtts_incr_backup`; debug "ASMCMD: $asmcmd\n"; $ENV{'ORACLE_HOME'} = $oh_saved; $ENV{'ORACLE_SID'} = $ohsid_saved; if ($asmcmd == 0) { Unlink ("$backupondest/$xtts_incr_backup", 1); }
}
else {Unlink ("$backupondest/$xtts_incr_backup", 1);
}
}
sub ChecktoProceed
{ my $parallel = $_[0]; # Check if any failure occured and stop exection checkErrFile(); my $running = 0;if ($#forkArray <= 0)
{return;
}
do {$running = 0; foreach my $index (0 .. $#forkArray) { my $x = $forkArray[$index]; if ($x <= 0) { next; } my $kid = waitpid($x, WNOHANG); if ($kid >= 0) { $running = $running + 1; } else { $forkArray[$index] = 0; } }
} while ($running >= $parallel);
return;
}sub UpdateForkArray
{ my ($pid, $parallel) = @_;if ($#forkArray < $parallel)
{push (@forkArray, $pid);
}
else {foreach my $index (0 .. $#forkArray) { my $x = $forkArray[$index]; if ($x == 0) { $forkArray[$index] = $pid; last; } }
}
}sub FixCnvScripts
{ my $toFixSql = $_[0]; my $inpSql = $_[1]; my $fixedStr = $_[2]; my $platformid = $props{'platformid'}; my $fixedStrL = ""; my $len = 0;open XTTS_INCR_BACKUP, "<$inpSql";
open XTTS_INCR_BACKUP1, ">$toFixSql"; my @arrayXttincr = ;# Bug 20192155: Use xib instead of "xtts_incr_backup" to prevent
# ORA error ORA-15126 with ASM $fixedStrL = substr($fixedStr, 0, 42); $xtts_incr_backup = "xib"."_".$fixedStrL; $len = length ($xtts_incr_backup); if ($len > 50) {ErrorMessage ("Length of backupiece exceeds 50 chars $xtts_incr_backup");
}
foreach my $x (@arrayXttincr)
{chomp($x); if ($x =~ m/(.*)xtts_incr_backup(.*)\,/) { print XTTS_INCR_BACKUP1 "$1$xtts_incr_backup$2,\n"; } elsif ($x =~ m/(.*)xtts_incr_backup(.*)/) { print XTTS_INCR_BACKUP1 "$1$xtts_incr_backup$2\n"; } elsif (!(($platformid == 0) && ($x =~ m/(.*)pltfrmfr(.*)/))) { print XTTS_INCR_BACKUP1 "$x\n"; } else { print XTTS_INCR_BACKUP1 "$x\n"; }
}
close XTTS_INCR_BACKUP1; close XTTS_INCR_BACKUP;}sub sortArrayOrder
{ return sort {(($a =~ /::(.*):::.*/)[0] <=> ($b =~ /::(.*):::.*/)[0] || ($a =~ /:::(.*?)=/)[0] <=> ($b =~ /:::(.*?)=/)[0] ) } @_;
}
sub ConvertBackup
{ my $backup = $_[0]; my $dfno = $_[1]; my $fixCnvSql; my $outputCnvrt;$fixCnvSql = "$tmp/xxttconv_$backup"."_$dfno.sql";
FixCnvScripts ($fixCnvSql, $cnvrSql, $backup."_".$dfno);
Unlink ("$backupondest/$xtts_incr_backup", 1);my $outputCnvrt =
`sqlplus -L -s \"$connectstringcnvinst\" \@$fixCnvSql $stageondest/$backup $backupondest $platformid`;
checkError ("$fixCnvSql execution failed", $outputCnvrt);
Unlink ($fixCnvSql);}sub RollPiece
{ my $sqlfile = $_[0]; my $outputCnvrt;## now call generated rollforward
$outputCnvrt =`sqlplus -L -s \"/ as sysdba\" \@$sqlfile`;
checkError ("$sqlfile execution failed", $outputCnvrt);
}sub ConvertRoll
{ my ($fixedStr, $oldre, $rb, $rend, $bkupList, $rmList) = @_; my $rollFile;$rollFile = "$tmp/xxttroll_$fixedStr.sql";
open (XTTROLL, ">$rollFile") or Die $!;
print XTTROLL $rb;foreach my $y (@{$rmList})
{print XTTROLL $y;
}
foreach my $backup (@{$bkupList})
{my $re = $oldre; ConvertBackup ($backup, $fixedStr); $re =~ s/##xtts_incr_backup/$xtts_incr_backup/; print XTTROLL $re;
}
print XTTROLL $rend; close XTTROLL;RollPiece ($rollFile);
rmconvertedincr();
}sub RestoreRecover
{ my ($fixedStr, $finalRes) = @_; my $cmdfile = "$tmp/xttresrec_$fixedStr.cmd";open RMANRE, ">$cmdfile";
print RMANRE "$finalResn"; close RMANRE;my ($rmanTrace, $traceFile) = GetRMANTrace("resrec");
my $output =rman target \/ $rmanTrace cmdfile $cmdfile
; debug ($output); if ($output =~ /ERROR MESSAGE/)
{Die("$output $!");
}
Unlink ($traceFile);
}sub clearUpRollfwd
{ Unlink ($fixCnvSql); Unlink ($xttrollforwardp); Unlink ($fixRollSql); rmconvertedincr();}sub rollforward()
{ PrintMessage ("Start rollforward");# Check if any failure occured and stop exection
checkErrFile();my $connectstringdest = "/ as sysdba";
my $ohcnv = $props{'cnvinst_home'}; my $ohcnvsid = $props{'cnvinst_sid'}; my $tsbkupmappath = "$tmp/tsbkupmap.txt"; my $tsn; my $fixedSql; my $pid = 0; my $i = 0; my $parent = 0; my $count = 0; my $newrm; my $oldre;my $rb = q(
set serveroutput on;
DECLARE
outhandle varchar2(512) ;outtag varchar2(30) ;
done boolean ; failover boolean ; devtype VARCHAR2(512);BEGIN
DBMS_OUTPUT.put_line('Entering RollForward');
-- Now the rolling forward.
devtype := sys.dbms_backup_restore.deviceAllocate;sys.dbms_backup_restore.applySetDatafile(
check_logical => FALSE, cleanup => FALSE) ;DBMS_OUTPUT.put_line('After applySetDataFile');
);
my $rm = q(
sys.dbms_backup_restore.applyDatafileTo(
dfnumber => ##fno, toname => ##fname, fuzziness_hint => 0, max_corrupt => 0, islevel0 => 0, recid => 0, stamp => 0);
);
my $rmend = q(
DBMS_OUTPUT.put_line('Done: applyDataFileTo'); );my $re = q(
DBMS_OUTPUT.put_line('Done: applyDataFileTo');-- Restore Set Piece
sys.dbms_backup_restore.restoreSetPiece(handle => '##backupondest/##xtts_incr_backup',tag => null, fromdisk => true, recid => 0, stamp => 0) ;
DBMS_OUTPUT.put_line('Done: RestoreSetPiece');
-- Restore Backup Piece
sys.dbms_backup_restore.restoreBackupPiece(done => done, params => null, outhandle => outhandle,outtag => outtag, failover => failover);
DBMS_OUTPUT.put_line('Done: RestoreBackupPiece');
);
my $rend = q(
sys.dbms_backup_restore.restoreCancel(TRUE); sys.dbms_backup_restore.deviceDeallocate;END;
/ exit);
$stageondest = $props{'stageondest'};
$backupondest = $props{'backupondest'}; $platformid = $props{'platformid'};$re =~ s/##backupondest/$backupondest/;
$oldre = $re;if (!defined($ohcnv))
{$connectstringcnvinst = "/ as sysdba";
}
else {debug "convert instance: $ohcnv \n"; debug "convert instance: $ohcnvsid \n"; $connectstringcnvinst = "/\@(DESCRIPTION=(ADDRESS=(PROTOCOL=BEQ)(PROGRAM=$ohcnv/bin/oracle)". "(ARGV0=oracle$ohcnvsid)(ARGS='(DESCRIPTION=(LOCAL=YES)". "(ADDRESS=(PROTOCOL=BEQ)))')". "(ENVS='ORACLE_HOME=$ohcnv,ORACLE_SID=$ohcnvsid'))". "(CONNECT_DATA=(SID=$ohcnvsid))) as sysdba";
}
my %tsbkupmap;
my @tsArray = ();open my $in, $tsbkupmappath or Die ("$tsbkupmappath $!");
@tsArray = <$in>; close $in;foreach my $x (sortArrayOrder @tsArray)
{$_ = $x; next if /^#/; if (m/(\S+):::.*?=(\S+)/g) { push (@{$tsbkupmap{$1}}, $2); }
}
##
## Putting database on target in nomount ## as it could cause the following error. ## ORA-00600: internal error code, arguments: [2130], [33], [32], [4] ## The reason for this is : ## ## The code is trying to access KCCDEDBF (datafile) record# 33 when there ## is only record# 32. It looks when you try to apply the incremental ## backup on linux box, you are expecting the datafile# 33 to exists in the ## target database. However, that can't be always true. ## ## This is worked around by starting the target database iin nomount. ## The convert call/script should work in nomount state of database too. ## if (defined($context->{"rolltbs"})) {checkDBState();
}
debug3 ("ROLLFORWARD: Starting DB in nomount mode");
my $outputstart = sqlplus -L -s \"/ as sysdba\" \@xttstartupnomount.sql
;
## Generate xttrollforwarddest.sql that is the script
## that will rollforward all the converted datafiles. my $tsbkcount = scalar keys %tsbkupmap;if ($tsbkcount == 0)
{Die ("No tablepsace entries found");
}
my @sortedkeys = sort keys %tsbkupmap;
foreach $tsn (@sortedkeys)
{my ($ts, $rdfno, $order) = split(/::/, $tsn); my $fixedStr; if ($context->{"rolltbs"}) { my $checkArray = checkInArray($ts); if ($checkArray == 0) { next; } } debug "rdfno " . $rdfno . "\n"; my @rdfnos = split /,/, $rdfno; open (ROLLPLAN, "$tmp/xttnewdatafiles.txt") or Die $!; my $scrape = 0; # Check if any failure occured and stop exection checkErrFile(); debug "BEFORE ROLLPLAN\n"; my @rmList = (); while () { if ($scrape == 1 && $_ !~ /::/) { chop; my $oldrm = $rm; my $dfstr = $_; my ($dfno, $dfname) = split /,/, $dfstr; my $ind; ## include the dfno only if present in the tsbkupmap.txt list ## of dfnos. my $found = 0; for ($ind = 0; $ind <= $#rdfnos ; $ind++) { if ($dfno == $rdfnos[$ind]) { $found = 1; last; } } if ($found == 1) { debug "datafile number : $dfno \n"; debug "datafile name : $dfname\n"; $newrm = $rm; $newrm =~ s/##fno/$dfno/; $newrm =~ s/##fname/'$dfname'/; push (@rmList, $newrm); } } if (/^::$ts$/) { $scrape = 1; } elsif (/^::/) { $scrape = 0; } } push (@rmList, $rmend); close(ROLLPLAN); debug "AFTER ROLLPLAN\n"; $rollParallel = $props{'rollparallel'}; $fixedStr = $rdfno; $fixedStr =~ tr/,/_/; # Bug 20192155: Use xib instead of "xtts_incr_backup" to prevent # ORA error ORA-15126 with ASM my $fixedStrL = substr($fixedStr, 0, 25); if ($rollParallel) { ChecktoProceed($rollParallel); $pid = fork(); if ($pid == 0) { ConvertRoll ($fixedStrL, $oldre, $rb ,$rend, \@{$tsbkupmap{$tsn}}, \@rmList); exit (0); } else { UpdateForkArray ($pid, $rollParallel); } } else { ConvertRoll ($fixedStrL, $oldre, $rb ,$rend, \@{$tsbkupmap{$tsn}}, \@rmList); }
}
while((my $pid = wait()) > 0)
{#sleep (1);
}
# Check if any failure occured and stop exection
checkErrFile();my $outputstart = sqlplus -L -s \"/ as sysdba\" \@xttdbopen.sql
;
PrintMessage ("End of rollforward phase");
exit;
}sub resrecBkp
{ my $option = $_[0];PrintMessage ("Start restore/recover");
# Check if any failure occured and stop exection
checkErrFile();my $tsbkupmappath = "$tmp/tsbkupmap.txt";
my $tsn; my $fixedSql; my $pid = 0; my $i = 0; my $parent = 0; my $count = 0; my $newrm; my $oldre; my $platName = $context->{"platname"};$stageondest = $props{'stageondest'};
$platformid = $props{'platformid'};my %tsbkupmap;
my @tsArray = ();open my $in, $tsbkupmappath or Die ("$tsbkupmappath $!");
@tsArray = <$in>; close $in;foreach my $x (sortArrayOrder @tsArray)
{$_ = $x; next if /^#/; if (m/(\S+):::.*?=(\S+)/g) { push (@{$tsbkupmap{$1}}, $2); }
}
my $tsbkcount = scalar keys %tsbkupmap;
if ($tsbkcount == 0)
{Die ("No tablepsace entries found");
}
my @sortedkeys = sort keys %tsbkupmap;
my $res = ''; my $finalRes = '';foreach $tsn (@sortedkeys)
{my ($ts, $rdfno, $order) = split(/::/, $tsn); my $fixedStr; debug "rdfno " . $rdfno . "\n"; my @rdfnos = split /,/, $rdfno; open (ROLLPLAN, "$tmp/xttnewdatafiles.txt") or Die $!; my $scrape = 0; # Check if any failure occured and stop exection checkErrFile(); debug "BEFORE ROLLPLAN\n"; my @rmList = (); my $dfnofinal = 0; $res = ''; while () { if ($_ !~ /::/) { chop; my $dfstr = $_; my ($dfno, $dfname) = split /,/, $dfstr; my $ind; ## include the dfno only if present in the tsbkupmap.txt list ## of dfnos. my $found = 0; for ($ind = 0; $ind <= $#rdfnos ; $ind++) { if ($dfno == $rdfnos[$ind]) { $found = 1; if (($option == RESTORE) || ($option == RECOVER)) { if ($res ne '') { $res = $res.","; } if ($option == RECOVER) { $res = $res."'$dfname'"; } else { $res = $res."$dfno format '$dfname'"; } } } } } } if ($option == RESTORE) { $finalRes = "restore from platform '$platName' FOREIGN DATAFILE ". $res. " from backupset '$stageondest/@{$tsbkupmap{$tsn}}';\n"; } elsif ($option == RECOVER) { $finalRes = " recover from platform '$platName' FOREIGN DATAFILECOPY ". $res. " from backupset '$stageondest/@{$tsbkupmap{$tsn}}';\n"; } close(ROLLPLAN); debug "AFTER ROLLPLAN\n"; $rollParallel = $props{'rollparallel'}; $fixedStr = $rdfno; $fixedStr =~ tr/,/_/; if ($rollParallel) { ChecktoProceed($rollParallel); $pid = fork(); if ($pid == 0) { RestoreRecover ($fixedStr, $finalRes); exit (0); } else { UpdateForkArray ($pid, $rollParallel); } } else { RestoreRecover ($fixedStr, $finalRes); }
}
while((my $pid = wait()) > 0)
{#sleep (1);
}
# Check if any failure occured and stop exection
checkErrFile();PrintMessage ("End of restore/recover phase");
}sub createDumpFile
{ my $option = $_[0]; my $finalRes = ''; my $bkPiece = ''; my $platName = $context->{"platname"};PrintMessage ("Start creating dumpfile");
# Check if any failure occured and stop exection
checkErrFile();my $tsbkupmappath = "$tmp/tsbkupmap.txt";
my %tsbkupmap; my @tsArray = ();open my $in, $tsbkupmappath or Die ("$tsbkupmappath $!");
@tsArray = <$in>; close $in;foreach my $x (sortArrayOrder @tsArray)
{$_ = $x; next if /^#/; if (m/.*DMPEXP::(.*)/) { $bkPiece = $1; last; }
}
my $dmpFile = "impdp".GetTimeStamp().".dmp";
my $dumpDir;$context->{"dmpfile"} = $dmpFile;
if (defined($props{'dumpdir'}))
{$dumpDir = $props{'dumpdir'}; if (!-e $dumpDir) { $dumpDir = $tmp; }
}
else {$dumpDir = $tmp;
}
$finalRes =
" restore from platform '$platName' ". " dump file '$dmpFile' ". " datapump destination '$dumpDir' ". " from backupset '$stageondest/$bkPiece';\n";
debug "$finalResn";
open RMANRE, ">rman_createdmp.cmd";
print RMANRE "$finalResn"; close RMANRE; my ($rmanTrace, $traceFile) = GetRMANTrace("convert"); my $output =rman target \/ $rmanTrace cmdfile rman_createdmp.cmd
; debug ($output); if ($output =~ /ERROR MESSAGE/)
{Die("$output $!");
}
Unlink ($traceFile);
# Check if any failure occured and stop exection
checkErrFile();PrintMessage ("End of creating dumpfile");
}sub newplan()
{ # Check if any failure occured and stop exection checkErrFile();## Move the current xttplan.txt.new as xttplan.txt
## The xttplan.txt.new gets generated during backincr() my $xttplanpath = "$tmp/xttplan.txt"; my $xttplanpathnew = "$tmp/xttplan.txt.new";if ( -e $xttplanpath )
{my $timenow = time; system("\\mv $xttplanpath $xttplanpath" . $timenow); system("\\mv $xttplanpathnew $xttplanpath");
}
## Checks that no tablespace went read only
## or datafiles offline &generate_batch_tsoutput(NEWPLAN);print "New $tmp/xttplan.txt with FROM SCN's generatedn";
exit;}sub convert()
{ PrintMessage ("Performing convert"); # Check if any failure occured and stop exection checkErrFile();my ($rmanTrace, $traceFile) = GetRMANTrace("convert");
my $output =rman target \/ $rmanTrace cmdfile $rmanpath
; if ($output =~ /ERROR MESSAGE/)
{Die("$output $!");
}
Unlink ($traceFile);my @lines = split /n/, $output;
my $xttnewdata = "$tmp/xttnewdatafiles.txt";
if ( -e $xttnewdata )
{my $timenow = time; system("\\mv $xttnewdata $xttnewdata" . $timenow);
}
open(XTTNEW, ">$xttnewdata") || Die("Cant open xttnewdatafiles.txt");
foreach my $line (@lines)
{my $tsname; my $filno; $_ = $line; if (/converted datafile=(.+)\/(\w+)[_](\d+)\.xtf/) { print XTTNEW "$3,"; $line =~ s/.*converted datafile=//; print XTTNEW "$line\n"; } elsif(/^ts(\S+)/) { print XTTNEW $1 . "\n"; $tsname = $1; }
}
close(XTTNEW);
PrintMessage ("Converted datafiles listed in: $xttnewdata");
exit;
}sub genConvertDFNames
{ my @convertArray = ();# Check if any failure occured and stop exection
checkErrFile();open(RMANCONVERT, "$rmanpath") ||
die 'Cant find rmanconvert.cmd, TMPDIR undefined';
@convertArray = ;
close RMANCONVERT;open(RMANDSTDF, ">$rmandstdf") ||
Die "Cant find $rmandstdf";
foreach my $x (@convertArray)
{chomp($x); $x = trim($x); if ($x =~ /'(.*)\.tf'/) { $x = $1.".tf"; if ($x =~ /.*(.+)\/(\w+)[_](\d+)\.tf/) { print RMANDSTDF "::$2\n"; print RMANDSTDF "$3,$x\n"; } }
}
close RMANDSTDF;
exit;
}sub plugin()
{ my $option = $_[0];PrintMessage ("Generating plugin");
# Check if any failure occured and stop exection checkErrFile();my $xttplugin = "$tmp/xttplugin.txt";
my $tts = "transport_tablespaces="; my $tdf = "transport_datafiles="; my $comma = 0; my $xttnewdata = "$tmp/xttnewdatafiles.txt"; my $command_str; my $dmpFile = $context->{"dmpfile"};if ( -e $xttplugin )
{my $timenow = time; system("\\mv $xttplugin $xttplugin" . $timenow);
}
open(XTTPLUG, ">$xttplugin") || Die("Unable to open file $xttplugin");
if ($option == LINK)
{$command_str = "impdp directory=logfile= \\" . "\n" . "network_link= transport_full_check=no \\" . "\n" ;
}
else {$command_str = "impdp directory=logfile= \\" . "\n" . "dumpfile=$dmpFile \\" . "\n" ;
}
print XTTPLUG $command_str;
if ($option == LINK)
{print XTTPLUG $tts; open(XTTPLAN, $xttpath) || Die("Cant find xttplan.txt\n $!"); while () { if ($comma == 1 && /::::/) { print XTTPLUG ","; $comma = 0; } if (/(\S+)::::(\S+)/) { print XTTPLUG $1; $comma = 1; } } close(XTTPLAN); print XTTPLUG " \\\n";
}
print XTTPLUG $tdf; $comma = 0;open(XTTNEWDATA, $xttnewdata) || Die("Cant find xttnewdatafiles.txt\n $!");
while ()
{if ($comma == 1) { print XTTPLUG ","; $comma = 0; } if (! /^::\S+/) { chop; my ($dfno, $dfname) = split /,/, $_; print XTTPLUG "'" . $dfname . "'"; $comma = 1; }
}
print XTTPLUG "n";
close(XTTNEWDATA);
close(XTTPLUG);PrintMessage ("Done generating plugin file $xttplugin");
exit;}sub usage
{ print STDERR << "EOF";This program prepares, backsup and rollsforward tablespaces
for cross-platform transportable tablespaces.usage: $0 {[--backup|-b] || [--bkpincr|-B] || [--bkpexport/E] [--resincrdmp|M] [--convert/-c] || [--generate|-e] || [--incremental|-i] || [[--prepare|-p] || [--getfile|-G]] || [--restore|R] || [--recover|X] [--rollforward|-r [--rolltbs|-T] || [--determinescn|-s] || [--orasid/O] || [--orahome|-o]] [--help|-h]} Additional options ------------------ [--debug|d] [--clearerrorfile|-C] [--xttdir|Dir ] [-F/--propfile] [-I/--propdir] -b : For 12c and above, generate transportable backups -B : For 12c and above, generate level 1 transportable backups -c : conversion of datafiles -M : create the dump file from the generated backup -e : generate impdp script: export over new link -i : incremental backup -p : prepare -G : get datafiles from source database using get_file, should not be used together with -p -r : roll forward datafiles -s : new from_scn values into xttplan.txt -R : For 12c restore the datafiles from the backups -X : For 12c recover the datafiles from the backups -T : roll forward specific tablespace(s) -h : this (help) message (Default) -d : provides more debug information, also rman is called with debug option so that tracing is better. -L : delete the ERROR FILE and proceed with the execution -D : Instead of defining environement variable, user can pass tmpdir through xttdir -O : Use this option to pass ORACLE_SID to override the environment variable -o : Use this option to pass ORACLE_HOME to override the environment variable -I : Use this option to mention the location from where the script will pick the properties file etc -F : Use this option to mention the location from where the script will pick the properties file.example: $0 -p $0 -i $0 -r $0 -s
EOF
exit;}sub GetTimeStamp
{my $timeStamp = $$."_".int(rand(1000));return ($timeStamp);
}
Main();
转载地址:http://osfqf.baihongyu.com/