mirror of
https://github.com/pgbackrest/pgbackrest.git
synced 2024-12-14 10:13:05 +02:00
Move restore test infrastructure to HostBackup.pm.
Required to test restores on the backup server, a fairly common scenario. Improve the restore function to accept optional parameters rather than a long list of parameters. In passing, clean up extraneous use of strType and strComment variables.
This commit is contained in:
parent
915ae5662a
commit
59fd92fdd5
@ -86,6 +86,10 @@
|
||||
|
||||
<p>Add unit tests for the <code>Manifest</code> module.</p>
|
||||
</release-item>
|
||||
|
||||
<release-item>
|
||||
<p>Move restore test infrastructure to <code>HostBackup.pm</code>. Required to test restores on the backup server, a fairly common scenario. Improve the restore function to accept optional parameters rather than a long list of parameters. In passing, clean up extraneous use of <code>strType</code> and <code>strComment</code> variables.</p>
|
||||
</release-item>
|
||||
</release-development-list>
|
||||
</release-test-list>
|
||||
</release>
|
||||
|
@ -13,6 +13,7 @@ use Carp qw(confess);
|
||||
|
||||
use Exporter qw(import);
|
||||
our @EXPORT = qw();
|
||||
use Fcntl ':mode';
|
||||
use File::Basename qw(dirname);
|
||||
use Storable qw(dclone);
|
||||
|
||||
@ -23,6 +24,7 @@ use pgBackRest::Common::Exception;
|
||||
use pgBackRest::Common::Ini;
|
||||
use pgBackRest::Common::Log;
|
||||
use pgBackRest::Config::Config;
|
||||
use pgBackRest::DbVersion;
|
||||
use pgBackRest::Manifest;
|
||||
use pgBackRest::Protocol::Storage::Helper;
|
||||
use pgBackRest::Storage::Posix::Driver;
|
||||
@ -1407,6 +1409,579 @@ sub infoRestore
|
||||
return logDebugReturn($strOperation);
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# configRecovery
|
||||
####################################################################################################################################
|
||||
sub configRecovery
|
||||
{
|
||||
my $self = shift;
|
||||
my $oHostBackup = shift;
|
||||
my $oRecoveryHashRef = shift;
|
||||
|
||||
# Get stanza
|
||||
my $strStanza = $self->stanza();
|
||||
|
||||
# Load db config file
|
||||
my $oConfig = iniParse(${storageTest->get($self->backrestConfig())}, {bRelaxed => true});
|
||||
|
||||
# Rewrite recovery options
|
||||
my @stryRecoveryOption;
|
||||
|
||||
foreach my $strOption (sort(keys(%$oRecoveryHashRef)))
|
||||
{
|
||||
push (@stryRecoveryOption, "${strOption}=${$oRecoveryHashRef}{$strOption}");
|
||||
}
|
||||
|
||||
if (@stryRecoveryOption)
|
||||
{
|
||||
$oConfig->{$strStanza}{cfgOptionName(CFGOPT_RECOVERY_OPTION)} = \@stryRecoveryOption;
|
||||
}
|
||||
|
||||
# Save db config file
|
||||
storageTest()->put($self->backrestConfig(), iniRender($oConfig, true));
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# configRemap
|
||||
####################################################################################################################################
|
||||
sub configRemap
|
||||
{
|
||||
my $self = shift;
|
||||
my $oRemapHashRef = shift;
|
||||
my $oManifestRef = shift;
|
||||
|
||||
# Get stanza name
|
||||
my $strStanza = $self->stanza();
|
||||
|
||||
# Load db config file
|
||||
my $oConfig = iniParse(${storageTest()->get($self->backrestConfig())}, {bRelaxed => true});
|
||||
|
||||
# Load backup config file
|
||||
my $oRemoteConfig;
|
||||
my $oHostBackup =
|
||||
!$self->standby() && !$self->nameTest($self->backupDestination()) ?
|
||||
hostGroupGet()->hostGet($self->backupDestination()) : undef;
|
||||
|
||||
if (defined($oHostBackup))
|
||||
{
|
||||
$oRemoteConfig = iniParse(${storageTest()->get($oHostBackup->backrestConfig())}, {bRelaxed => true});
|
||||
}
|
||||
|
||||
# Rewrite recovery section
|
||||
delete($oConfig->{"${strStanza}:restore"}{cfgOptionName(CFGOPT_TABLESPACE_MAP)});
|
||||
my @stryTablespaceMap;
|
||||
|
||||
foreach my $strRemap (sort(keys(%$oRemapHashRef)))
|
||||
{
|
||||
my $strRemapPath = ${$oRemapHashRef}{$strRemap};
|
||||
|
||||
if ($strRemap eq MANIFEST_TARGET_PGDATA)
|
||||
{
|
||||
$oConfig->{$strStanza}{$self->optionIndexName(CFGOPT_DB_PATH, 1)} = $strRemapPath;
|
||||
|
||||
${$oManifestRef}{&MANIFEST_SECTION_BACKUP_TARGET}{&MANIFEST_TARGET_PGDATA}{&MANIFEST_SUBKEY_PATH} = $strRemapPath;
|
||||
|
||||
if (defined($oHostBackup))
|
||||
{
|
||||
my $bForce = $oHostBackup->nameTest(HOST_BACKUP) && defined(hostGroupGet()->hostGet(HOST_DB_STANDBY, true));
|
||||
$oRemoteConfig->{$strStanza}{$self->optionIndexName(CFGOPT_DB_PATH, 1, $bForce)} = $strRemapPath;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
my $strTablespaceOid = (split('\/', $strRemap))[1];
|
||||
push (@stryTablespaceMap, "${strTablespaceOid}=${strRemapPath}");
|
||||
|
||||
${$oManifestRef}{&MANIFEST_SECTION_BACKUP_TARGET}{$strRemap}{&MANIFEST_SUBKEY_PATH} = $strRemapPath;
|
||||
${$oManifestRef}{&MANIFEST_SECTION_TARGET_LINK}{MANIFEST_TARGET_PGDATA . "/${strRemap}"}{destination} = $strRemapPath;
|
||||
}
|
||||
}
|
||||
|
||||
if (@stryTablespaceMap)
|
||||
{
|
||||
$oConfig->{"${strStanza}:restore"}{cfgOptionName(CFGOPT_TABLESPACE_MAP)} = \@stryTablespaceMap;
|
||||
}
|
||||
|
||||
# Save db config file
|
||||
storageTest()->put($self->backrestConfig(), iniRender($oConfig, true));
|
||||
|
||||
# Save backup config file (but not if this is the standby which is not the source of backups)
|
||||
if (defined($oHostBackup))
|
||||
{
|
||||
# Modify the file permissions so it can be read/saved by all test users
|
||||
executeTest(
|
||||
'sudo chmod 660 ' . $oHostBackup->backrestConfig() . ' && sudo chmod 770 ' . dirname($oHostBackup->backrestConfig()));
|
||||
|
||||
storageTest()->put($oHostBackup->backrestConfig(), iniRender($oRemoteConfig, true));
|
||||
|
||||
# Fix permissions
|
||||
executeTest(
|
||||
'sudo chmod 660 ' . $oHostBackup->backrestConfig() . ' && sudo chmod 770 ' . dirname($oHostBackup->backrestConfig()) .
|
||||
' && sudo chown ' . $oHostBackup->userGet() . ' ' . $oHostBackup->backrestConfig());
|
||||
}
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# restore
|
||||
####################################################################################################################################
|
||||
sub restore
|
||||
{
|
||||
my $self = shift;
|
||||
|
||||
# Assign function parameters, defaults, and log debug info
|
||||
my
|
||||
(
|
||||
$strOperation,
|
||||
$strComment,
|
||||
$strBackup,
|
||||
$rhExpectedManifest,
|
||||
$rhRemapHash,
|
||||
$bDelta,
|
||||
$bForce,
|
||||
$strType,
|
||||
$strTarget,
|
||||
$bTargetExclusive,
|
||||
$strTargetAction,
|
||||
$strTargetTimeline,
|
||||
$rhRecoveryHash,
|
||||
$iExpectedExitStatus,
|
||||
$strOptionalParam,
|
||||
$bTablespace,
|
||||
$strUser,
|
||||
) =
|
||||
logDebugParam
|
||||
(
|
||||
__PACKAGE__ . '->restore', \@_,
|
||||
{name => 'strComment', required => false},
|
||||
{name => 'strBackup'},
|
||||
{name => 'rhExpectedManifest', optional => true},
|
||||
{name => 'rhRemapHash', optional => true},
|
||||
{name => 'bDelta', optional => true, default => false},
|
||||
{name => 'bForce', optional => true, default => false},
|
||||
{name => 'strType', optional => true},
|
||||
{name => 'strTarget', optional => true},
|
||||
{name => 'bTargetExclusive', optional => true, default => false},
|
||||
{name => 'strTargetAction', optional => true},
|
||||
{name => 'strTargetTimeline', optional => true},
|
||||
{name => 'rhRecoveryHash', optional => true},
|
||||
{name => 'iExpectedExitStatus', optional => true},
|
||||
{name => 'strOptionalParam', optional => true},
|
||||
{name => 'bTablespace', optional => true},
|
||||
{name => 'strUser', optional => true},
|
||||
);
|
||||
|
||||
# Build link map options
|
||||
my $strLinkMap;
|
||||
|
||||
foreach my $strTarget (sort(keys(%{$self->{hLinkRemap}})))
|
||||
{
|
||||
$strLinkMap .= " --link-map=\"${strTarget}=${$self->{hLinkRemap}}{$strTarget}\"";
|
||||
}
|
||||
|
||||
$strComment = 'restore' .
|
||||
($bDelta ? ' delta' : '') .
|
||||
($bForce ? ', force' : '') .
|
||||
($strBackup ne cfgDefOptionDefault(CFGCMD_RESTORE, CFGOPT_SET) ? ", backup '${strBackup}'" : '') .
|
||||
# This does not output 'default' for synthetic tests to make expect logs match up (may change later)
|
||||
($strType ? ", type '${strType}'" : (defined($rhExpectedManifest) ? '' : ", type 'default'")) .
|
||||
($strTarget ? ", target '${strTarget}'" : '') .
|
||||
($strTargetTimeline ? ", timeline '${strTargetTimeline}'" : '') .
|
||||
($bTargetExclusive ? ', exclusive' : '') .
|
||||
(defined($strTargetAction) && $strTargetAction ne cfgDefOptionDefault(CFGCMD_RESTORE, CFGOPT_TARGET_ACTION)
|
||||
? ', ' . cfgOptionName(CFGOPT_TARGET_ACTION) . "=${strTargetAction}" : '') .
|
||||
(defined($rhRemapHash) ? ', remap' : '') .
|
||||
(defined($iExpectedExitStatus) ? ", expect exit ${iExpectedExitStatus}" : '') .
|
||||
(defined($strComment) ? " - ${strComment}" : '') .
|
||||
' (' . $self->nameGet() . ' host)';
|
||||
&log(INFO, " ${strComment}");
|
||||
|
||||
# Get the backup host
|
||||
my $oHostGroup = hostGroupGet();
|
||||
my $oHostBackup = defined($oHostGroup->hostGet(HOST_BACKUP, true)) ? $oHostGroup->hostGet(HOST_BACKUP) : $self;
|
||||
|
||||
# Load the expected manifest if it was not defined
|
||||
my $oExpectedManifest = undef;
|
||||
|
||||
if (!defined($rhExpectedManifest))
|
||||
{
|
||||
# Load the manifest
|
||||
my $oExpectedManifest = new pgBackRest::Manifest(
|
||||
storageRepo()->pathGet(
|
||||
STORAGE_REPO_BACKUP . qw{/} . ($strBackup eq 'latest' ? $oHostBackup->backupLast() : $strBackup) . qw{/} .
|
||||
FILE_MANIFEST),
|
||||
{strCipherPass => $oHostBackup->cipherPassManifest()});
|
||||
|
||||
$rhExpectedManifest = $oExpectedManifest->{oContent};
|
||||
|
||||
# Remap links in the expected manifest
|
||||
foreach my $strTarget (sort(keys(%{$self->{hLinkRemap}})))
|
||||
{
|
||||
my $strDestination = ${$self->{hLinkRemap}}{$strTarget};
|
||||
my $strTarget = 'pg_data/' . $strTarget;
|
||||
my $strTargetPath = $strDestination;
|
||||
|
||||
# If this link is to a file then the specified path must be split into file and path parts
|
||||
if ($oExpectedManifest->isTargetFile($strTarget))
|
||||
{
|
||||
$strTargetPath = dirname($strTargetPath);
|
||||
|
||||
# Error when the path is not deep enough to be valid
|
||||
if (!defined($strTargetPath))
|
||||
{
|
||||
confess &log(ERROR, "${strDestination} is not long enough to be target for ${strTarget}");
|
||||
}
|
||||
|
||||
# Set the file part
|
||||
$oExpectedManifest->set(
|
||||
MANIFEST_SECTION_BACKUP_TARGET, $strTarget, MANIFEST_SUBKEY_FILE,
|
||||
substr($strDestination, length($strTargetPath) + 1));
|
||||
|
||||
# Set the link target
|
||||
$oExpectedManifest->set(
|
||||
MANIFEST_SECTION_TARGET_LINK, $strTarget, MANIFEST_SUBKEY_DESTINATION, $strDestination);
|
||||
}
|
||||
else
|
||||
{
|
||||
# Set the link target
|
||||
$oExpectedManifest->set(MANIFEST_SECTION_TARGET_LINK, $strTarget, MANIFEST_SUBKEY_DESTINATION, $strTargetPath);
|
||||
}
|
||||
|
||||
# Set the target path
|
||||
$oExpectedManifest->set(MANIFEST_SECTION_BACKUP_TARGET, $strTarget, MANIFEST_SUBKEY_PATH, $strTargetPath);
|
||||
}
|
||||
}
|
||||
|
||||
# Get the backup host
|
||||
if (defined($rhRemapHash))
|
||||
{
|
||||
$self->configRemap($rhRemapHash, $rhExpectedManifest);
|
||||
}
|
||||
|
||||
if (defined($rhRecoveryHash))
|
||||
{
|
||||
$self->configRecovery($oHostBackup, $rhRecoveryHash);
|
||||
}
|
||||
|
||||
# Create the restore command
|
||||
$self->executeSimple(
|
||||
$self->backrestExe() .
|
||||
' --config=' . $self->backrestConfig() .
|
||||
($bDelta ? ' --delta' : '') .
|
||||
($bForce ? ' --force' : '') .
|
||||
($strBackup ne cfgDefOptionDefault(CFGCMD_RESTORE, CFGOPT_SET) ? " --set=${strBackup}" : '') .
|
||||
(defined($strOptionalParam) ? " ${strOptionalParam} " : '') .
|
||||
(defined($strType) && $strType ne CFGOPTVAL_RESTORE_TYPE_DEFAULT ? " --type=${strType}" : '') .
|
||||
(defined($strTarget) ? " --target=\"${strTarget}\"" : '') .
|
||||
(defined($strTargetTimeline) ? " --target-timeline=\"${strTargetTimeline}\"" : '') .
|
||||
($bTargetExclusive ? ' --target-exclusive' : '') .
|
||||
(defined($strLinkMap) ? $strLinkMap : '') .
|
||||
($self->synthetic() ? '' : ' --link-all') .
|
||||
(defined($strTargetAction) && $strTargetAction ne cfgDefOptionDefault(CFGCMD_RESTORE, CFGOPT_TARGET_ACTION)
|
||||
? ' --' . cfgOptionName(CFGOPT_TARGET_ACTION) . "=${strTargetAction}" : '') .
|
||||
' --stanza=' . $self->stanza() . ' restore',
|
||||
{strComment => $strComment, iExpectedExitStatus => $iExpectedExitStatus, oLogTest => $self->{oLogTest},
|
||||
bLogOutput => $self->synthetic()},
|
||||
$strUser);
|
||||
|
||||
if (!defined($iExpectedExitStatus))
|
||||
{
|
||||
$self->restoreCompare($strBackup, dclone($rhExpectedManifest), $bTablespace);
|
||||
|
||||
if (defined($self->{oLogTest}))
|
||||
{
|
||||
$self->{oLogTest}->supplementalAdd(
|
||||
$rhExpectedManifest->{&MANIFEST_SECTION_BACKUP_TARGET}{&MANIFEST_TARGET_PGDATA}{&MANIFEST_SUBKEY_PATH} .
|
||||
"/recovery.conf");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# restoreCompare
|
||||
####################################################################################################################################
|
||||
sub restoreCompare
|
||||
{
|
||||
my $self = shift;
|
||||
my $strBackup = shift;
|
||||
my $oExpectedManifestRef = shift;
|
||||
my $bTablespace = shift;
|
||||
|
||||
my $strTestPath = $self->testPath();
|
||||
|
||||
# Get the backup host
|
||||
my $oHostGroup = hostGroupGet();
|
||||
my $oHostBackup = defined($oHostGroup->hostGet(HOST_BACKUP, true)) ? $oHostGroup->hostGet(HOST_BACKUP) : $self;
|
||||
|
||||
# Load the last manifest if it exists
|
||||
my $oLastManifest = undef;
|
||||
|
||||
if (defined(${$oExpectedManifestRef}{&MANIFEST_SECTION_BACKUP}{&MANIFEST_KEY_PRIOR}))
|
||||
{
|
||||
my $oExpectedManifest =
|
||||
new pgBackRest::Manifest(
|
||||
storageRepo()->pathGet(
|
||||
STORAGE_REPO_BACKUP . qw{/} . ($strBackup eq 'latest' ? $oHostBackup->backupLast() : $strBackup) .
|
||||
'/'. FILE_MANIFEST),
|
||||
{strCipherPass => $oHostBackup->cipherPassManifest()});
|
||||
|
||||
$oLastManifest =
|
||||
new pgBackRest::Manifest(
|
||||
storageRepo()->pathGet(
|
||||
STORAGE_REPO_BACKUP . qw{/} .
|
||||
${$oExpectedManifestRef}{&MANIFEST_SECTION_BACKUP}{&MANIFEST_KEY_PRIOR} . qw{/} . FILE_MANIFEST),
|
||||
{strCipherPass => $oHostBackup->cipherPassManifest()});
|
||||
}
|
||||
|
||||
# Generate the tablespace map for real backups
|
||||
my $oTablespaceMap = undef;
|
||||
|
||||
if (!$self->synthetic())
|
||||
{
|
||||
# Tablespace_map file is not restored in versions >= 9.5 because it interferes with internal remapping features.
|
||||
if (${$oExpectedManifestRef}{&MANIFEST_SECTION_BACKUP_DB}{&MANIFEST_KEY_DB_VERSION} >= PG_VERSION_95)
|
||||
{
|
||||
delete(${$oExpectedManifestRef}{&MANIFEST_SECTION_TARGET_FILE}{MANIFEST_TARGET_PGDATA . '/tablespace_map'});
|
||||
}
|
||||
|
||||
foreach my $strTarget (keys(%{${$oExpectedManifestRef}{&MANIFEST_SECTION_BACKUP_TARGET}}))
|
||||
{
|
||||
if (defined(${$oExpectedManifestRef}{&MANIFEST_SECTION_BACKUP_TARGET}{$strTarget}{&MANIFEST_SUBKEY_TABLESPACE_ID}))
|
||||
{
|
||||
my $iTablespaceId =
|
||||
${$oExpectedManifestRef}{&MANIFEST_SECTION_BACKUP_TARGET}{$strTarget}{&MANIFEST_SUBKEY_TABLESPACE_ID};
|
||||
|
||||
$$oTablespaceMap{$iTablespaceId} =
|
||||
${$oExpectedManifestRef}{&MANIFEST_SECTION_BACKUP_TARGET}{$strTarget}{&MANIFEST_SUBKEY_TABLESPACE_NAME};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Generate the actual manifest
|
||||
my $strDbClusterPath =
|
||||
${$oExpectedManifestRef}{&MANIFEST_SECTION_BACKUP_TARGET}{&MANIFEST_TARGET_PGDATA}{&MANIFEST_SUBKEY_PATH};
|
||||
|
||||
if (defined($bTablespace) && !$bTablespace)
|
||||
{
|
||||
foreach my $strTarget (keys(%{${$oExpectedManifestRef}{&MANIFEST_SECTION_BACKUP_TARGET}}))
|
||||
{
|
||||
if ($$oExpectedManifestRef{&MANIFEST_SECTION_BACKUP_TARGET}{$strTarget}{&MANIFEST_SUBKEY_TYPE} eq
|
||||
MANIFEST_VALUE_LINK &&
|
||||
defined($$oExpectedManifestRef{&MANIFEST_SECTION_BACKUP_TARGET}{$strTarget}{&MANIFEST_SUBKEY_TABLESPACE_ID}))
|
||||
{
|
||||
my $strRemapPath;
|
||||
my $iTablespaceName =
|
||||
$$oExpectedManifestRef{&MANIFEST_SECTION_BACKUP_TARGET}{$strTarget}{&MANIFEST_SUBKEY_TABLESPACE_NAME};
|
||||
|
||||
$strRemapPath = "../../tablespace/${iTablespaceName}";
|
||||
|
||||
$$oExpectedManifestRef{&MANIFEST_SECTION_BACKUP_TARGET}{$strTarget}{&MANIFEST_SUBKEY_PATH} = $strRemapPath;
|
||||
$$oExpectedManifestRef{&MANIFEST_SECTION_TARGET_LINK}{MANIFEST_TARGET_PGDATA . "/${strTarget}"}
|
||||
{&MANIFEST_SUBKEY_DESTINATION} = $strRemapPath;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
my $oActualManifest = new pgBackRest::Manifest(
|
||||
"${strTestPath}/" . FILE_MANIFEST,
|
||||
{bLoad => false, strDbVersion => $oExpectedManifestRef->{&MANIFEST_SECTION_BACKUP_DB}{&MANIFEST_KEY_DB_VERSION},
|
||||
oStorage => storageTest()});
|
||||
|
||||
$oActualManifest->set(
|
||||
MANIFEST_SECTION_BACKUP_DB, MANIFEST_KEY_DB_VERSION, undef,
|
||||
$$oExpectedManifestRef{&MANIFEST_SECTION_BACKUP_DB}{&MANIFEST_KEY_DB_VERSION});
|
||||
$oActualManifest->numericSet(
|
||||
MANIFEST_SECTION_BACKUP_DB, MANIFEST_KEY_CATALOG, undef,
|
||||
$$oExpectedManifestRef{&MANIFEST_SECTION_BACKUP_DB}{&MANIFEST_KEY_CATALOG});
|
||||
|
||||
$oActualManifest->build(storageTest(), $strDbClusterPath, $oLastManifest, false, $oTablespaceMap);
|
||||
|
||||
my $strSectionPath = $oActualManifest->get(MANIFEST_SECTION_BACKUP_TARGET, MANIFEST_TARGET_PGDATA, MANIFEST_SUBKEY_PATH);
|
||||
|
||||
foreach my $strName ($oActualManifest->keys(MANIFEST_SECTION_TARGET_FILE))
|
||||
{
|
||||
# If synthetic match checksum errors since they can't be verified here
|
||||
if ($self->synthetic)
|
||||
{
|
||||
my $bChecksumPage = $oExpectedManifestRef->{&MANIFEST_SECTION_TARGET_FILE}{$strName}{&MANIFEST_SUBKEY_CHECKSUM_PAGE};
|
||||
|
||||
if (defined($bChecksumPage))
|
||||
{
|
||||
$oActualManifest->boolSet(MANIFEST_SECTION_TARGET_FILE, $strName, MANIFEST_SUBKEY_CHECKSUM_PAGE, $bChecksumPage);
|
||||
|
||||
if (!$bChecksumPage &&
|
||||
defined($oExpectedManifestRef->{&MANIFEST_SECTION_TARGET_FILE}{$strName}{&MANIFEST_SUBKEY_CHECKSUM_PAGE_ERROR}))
|
||||
{
|
||||
$oActualManifest->set(
|
||||
MANIFEST_SECTION_TARGET_FILE, $strName, MANIFEST_SUBKEY_CHECKSUM_PAGE_ERROR,
|
||||
$oExpectedManifestRef->{&MANIFEST_SECTION_TARGET_FILE}{$strName}{&MANIFEST_SUBKEY_CHECKSUM_PAGE_ERROR});
|
||||
}
|
||||
}
|
||||
}
|
||||
# Else if page checksums are enabled make sure the correct files are being checksummed
|
||||
else
|
||||
{
|
||||
if ($oExpectedManifestRef->{&MANIFEST_SECTION_BACKUP_OPTION}{&MANIFEST_KEY_CHECKSUM_PAGE})
|
||||
{
|
||||
if (defined($oExpectedManifestRef->{&MANIFEST_SECTION_TARGET_FILE}{$strName}{&MANIFEST_SUBKEY_CHECKSUM_PAGE}) !=
|
||||
isChecksumPage($strName))
|
||||
{
|
||||
confess
|
||||
"check-page actual for ${strName} is " .
|
||||
($oActualManifest->test(MANIFEST_SECTION_TARGET_FILE, $strName,
|
||||
MANIFEST_SUBKEY_CHECKSUM_PAGE) ? 'set' : '[undef]') .
|
||||
' but isChecksumPage() says it should be ' .
|
||||
(isChecksumPage($strName) ? 'set' : '[undef]') . '.';
|
||||
}
|
||||
|
||||
# Because the page checksum flag is copied to incr and diff from the previous backup but further processing is not
|
||||
# done, they can't be expected to match so delete them.
|
||||
delete($oExpectedManifestRef->{&MANIFEST_SECTION_TARGET_FILE}{$strName}{&MANIFEST_SUBKEY_CHECKSUM_PAGE});
|
||||
$oActualManifest->remove(MANIFEST_SECTION_TARGET_FILE, $strName, MANIFEST_SUBKEY_CHECKSUM_PAGE);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$self->synthetic())
|
||||
{
|
||||
$oActualManifest->set(
|
||||
MANIFEST_SECTION_TARGET_FILE, $strName, MANIFEST_SUBKEY_SIZE,
|
||||
${$oExpectedManifestRef}{&MANIFEST_SECTION_TARGET_FILE}{$strName}{size});
|
||||
}
|
||||
|
||||
# Remove repo-size from the manifest. ??? This could be improved to get actual sizes from the backup.
|
||||
$oActualManifest->remove(MANIFEST_SECTION_TARGET_FILE, $strName, MANIFEST_SUBKEY_REPO_SIZE);
|
||||
delete($oExpectedManifestRef->{&MANIFEST_SECTION_TARGET_FILE}{$strName}{&MANIFEST_SUBKEY_REPO_SIZE});
|
||||
|
||||
if ($oActualManifest->get(MANIFEST_SECTION_TARGET_FILE, $strName, MANIFEST_SUBKEY_SIZE) != 0)
|
||||
{
|
||||
my $oStat = storageTest()->info($oActualManifest->dbPathGet($strSectionPath, $strName));
|
||||
|
||||
if ($oStat->blocks > 0 || S_ISLNK($oStat->mode))
|
||||
{
|
||||
my ($strHash) = storageTest()->hashSize($oActualManifest->dbPathGet($strSectionPath, $strName));
|
||||
|
||||
$oActualManifest->set(
|
||||
MANIFEST_SECTION_TARGET_FILE, $strName, MANIFEST_SUBKEY_CHECKSUM, $strHash);
|
||||
}
|
||||
else
|
||||
{
|
||||
$oActualManifest->remove(MANIFEST_SECTION_TARGET_FILE, $strName, MANIFEST_SUBKEY_CHECKSUM);
|
||||
delete(${$oExpectedManifestRef}{&MANIFEST_SECTION_TARGET_FILE}{$strName}{&MANIFEST_SUBKEY_CHECKSUM});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# If the link section is empty then delete it and the default section
|
||||
if (keys(%{${$oExpectedManifestRef}{&MANIFEST_SECTION_TARGET_LINK}}) == 0)
|
||||
{
|
||||
delete($$oExpectedManifestRef{&MANIFEST_SECTION_TARGET_LINK});
|
||||
delete($$oExpectedManifestRef{&MANIFEST_SECTION_TARGET_LINK . ':default'});
|
||||
}
|
||||
|
||||
# Set actual to expected for settings that always change from backup to backup
|
||||
$oActualManifest->set(MANIFEST_SECTION_BACKUP_OPTION, MANIFEST_KEY_ARCHIVE_CHECK, undef,
|
||||
${$oExpectedManifestRef}{&MANIFEST_SECTION_BACKUP_OPTION}{&MANIFEST_KEY_ARCHIVE_CHECK});
|
||||
$oActualManifest->set(MANIFEST_SECTION_BACKUP_OPTION, MANIFEST_KEY_ARCHIVE_COPY, undef,
|
||||
${$oExpectedManifestRef}{&MANIFEST_SECTION_BACKUP_OPTION}{&MANIFEST_KEY_ARCHIVE_COPY});
|
||||
$oActualManifest->set(MANIFEST_SECTION_BACKUP_OPTION, MANIFEST_KEY_BACKUP_STANDBY, undef,
|
||||
${$oExpectedManifestRef}{&MANIFEST_SECTION_BACKUP_OPTION}{&MANIFEST_KEY_BACKUP_STANDBY});
|
||||
$oActualManifest->set(MANIFEST_SECTION_BACKUP_OPTION, MANIFEST_KEY_COMPRESS, undef,
|
||||
${$oExpectedManifestRef}{&MANIFEST_SECTION_BACKUP_OPTION}{&MANIFEST_KEY_COMPRESS});
|
||||
$oActualManifest->set(MANIFEST_SECTION_BACKUP_OPTION, MANIFEST_KEY_HARDLINK, undef,
|
||||
${$oExpectedManifestRef}{&MANIFEST_SECTION_BACKUP_OPTION}{&MANIFEST_KEY_HARDLINK});
|
||||
$oActualManifest->set(MANIFEST_SECTION_BACKUP_OPTION, MANIFEST_KEY_ONLINE, undef,
|
||||
${$oExpectedManifestRef}{&MANIFEST_SECTION_BACKUP_OPTION}{&MANIFEST_KEY_ONLINE});
|
||||
|
||||
$oActualManifest->set(MANIFEST_SECTION_BACKUP_DB, MANIFEST_KEY_DB_VERSION, undef,
|
||||
${$oExpectedManifestRef}{&MANIFEST_SECTION_BACKUP_DB}{&MANIFEST_KEY_DB_VERSION});
|
||||
$oActualManifest->numericSet(MANIFEST_SECTION_BACKUP_DB, MANIFEST_KEY_CONTROL, undef,
|
||||
${$oExpectedManifestRef}{&MANIFEST_SECTION_BACKUP_DB}{&MANIFEST_KEY_CONTROL});
|
||||
$oActualManifest->numericSet(MANIFEST_SECTION_BACKUP_DB, MANIFEST_KEY_CATALOG, undef,
|
||||
${$oExpectedManifestRef}{&MANIFEST_SECTION_BACKUP_DB}{&MANIFEST_KEY_CATALOG});
|
||||
$oActualManifest->numericSet(MANIFEST_SECTION_BACKUP_DB, MANIFEST_KEY_SYSTEM_ID, undef,
|
||||
${$oExpectedManifestRef}{&MANIFEST_SECTION_BACKUP_DB}{&MANIFEST_KEY_SYSTEM_ID});
|
||||
$oActualManifest->numericSet(MANIFEST_SECTION_BACKUP_DB, MANIFEST_KEY_DB_ID, undef,
|
||||
${$oExpectedManifestRef}{&MANIFEST_SECTION_BACKUP_DB}{&MANIFEST_KEY_DB_ID});
|
||||
|
||||
$oActualManifest->set(INI_SECTION_BACKREST, INI_KEY_VERSION, undef,
|
||||
${$oExpectedManifestRef}{&INI_SECTION_BACKREST}{&INI_KEY_VERSION});
|
||||
|
||||
# Copy passphrase if one exists
|
||||
if (defined($oExpectedManifestRef->{&INI_SECTION_CIPHER}) &&
|
||||
defined($oExpectedManifestRef->{&INI_SECTION_CIPHER}{&INI_KEY_CIPHER_PASS}))
|
||||
{
|
||||
$oActualManifest->set(INI_SECTION_CIPHER, INI_KEY_CIPHER_PASS, undef,
|
||||
$oExpectedManifestRef->{&INI_SECTION_CIPHER}{&INI_KEY_CIPHER_PASS});
|
||||
}
|
||||
|
||||
# This option won't be set in the actual manifest
|
||||
delete($oExpectedManifestRef->{&MANIFEST_SECTION_BACKUP_OPTION}{&MANIFEST_KEY_CHECKSUM_PAGE});
|
||||
|
||||
if ($self->synthetic())
|
||||
{
|
||||
$oActualManifest->remove(MANIFEST_SECTION_BACKUP);
|
||||
delete($oExpectedManifestRef->{&MANIFEST_SECTION_BACKUP});
|
||||
}
|
||||
else
|
||||
{
|
||||
$oActualManifest->set(
|
||||
INI_SECTION_BACKREST, INI_KEY_CHECKSUM, undef, $oExpectedManifestRef->{&INI_SECTION_BACKREST}{&INI_KEY_CHECKSUM});
|
||||
$oActualManifest->set(
|
||||
MANIFEST_SECTION_BACKUP, MANIFEST_KEY_LABEL, undef,
|
||||
$oExpectedManifestRef->{&MANIFEST_SECTION_BACKUP}{&MANIFEST_KEY_LABEL});
|
||||
$oActualManifest->set(
|
||||
MANIFEST_SECTION_BACKUP, MANIFEST_KEY_TIMESTAMP_COPY_START, undef,
|
||||
$oExpectedManifestRef->{&MANIFEST_SECTION_BACKUP}{&MANIFEST_KEY_TIMESTAMP_COPY_START});
|
||||
$oActualManifest->set(
|
||||
MANIFEST_SECTION_BACKUP, MANIFEST_KEY_TIMESTAMP_START, undef,
|
||||
$oExpectedManifestRef->{&MANIFEST_SECTION_BACKUP}{&MANIFEST_KEY_TIMESTAMP_START});
|
||||
$oActualManifest->set(
|
||||
MANIFEST_SECTION_BACKUP, MANIFEST_KEY_TIMESTAMP_STOP, undef,
|
||||
$oExpectedManifestRef->{&MANIFEST_SECTION_BACKUP}{&MANIFEST_KEY_TIMESTAMP_STOP});
|
||||
$oActualManifest->set(
|
||||
MANIFEST_SECTION_BACKUP, MANIFEST_KEY_TYPE, undef,
|
||||
$oExpectedManifestRef->{&MANIFEST_SECTION_BACKUP}{&MANIFEST_KEY_TYPE});
|
||||
|
||||
$oActualManifest->set(MANIFEST_SECTION_BACKUP, MANIFEST_KEY_LSN_START, undef,
|
||||
${$oExpectedManifestRef}{&MANIFEST_SECTION_BACKUP}{&MANIFEST_KEY_LSN_START});
|
||||
$oActualManifest->set(MANIFEST_SECTION_BACKUP, MANIFEST_KEY_LSN_STOP, undef,
|
||||
${$oExpectedManifestRef}{&MANIFEST_SECTION_BACKUP}{&MANIFEST_KEY_LSN_STOP});
|
||||
|
||||
if (defined(${$oExpectedManifestRef}{&MANIFEST_SECTION_BACKUP}{&MANIFEST_KEY_ARCHIVE_START}))
|
||||
{
|
||||
$oActualManifest->set(MANIFEST_SECTION_BACKUP, MANIFEST_KEY_ARCHIVE_START, undef,
|
||||
${$oExpectedManifestRef}{&MANIFEST_SECTION_BACKUP}{&MANIFEST_KEY_ARCHIVE_START});
|
||||
}
|
||||
|
||||
if (${$oExpectedManifestRef}{&MANIFEST_SECTION_BACKUP}{&MANIFEST_KEY_ARCHIVE_STOP})
|
||||
{
|
||||
$oActualManifest->set(MANIFEST_SECTION_BACKUP, MANIFEST_KEY_ARCHIVE_STOP, undef,
|
||||
${$oExpectedManifestRef}{&MANIFEST_SECTION_BACKUP}{&MANIFEST_KEY_ARCHIVE_STOP});
|
||||
}
|
||||
}
|
||||
|
||||
# Check that archive status exists in the manifest for an online backup
|
||||
my $strArchiveStatusPath = MANIFEST_TARGET_PGDATA . qw{/} . $oActualManifest->walPath() . qw{/} . DB_PATH_ARCHIVESTATUS;
|
||||
|
||||
if ($oExpectedManifestRef->{&MANIFEST_SECTION_BACKUP_OPTION}{&MANIFEST_KEY_ONLINE} &&
|
||||
!defined($oExpectedManifestRef->{&MANIFEST_SECTION_TARGET_PATH}{$strArchiveStatusPath}))
|
||||
{
|
||||
confess &log(ERROR, "${strArchiveStatusPath} expected for online backup", ERROR_ASSERT);
|
||||
}
|
||||
|
||||
# Delete the list of DBs
|
||||
delete($$oExpectedManifestRef{&MANIFEST_SECTION_DB});
|
||||
|
||||
$self->manifestDefault($oExpectedManifestRef);
|
||||
|
||||
storageTest()->put("${strTestPath}/actual.manifest", iniRender($oActualManifest->{oContent}));
|
||||
storageTest()->put("${strTestPath}/expected.manifest", iniRender($oExpectedManifestRef));
|
||||
|
||||
executeTest("diff ${strTestPath}/expected.manifest ${strTestPath}/actual.manifest");
|
||||
|
||||
storageTest()->remove("${strTestPath}/expected.manifest");
|
||||
storageTest()->remove("${strTestPath}/actual.manifest");
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# Getters
|
||||
####################################################################################################################################
|
||||
|
@ -14,7 +14,6 @@ use Carp qw(confess);
|
||||
use DBI;
|
||||
use Exporter qw(import);
|
||||
our @EXPORT = qw();
|
||||
use Fcntl ':mode';
|
||||
use File::Basename qw(dirname);
|
||||
use Storable qw(dclone);
|
||||
|
||||
@ -155,118 +154,6 @@ sub archivePush
|
||||
return logDebugReturn($strOperation);
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# configRecovery
|
||||
####################################################################################################################################
|
||||
sub configRecovery
|
||||
{
|
||||
my $self = shift;
|
||||
my $oHostBackup = shift;
|
||||
my $oRecoveryHashRef = shift;
|
||||
|
||||
# Get stanza
|
||||
my $strStanza = $self->stanza();
|
||||
|
||||
# Load db config file
|
||||
my $oConfig = iniParse(${storageTest->get($self->backrestConfig())}, {bRelaxed => true});
|
||||
|
||||
# Rewrite recovery options
|
||||
my @stryRecoveryOption;
|
||||
|
||||
foreach my $strOption (sort(keys(%$oRecoveryHashRef)))
|
||||
{
|
||||
push (@stryRecoveryOption, "${strOption}=${$oRecoveryHashRef}{$strOption}");
|
||||
}
|
||||
|
||||
if (@stryRecoveryOption)
|
||||
{
|
||||
$oConfig->{$strStanza}{cfgOptionName(CFGOPT_RECOVERY_OPTION)} = \@stryRecoveryOption;
|
||||
}
|
||||
|
||||
# Save db config file
|
||||
storageTest()->put($self->backrestConfig(), iniRender($oConfig, true));
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# configRemap
|
||||
####################################################################################################################################
|
||||
sub configRemap
|
||||
{
|
||||
my $self = shift;
|
||||
my $oRemapHashRef = shift;
|
||||
my $oManifestRef = shift;
|
||||
|
||||
# Get stanza name
|
||||
my $strStanza = $self->stanza();
|
||||
|
||||
# Load db config file
|
||||
my $oConfig = iniParse(${storageTest()->get($self->backrestConfig())}, {bRelaxed => true});
|
||||
|
||||
# Load backup config file
|
||||
my $oRemoteConfig;
|
||||
my $oHostBackup =
|
||||
!$self->standby() && !$self->nameTest($self->backupDestination()) ?
|
||||
hostGroupGet()->hostGet($self->backupDestination()) : undef;
|
||||
|
||||
if (defined($oHostBackup))
|
||||
{
|
||||
$oRemoteConfig = iniParse(${storageTest()->get($oHostBackup->backrestConfig())}, {bRelaxed => true});
|
||||
}
|
||||
|
||||
# Rewrite recovery section
|
||||
delete($oConfig->{"${strStanza}:restore"}{cfgOptionName(CFGOPT_TABLESPACE_MAP)});
|
||||
my @stryTablespaceMap;
|
||||
|
||||
foreach my $strRemap (sort(keys(%$oRemapHashRef)))
|
||||
{
|
||||
my $strRemapPath = ${$oRemapHashRef}{$strRemap};
|
||||
|
||||
if ($strRemap eq MANIFEST_TARGET_PGDATA)
|
||||
{
|
||||
$oConfig->{$strStanza}{$self->optionIndexName(CFGOPT_DB_PATH, 1)} = $strRemapPath;
|
||||
|
||||
${$oManifestRef}{&MANIFEST_SECTION_BACKUP_TARGET}{&MANIFEST_TARGET_PGDATA}{&MANIFEST_SUBKEY_PATH} = $strRemapPath;
|
||||
|
||||
if (defined($oHostBackup))
|
||||
{
|
||||
my $bForce = $oHostBackup->nameTest(HOST_BACKUP) && defined(hostGroupGet()->hostGet(HOST_DB_STANDBY, true));
|
||||
$oRemoteConfig->{$strStanza}{$self->optionIndexName(CFGOPT_DB_PATH, 1, $bForce)} = $strRemapPath;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
my $strTablespaceOid = (split('\/', $strRemap))[1];
|
||||
push (@stryTablespaceMap, "${strTablespaceOid}=${strRemapPath}");
|
||||
|
||||
${$oManifestRef}{&MANIFEST_SECTION_BACKUP_TARGET}{$strRemap}{&MANIFEST_SUBKEY_PATH} = $strRemapPath;
|
||||
${$oManifestRef}{&MANIFEST_SECTION_TARGET_LINK}{MANIFEST_TARGET_PGDATA . "/${strRemap}"}{destination} = $strRemapPath;
|
||||
}
|
||||
}
|
||||
|
||||
if (@stryTablespaceMap)
|
||||
{
|
||||
$oConfig->{"${strStanza}:restore"}{cfgOptionName(CFGOPT_TABLESPACE_MAP)} = \@stryTablespaceMap;
|
||||
}
|
||||
|
||||
# Save db config file
|
||||
storageTest()->put($self->backrestConfig(), iniRender($oConfig, true));
|
||||
|
||||
# Save backup config file (but not if this is the standby which is not the source of backups)
|
||||
if (defined($oHostBackup))
|
||||
{
|
||||
# Modify the file permissions so it can be read/saved by all test users
|
||||
executeTest(
|
||||
'sudo chmod 660 ' . $oHostBackup->backrestConfig() . ' && sudo chmod 770 ' . dirname($oHostBackup->backrestConfig()));
|
||||
|
||||
storageTest()->put($oHostBackup->backrestConfig(), iniRender($oRemoteConfig, true));
|
||||
|
||||
# Fix permissions
|
||||
executeTest(
|
||||
'sudo chmod 660 ' . $oHostBackup->backrestConfig() . ' && sudo chmod 770 ' . dirname($oHostBackup->backrestConfig()) .
|
||||
' && sudo chown ' . $oHostBackup->userGet() . ' ' . $oHostBackup->backrestConfig());
|
||||
}
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# linkRemap
|
||||
####################################################################################################################################
|
||||
@ -294,444 +181,6 @@ sub linkRemap
|
||||
return logDebugReturn($strOperation);
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# restore
|
||||
####################################################################################################################################
|
||||
sub restore
|
||||
{
|
||||
my $self = shift;
|
||||
my $strBackup = shift;
|
||||
my $oExpectedManifestRef = shift;
|
||||
my $oRemapHashRef = shift;
|
||||
my $bDelta = shift;
|
||||
my $bForce = shift;
|
||||
my $strType = shift;
|
||||
my $strTarget = shift;
|
||||
my $bTargetExclusive = shift;
|
||||
my $strTargetAction = shift;
|
||||
my $strTargetTimeline = shift;
|
||||
my $oRecoveryHashRef = shift;
|
||||
my $strComment = shift;
|
||||
my $iExpectedExitStatus = shift;
|
||||
my $strOptionalParam = shift;
|
||||
my $bTablespace = shift;
|
||||
my $strUser = shift;
|
||||
|
||||
# Set defaults
|
||||
$bDelta = defined($bDelta) ? $bDelta : false;
|
||||
$bForce = defined($bForce) ? $bForce : false;
|
||||
|
||||
# Build link map options
|
||||
my $strLinkMap;
|
||||
|
||||
foreach my $strTarget (sort(keys(%{$self->{hLinkRemap}})))
|
||||
{
|
||||
$strLinkMap .= " --link-map=\"${strTarget}=${$self->{hLinkRemap}}{$strTarget}\"";
|
||||
}
|
||||
|
||||
$strComment = 'restore' .
|
||||
($bDelta ? ' delta' : '') .
|
||||
($bForce ? ', force' : '') .
|
||||
($strBackup ne cfgDefOptionDefault(CFGCMD_RESTORE, CFGOPT_SET) ? ", backup '${strBackup}'" : '') .
|
||||
($strType ? ", type '${strType}'" : '') .
|
||||
($strTarget ? ", target '${strTarget}'" : '') .
|
||||
($strTargetTimeline ? ", timeline '${strTargetTimeline}'" : '') .
|
||||
(defined($bTargetExclusive) && $bTargetExclusive ? ', exclusive' : '') .
|
||||
(defined($strTargetAction) && $strTargetAction ne cfgDefOptionDefault(CFGCMD_RESTORE, CFGOPT_TARGET_ACTION)
|
||||
? ', ' . cfgOptionName(CFGOPT_TARGET_ACTION) . "=${strTargetAction}" : '') .
|
||||
(defined($oRemapHashRef) ? ', remap' : '') .
|
||||
(defined($iExpectedExitStatus) ? ", expect exit ${iExpectedExitStatus}" : '') .
|
||||
(defined($strComment) ? " - ${strComment}" : '') .
|
||||
' (' . $self->nameGet() . ' host)';
|
||||
&log(INFO, " ${strComment}");
|
||||
|
||||
# Get the backup host
|
||||
my $oHostGroup = hostGroupGet();
|
||||
my $oHostBackup = defined($oHostGroup->hostGet(HOST_BACKUP, true)) ? $oHostGroup->hostGet(HOST_BACKUP) : $self;
|
||||
|
||||
# Load the expected manifest if it was not defined
|
||||
my $oExpectedManifest = undef;
|
||||
|
||||
if (!defined($oExpectedManifestRef))
|
||||
{
|
||||
# Load the manifest
|
||||
my $oExpectedManifest = new pgBackRest::Manifest(
|
||||
storageRepo()->pathGet(
|
||||
STORAGE_REPO_BACKUP . qw{/} . ($strBackup eq 'latest' ? $oHostBackup->backupLast() : $strBackup) . qw{/} .
|
||||
FILE_MANIFEST),
|
||||
{strCipherPass => $oHostBackup->cipherPassManifest()});
|
||||
|
||||
$oExpectedManifestRef = $oExpectedManifest->{oContent};
|
||||
|
||||
# Remap links in the expected manifest
|
||||
foreach my $strTarget (sort(keys(%{$self->{hLinkRemap}})))
|
||||
{
|
||||
my $strDestination = ${$self->{hLinkRemap}}{$strTarget};
|
||||
my $strTarget = 'pg_data/' . $strTarget;
|
||||
my $strTargetPath = $strDestination;
|
||||
|
||||
# If this link is to a file then the specified path must be split into file and path parts
|
||||
if ($oExpectedManifest->isTargetFile($strTarget))
|
||||
{
|
||||
$strTargetPath = dirname($strTargetPath);
|
||||
|
||||
# Error when the path is not deep enough to be valid
|
||||
if (!defined($strTargetPath))
|
||||
{
|
||||
confess &log(ERROR, "${strDestination} is not long enough to be target for ${strTarget}");
|
||||
}
|
||||
|
||||
# Set the file part
|
||||
$oExpectedManifest->set(
|
||||
MANIFEST_SECTION_BACKUP_TARGET, $strTarget, MANIFEST_SUBKEY_FILE,
|
||||
substr($strDestination, length($strTargetPath) + 1));
|
||||
|
||||
# Set the link target
|
||||
$oExpectedManifest->set(
|
||||
MANIFEST_SECTION_TARGET_LINK, $strTarget, MANIFEST_SUBKEY_DESTINATION, $strDestination);
|
||||
}
|
||||
else
|
||||
{
|
||||
# Set the link target
|
||||
$oExpectedManifest->set(MANIFEST_SECTION_TARGET_LINK, $strTarget, MANIFEST_SUBKEY_DESTINATION, $strTargetPath);
|
||||
}
|
||||
|
||||
# Set the target path
|
||||
$oExpectedManifest->set(MANIFEST_SECTION_BACKUP_TARGET, $strTarget, MANIFEST_SUBKEY_PATH, $strTargetPath);
|
||||
}
|
||||
}
|
||||
|
||||
# Get the backup host
|
||||
if (defined($oRemapHashRef))
|
||||
{
|
||||
$self->configRemap($oRemapHashRef, $oExpectedManifestRef);
|
||||
}
|
||||
|
||||
if (defined($oRecoveryHashRef))
|
||||
{
|
||||
$self->configRecovery($oHostBackup, $oRecoveryHashRef);
|
||||
}
|
||||
|
||||
# Create the restore command
|
||||
$self->executeSimple(
|
||||
$self->backrestExe() .
|
||||
' --config=' . $self->backrestConfig() .
|
||||
(defined($bDelta) && $bDelta ? ' --delta' : '') .
|
||||
(defined($bForce) && $bForce ? ' --force' : '') .
|
||||
($strBackup ne cfgDefOptionDefault(CFGCMD_RESTORE, CFGOPT_SET) ? " --set=${strBackup}" : '') .
|
||||
(defined($strOptionalParam) ? " ${strOptionalParam} " : '') .
|
||||
(defined($strType) && $strType ne CFGOPTVAL_RESTORE_TYPE_DEFAULT ? " --type=${strType}" : '') .
|
||||
(defined($strTarget) ? " --target=\"${strTarget}\"" : '') .
|
||||
(defined($strTargetTimeline) ? " --target-timeline=\"${strTargetTimeline}\"" : '') .
|
||||
(defined($bTargetExclusive) && $bTargetExclusive ? ' --target-exclusive' : '') .
|
||||
(defined($strLinkMap) ? $strLinkMap : '') .
|
||||
($self->synthetic() ? '' : ' --link-all') .
|
||||
(defined($strTargetAction) && $strTargetAction ne cfgDefOptionDefault(CFGCMD_RESTORE, CFGOPT_TARGET_ACTION)
|
||||
? ' --' . cfgOptionName(CFGOPT_TARGET_ACTION) . "=${strTargetAction}" : '') .
|
||||
' --stanza=' . $self->stanza() . ' restore',
|
||||
{strComment => $strComment, iExpectedExitStatus => $iExpectedExitStatus, oLogTest => $self->{oLogTest},
|
||||
bLogOutput => $self->synthetic()},
|
||||
$strUser);
|
||||
|
||||
if (!defined($iExpectedExitStatus))
|
||||
{
|
||||
$self->restoreCompare($strBackup, dclone($oExpectedManifestRef), $bTablespace);
|
||||
|
||||
if (defined($self->{oLogTest}))
|
||||
{
|
||||
$self->{oLogTest}->supplementalAdd(
|
||||
$$oExpectedManifestRef{&MANIFEST_SECTION_BACKUP_TARGET}{&MANIFEST_TARGET_PGDATA}{&MANIFEST_SUBKEY_PATH} .
|
||||
"/recovery.conf");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# restoreCompare
|
||||
####################################################################################################################################
|
||||
sub restoreCompare
|
||||
{
|
||||
my $self = shift;
|
||||
my $strBackup = shift;
|
||||
my $oExpectedManifestRef = shift;
|
||||
my $bTablespace = shift;
|
||||
|
||||
my $strTestPath = $self->testPath();
|
||||
|
||||
# Get the backup host
|
||||
my $oHostGroup = hostGroupGet();
|
||||
my $oHostBackup = defined($oHostGroup->hostGet(HOST_BACKUP, true)) ? $oHostGroup->hostGet(HOST_BACKUP) : $self;
|
||||
|
||||
# Load the last manifest if it exists
|
||||
my $oLastManifest = undef;
|
||||
|
||||
if (defined(${$oExpectedManifestRef}{&MANIFEST_SECTION_BACKUP}{&MANIFEST_KEY_PRIOR}))
|
||||
{
|
||||
my $oExpectedManifest =
|
||||
new pgBackRest::Manifest(
|
||||
storageRepo()->pathGet(
|
||||
STORAGE_REPO_BACKUP . qw{/} . ($strBackup eq 'latest' ? $oHostBackup->backupLast() : $strBackup) .
|
||||
'/'. FILE_MANIFEST),
|
||||
{strCipherPass => $oHostBackup->cipherPassManifest()});
|
||||
|
||||
$oLastManifest =
|
||||
new pgBackRest::Manifest(
|
||||
storageRepo()->pathGet(
|
||||
STORAGE_REPO_BACKUP . qw{/} .
|
||||
${$oExpectedManifestRef}{&MANIFEST_SECTION_BACKUP}{&MANIFEST_KEY_PRIOR} . qw{/} . FILE_MANIFEST),
|
||||
{strCipherPass => $oHostBackup->cipherPassManifest()});
|
||||
}
|
||||
|
||||
# Generate the tablespace map for real backups
|
||||
my $oTablespaceMap = undef;
|
||||
|
||||
if (!$self->synthetic())
|
||||
{
|
||||
# Tablespace_map file is not restored in versions >= 9.5 because it interferes with internal remapping features.
|
||||
if (${$oExpectedManifestRef}{&MANIFEST_SECTION_BACKUP_DB}{&MANIFEST_KEY_DB_VERSION} >= PG_VERSION_95)
|
||||
{
|
||||
delete(${$oExpectedManifestRef}{&MANIFEST_SECTION_TARGET_FILE}{MANIFEST_TARGET_PGDATA . '/tablespace_map'});
|
||||
}
|
||||
|
||||
foreach my $strTarget (keys(%{${$oExpectedManifestRef}{&MANIFEST_SECTION_BACKUP_TARGET}}))
|
||||
{
|
||||
if (defined(${$oExpectedManifestRef}{&MANIFEST_SECTION_BACKUP_TARGET}{$strTarget}{&MANIFEST_SUBKEY_TABLESPACE_ID}))
|
||||
{
|
||||
my $iTablespaceId =
|
||||
${$oExpectedManifestRef}{&MANIFEST_SECTION_BACKUP_TARGET}{$strTarget}{&MANIFEST_SUBKEY_TABLESPACE_ID};
|
||||
|
||||
$$oTablespaceMap{$iTablespaceId} =
|
||||
${$oExpectedManifestRef}{&MANIFEST_SECTION_BACKUP_TARGET}{$strTarget}{&MANIFEST_SUBKEY_TABLESPACE_NAME};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Generate the actual manifest
|
||||
my $strDbClusterPath =
|
||||
${$oExpectedManifestRef}{&MANIFEST_SECTION_BACKUP_TARGET}{&MANIFEST_TARGET_PGDATA}{&MANIFEST_SUBKEY_PATH};
|
||||
|
||||
if (defined($bTablespace) && !$bTablespace)
|
||||
{
|
||||
foreach my $strTarget (keys(%{${$oExpectedManifestRef}{&MANIFEST_SECTION_BACKUP_TARGET}}))
|
||||
{
|
||||
if ($$oExpectedManifestRef{&MANIFEST_SECTION_BACKUP_TARGET}{$strTarget}{&MANIFEST_SUBKEY_TYPE} eq
|
||||
MANIFEST_VALUE_LINK &&
|
||||
defined($$oExpectedManifestRef{&MANIFEST_SECTION_BACKUP_TARGET}{$strTarget}{&MANIFEST_SUBKEY_TABLESPACE_ID}))
|
||||
{
|
||||
my $strRemapPath;
|
||||
my $iTablespaceName =
|
||||
$$oExpectedManifestRef{&MANIFEST_SECTION_BACKUP_TARGET}{$strTarget}{&MANIFEST_SUBKEY_TABLESPACE_NAME};
|
||||
|
||||
$strRemapPath = "../../tablespace/${iTablespaceName}";
|
||||
|
||||
$$oExpectedManifestRef{&MANIFEST_SECTION_BACKUP_TARGET}{$strTarget}{&MANIFEST_SUBKEY_PATH} = $strRemapPath;
|
||||
$$oExpectedManifestRef{&MANIFEST_SECTION_TARGET_LINK}{MANIFEST_TARGET_PGDATA . "/${strTarget}"}
|
||||
{&MANIFEST_SUBKEY_DESTINATION} = $strRemapPath;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
my $oActualManifest = new pgBackRest::Manifest(
|
||||
"${strTestPath}/" . FILE_MANIFEST,
|
||||
{bLoad => false, strDbVersion => $oExpectedManifestRef->{&MANIFEST_SECTION_BACKUP_DB}{&MANIFEST_KEY_DB_VERSION},
|
||||
oStorage => storageTest()});
|
||||
|
||||
$oActualManifest->set(
|
||||
MANIFEST_SECTION_BACKUP_DB, MANIFEST_KEY_DB_VERSION, undef,
|
||||
$$oExpectedManifestRef{&MANIFEST_SECTION_BACKUP_DB}{&MANIFEST_KEY_DB_VERSION});
|
||||
$oActualManifest->numericSet(
|
||||
MANIFEST_SECTION_BACKUP_DB, MANIFEST_KEY_CATALOG, undef,
|
||||
$$oExpectedManifestRef{&MANIFEST_SECTION_BACKUP_DB}{&MANIFEST_KEY_CATALOG});
|
||||
|
||||
$oActualManifest->build(storageTest(), $strDbClusterPath, $oLastManifest, false, $oTablespaceMap);
|
||||
|
||||
my $strSectionPath = $oActualManifest->get(MANIFEST_SECTION_BACKUP_TARGET, MANIFEST_TARGET_PGDATA, MANIFEST_SUBKEY_PATH);
|
||||
|
||||
foreach my $strName ($oActualManifest->keys(MANIFEST_SECTION_TARGET_FILE))
|
||||
{
|
||||
# If synthetic match checksum errors since they can't be verified here
|
||||
if ($self->synthetic)
|
||||
{
|
||||
my $bChecksumPage = $oExpectedManifestRef->{&MANIFEST_SECTION_TARGET_FILE}{$strName}{&MANIFEST_SUBKEY_CHECKSUM_PAGE};
|
||||
|
||||
if (defined($bChecksumPage))
|
||||
{
|
||||
$oActualManifest->boolSet(MANIFEST_SECTION_TARGET_FILE, $strName, MANIFEST_SUBKEY_CHECKSUM_PAGE, $bChecksumPage);
|
||||
|
||||
if (!$bChecksumPage &&
|
||||
defined($oExpectedManifestRef->{&MANIFEST_SECTION_TARGET_FILE}{$strName}{&MANIFEST_SUBKEY_CHECKSUM_PAGE_ERROR}))
|
||||
{
|
||||
$oActualManifest->set(
|
||||
MANIFEST_SECTION_TARGET_FILE, $strName, MANIFEST_SUBKEY_CHECKSUM_PAGE_ERROR,
|
||||
$oExpectedManifestRef->{&MANIFEST_SECTION_TARGET_FILE}{$strName}{&MANIFEST_SUBKEY_CHECKSUM_PAGE_ERROR});
|
||||
}
|
||||
}
|
||||
}
|
||||
# Else if page checksums are enabled make sure the correct files are being checksummed
|
||||
else
|
||||
{
|
||||
if ($oExpectedManifestRef->{&MANIFEST_SECTION_BACKUP_OPTION}{&MANIFEST_KEY_CHECKSUM_PAGE})
|
||||
{
|
||||
if (defined($oExpectedManifestRef->{&MANIFEST_SECTION_TARGET_FILE}{$strName}{&MANIFEST_SUBKEY_CHECKSUM_PAGE}) !=
|
||||
isChecksumPage($strName))
|
||||
{
|
||||
confess
|
||||
"check-page actual for ${strName} is " .
|
||||
($oActualManifest->test(MANIFEST_SECTION_TARGET_FILE, $strName,
|
||||
MANIFEST_SUBKEY_CHECKSUM_PAGE) ? 'set' : '[undef]') .
|
||||
' but isChecksumPage() says it should be ' .
|
||||
(isChecksumPage($strName) ? 'set' : '[undef]') . '.';
|
||||
}
|
||||
|
||||
# Because the page checksum flag is copied to incr and diff from the previous backup but further processing is not
|
||||
# done, they can't be expected to match so delete them.
|
||||
delete($oExpectedManifestRef->{&MANIFEST_SECTION_TARGET_FILE}{$strName}{&MANIFEST_SUBKEY_CHECKSUM_PAGE});
|
||||
$oActualManifest->remove(MANIFEST_SECTION_TARGET_FILE, $strName, MANIFEST_SUBKEY_CHECKSUM_PAGE);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$self->synthetic())
|
||||
{
|
||||
$oActualManifest->set(
|
||||
MANIFEST_SECTION_TARGET_FILE, $strName, MANIFEST_SUBKEY_SIZE,
|
||||
${$oExpectedManifestRef}{&MANIFEST_SECTION_TARGET_FILE}{$strName}{size});
|
||||
}
|
||||
|
||||
# Remove repo-size from the manifest. ??? This could be improved to get actual sizes from the backup.
|
||||
$oActualManifest->remove(MANIFEST_SECTION_TARGET_FILE, $strName, MANIFEST_SUBKEY_REPO_SIZE);
|
||||
delete($oExpectedManifestRef->{&MANIFEST_SECTION_TARGET_FILE}{$strName}{&MANIFEST_SUBKEY_REPO_SIZE});
|
||||
|
||||
if ($oActualManifest->get(MANIFEST_SECTION_TARGET_FILE, $strName, MANIFEST_SUBKEY_SIZE) != 0)
|
||||
{
|
||||
my $oStat = storageTest()->info($oActualManifest->dbPathGet($strSectionPath, $strName));
|
||||
|
||||
if ($oStat->blocks > 0 || S_ISLNK($oStat->mode))
|
||||
{
|
||||
my ($strHash) = storageTest()->hashSize($oActualManifest->dbPathGet($strSectionPath, $strName));
|
||||
|
||||
$oActualManifest->set(
|
||||
MANIFEST_SECTION_TARGET_FILE, $strName, MANIFEST_SUBKEY_CHECKSUM, $strHash);
|
||||
}
|
||||
else
|
||||
{
|
||||
$oActualManifest->remove(MANIFEST_SECTION_TARGET_FILE, $strName, MANIFEST_SUBKEY_CHECKSUM);
|
||||
delete(${$oExpectedManifestRef}{&MANIFEST_SECTION_TARGET_FILE}{$strName}{&MANIFEST_SUBKEY_CHECKSUM});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# If the link section is empty then delete it and the default section
|
||||
if (keys(%{${$oExpectedManifestRef}{&MANIFEST_SECTION_TARGET_LINK}}) == 0)
|
||||
{
|
||||
delete($$oExpectedManifestRef{&MANIFEST_SECTION_TARGET_LINK});
|
||||
delete($$oExpectedManifestRef{&MANIFEST_SECTION_TARGET_LINK . ':default'});
|
||||
}
|
||||
|
||||
# Set actual to expected for settings that always change from backup to backup
|
||||
$oActualManifest->set(MANIFEST_SECTION_BACKUP_OPTION, MANIFEST_KEY_ARCHIVE_CHECK, undef,
|
||||
${$oExpectedManifestRef}{&MANIFEST_SECTION_BACKUP_OPTION}{&MANIFEST_KEY_ARCHIVE_CHECK});
|
||||
$oActualManifest->set(MANIFEST_SECTION_BACKUP_OPTION, MANIFEST_KEY_ARCHIVE_COPY, undef,
|
||||
${$oExpectedManifestRef}{&MANIFEST_SECTION_BACKUP_OPTION}{&MANIFEST_KEY_ARCHIVE_COPY});
|
||||
$oActualManifest->set(MANIFEST_SECTION_BACKUP_OPTION, MANIFEST_KEY_BACKUP_STANDBY, undef,
|
||||
${$oExpectedManifestRef}{&MANIFEST_SECTION_BACKUP_OPTION}{&MANIFEST_KEY_BACKUP_STANDBY});
|
||||
$oActualManifest->set(MANIFEST_SECTION_BACKUP_OPTION, MANIFEST_KEY_COMPRESS, undef,
|
||||
${$oExpectedManifestRef}{&MANIFEST_SECTION_BACKUP_OPTION}{&MANIFEST_KEY_COMPRESS});
|
||||
$oActualManifest->set(MANIFEST_SECTION_BACKUP_OPTION, MANIFEST_KEY_HARDLINK, undef,
|
||||
${$oExpectedManifestRef}{&MANIFEST_SECTION_BACKUP_OPTION}{&MANIFEST_KEY_HARDLINK});
|
||||
$oActualManifest->set(MANIFEST_SECTION_BACKUP_OPTION, MANIFEST_KEY_ONLINE, undef,
|
||||
${$oExpectedManifestRef}{&MANIFEST_SECTION_BACKUP_OPTION}{&MANIFEST_KEY_ONLINE});
|
||||
|
||||
$oActualManifest->set(MANIFEST_SECTION_BACKUP_DB, MANIFEST_KEY_DB_VERSION, undef,
|
||||
${$oExpectedManifestRef}{&MANIFEST_SECTION_BACKUP_DB}{&MANIFEST_KEY_DB_VERSION});
|
||||
$oActualManifest->numericSet(MANIFEST_SECTION_BACKUP_DB, MANIFEST_KEY_CONTROL, undef,
|
||||
${$oExpectedManifestRef}{&MANIFEST_SECTION_BACKUP_DB}{&MANIFEST_KEY_CONTROL});
|
||||
$oActualManifest->numericSet(MANIFEST_SECTION_BACKUP_DB, MANIFEST_KEY_CATALOG, undef,
|
||||
${$oExpectedManifestRef}{&MANIFEST_SECTION_BACKUP_DB}{&MANIFEST_KEY_CATALOG});
|
||||
$oActualManifest->numericSet(MANIFEST_SECTION_BACKUP_DB, MANIFEST_KEY_SYSTEM_ID, undef,
|
||||
${$oExpectedManifestRef}{&MANIFEST_SECTION_BACKUP_DB}{&MANIFEST_KEY_SYSTEM_ID});
|
||||
$oActualManifest->numericSet(MANIFEST_SECTION_BACKUP_DB, MANIFEST_KEY_DB_ID, undef,
|
||||
${$oExpectedManifestRef}{&MANIFEST_SECTION_BACKUP_DB}{&MANIFEST_KEY_DB_ID});
|
||||
|
||||
$oActualManifest->set(INI_SECTION_BACKREST, INI_KEY_VERSION, undef,
|
||||
${$oExpectedManifestRef}{&INI_SECTION_BACKREST}{&INI_KEY_VERSION});
|
||||
|
||||
# Copy passphrase if one exists
|
||||
if (defined($oExpectedManifestRef->{&INI_SECTION_CIPHER}) &&
|
||||
defined($oExpectedManifestRef->{&INI_SECTION_CIPHER}{&INI_KEY_CIPHER_PASS}))
|
||||
{
|
||||
$oActualManifest->set(INI_SECTION_CIPHER, INI_KEY_CIPHER_PASS, undef,
|
||||
$oExpectedManifestRef->{&INI_SECTION_CIPHER}{&INI_KEY_CIPHER_PASS});
|
||||
}
|
||||
|
||||
# This option won't be set in the actual manifest
|
||||
delete($oExpectedManifestRef->{&MANIFEST_SECTION_BACKUP_OPTION}{&MANIFEST_KEY_CHECKSUM_PAGE});
|
||||
|
||||
if ($self->synthetic())
|
||||
{
|
||||
$oActualManifest->remove(MANIFEST_SECTION_BACKUP);
|
||||
delete($oExpectedManifestRef->{&MANIFEST_SECTION_BACKUP});
|
||||
}
|
||||
else
|
||||
{
|
||||
$oActualManifest->set(
|
||||
INI_SECTION_BACKREST, INI_KEY_CHECKSUM, undef, $oExpectedManifestRef->{&INI_SECTION_BACKREST}{&INI_KEY_CHECKSUM});
|
||||
$oActualManifest->set(
|
||||
MANIFEST_SECTION_BACKUP, MANIFEST_KEY_LABEL, undef,
|
||||
$oExpectedManifestRef->{&MANIFEST_SECTION_BACKUP}{&MANIFEST_KEY_LABEL});
|
||||
$oActualManifest->set(
|
||||
MANIFEST_SECTION_BACKUP, MANIFEST_KEY_TIMESTAMP_COPY_START, undef,
|
||||
$oExpectedManifestRef->{&MANIFEST_SECTION_BACKUP}{&MANIFEST_KEY_TIMESTAMP_COPY_START});
|
||||
$oActualManifest->set(
|
||||
MANIFEST_SECTION_BACKUP, MANIFEST_KEY_TIMESTAMP_START, undef,
|
||||
$oExpectedManifestRef->{&MANIFEST_SECTION_BACKUP}{&MANIFEST_KEY_TIMESTAMP_START});
|
||||
$oActualManifest->set(
|
||||
MANIFEST_SECTION_BACKUP, MANIFEST_KEY_TIMESTAMP_STOP, undef,
|
||||
$oExpectedManifestRef->{&MANIFEST_SECTION_BACKUP}{&MANIFEST_KEY_TIMESTAMP_STOP});
|
||||
$oActualManifest->set(
|
||||
MANIFEST_SECTION_BACKUP, MANIFEST_KEY_TYPE, undef,
|
||||
$oExpectedManifestRef->{&MANIFEST_SECTION_BACKUP}{&MANIFEST_KEY_TYPE});
|
||||
|
||||
$oActualManifest->set(MANIFEST_SECTION_BACKUP, MANIFEST_KEY_LSN_START, undef,
|
||||
${$oExpectedManifestRef}{&MANIFEST_SECTION_BACKUP}{&MANIFEST_KEY_LSN_START});
|
||||
$oActualManifest->set(MANIFEST_SECTION_BACKUP, MANIFEST_KEY_LSN_STOP, undef,
|
||||
${$oExpectedManifestRef}{&MANIFEST_SECTION_BACKUP}{&MANIFEST_KEY_LSN_STOP});
|
||||
|
||||
if (defined(${$oExpectedManifestRef}{&MANIFEST_SECTION_BACKUP}{&MANIFEST_KEY_ARCHIVE_START}))
|
||||
{
|
||||
$oActualManifest->set(MANIFEST_SECTION_BACKUP, MANIFEST_KEY_ARCHIVE_START, undef,
|
||||
${$oExpectedManifestRef}{&MANIFEST_SECTION_BACKUP}{&MANIFEST_KEY_ARCHIVE_START});
|
||||
}
|
||||
|
||||
if (${$oExpectedManifestRef}{&MANIFEST_SECTION_BACKUP}{&MANIFEST_KEY_ARCHIVE_STOP})
|
||||
{
|
||||
$oActualManifest->set(MANIFEST_SECTION_BACKUP, MANIFEST_KEY_ARCHIVE_STOP, undef,
|
||||
${$oExpectedManifestRef}{&MANIFEST_SECTION_BACKUP}{&MANIFEST_KEY_ARCHIVE_STOP});
|
||||
}
|
||||
}
|
||||
|
||||
# Check that archive status exists in the manifest for an online backup
|
||||
my $strArchiveStatusPath = MANIFEST_TARGET_PGDATA . qw{/} . $oActualManifest->walPath() . qw{/} . DB_PATH_ARCHIVESTATUS;
|
||||
|
||||
if ($oExpectedManifestRef->{&MANIFEST_SECTION_BACKUP_OPTION}{&MANIFEST_KEY_ONLINE} &&
|
||||
!defined($oExpectedManifestRef->{&MANIFEST_SECTION_TARGET_PATH}{$strArchiveStatusPath}))
|
||||
{
|
||||
confess &log(ERROR, "${strArchiveStatusPath} expected for online backup", ERROR_ASSERT);
|
||||
}
|
||||
|
||||
# Delete the list of DBs
|
||||
delete($$oExpectedManifestRef{&MANIFEST_SECTION_DB});
|
||||
|
||||
$self->manifestDefault($oExpectedManifestRef);
|
||||
|
||||
storageTest()->put("${strTestPath}/actual.manifest", iniRender($oActualManifest->{oContent}));
|
||||
storageTest()->put("${strTestPath}/expected.manifest", iniRender($oExpectedManifestRef));
|
||||
|
||||
executeTest("diff ${strTestPath}/expected.manifest ${strTestPath}/actual.manifest");
|
||||
|
||||
storageTest()->remove("${strTestPath}/expected.manifest");
|
||||
storageTest()->remove("${strTestPath}/actual.manifest");
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# Getters
|
||||
####################################################################################################################################
|
||||
|
@ -499,9 +499,6 @@ sub run
|
||||
|
||||
# Restore - tests various mode, extra files/paths, missing files/paths
|
||||
#---------------------------------------------------------------------------------------------------------------------------
|
||||
my $bDelta = true;
|
||||
my $bForce = false;
|
||||
|
||||
# Munge permissions/modes on files that will be fixed by the restore
|
||||
if (!$bRemote)
|
||||
{
|
||||
@ -532,9 +529,9 @@ sub run
|
||||
}
|
||||
|
||||
$oHostDbMaster->restore(
|
||||
$strFullBackup, \%oManifest, undef, $bDelta, $bForce, undef, undef, undef, undef, undef, undef,
|
||||
'add and delete files', undef, ' --link-all' . ($bRemote ? ' --cmd-ssh=/usr/bin/ssh' : ''),
|
||||
undef, !$bRemote ? 'root' : undef);
|
||||
'add and delete files', $strFullBackup,
|
||||
{rhExpectedManifest => \%oManifest, bDelta => true, strUser => !$bRemote ? 'root' : undef,
|
||||
strOptionalParam => ' --link-all' . ($bRemote ? ' --cmd-ssh=/usr/bin/ssh' : '')});
|
||||
|
||||
# Run again to fix permissions
|
||||
if (!$bRemote)
|
||||
@ -560,8 +557,9 @@ sub run
|
||||
{&MANIFEST_SUBKEY_GROUP});
|
||||
|
||||
$oHostDbMaster->restore(
|
||||
$strFullBackup, \%oManifest, undef, $bDelta, $bForce, undef, undef, undef, undef, undef, undef,
|
||||
'fix permissions', undef, ' --link-all --log-level-console=detail', undef, 'root');
|
||||
'fix permissions', $strFullBackup,
|
||||
{rhExpectedManifest => \%oManifest, bDelta => true, strUser => 'root',
|
||||
strOptionalParam => ' --link-all --log-level-console=detail'});
|
||||
|
||||
# Fix and remove files that are now owned by root
|
||||
executeTest('sudo chown -R ' . TEST_USER . ':' . TEST_GROUP . ' ' . $oHostBackup->logPath());
|
||||
@ -573,69 +571,70 @@ sub run
|
||||
$oHostDbMaster->dbLinkCreate(\%oManifest, MANIFEST_TARGET_PGDATA, 'pg_stat', '../wrong');
|
||||
|
||||
$oHostDbMaster->restore(
|
||||
$strFullBackup, \%oManifest, undef, $bDelta, $bForce, undef, undef, undef, undef, undef, undef,
|
||||
'fix broken symlink', undef, " --link-all ${strLogReduced}" . ($bRemote ? ' --compress-level-network=0' : ''));
|
||||
'fix broken symlink', $strFullBackup,
|
||||
{rhExpectedManifest => \%oManifest, bDelta => true,
|
||||
strOptionalParam => " --link-all ${strLogReduced}" . ($bRemote ? ' --compress-level-network=0' : '')});
|
||||
|
||||
# Additional restore tests that don't need to be performed for every permutation
|
||||
if (!$bRemote)
|
||||
{
|
||||
# This time manually restore all links
|
||||
$oHostDbMaster->restore(
|
||||
$strFullBackup, \%oManifest, undef, $bDelta, $bForce, undef, undef, undef, undef, undef, undef,
|
||||
'restore all links by mapping', undef, $strLogReduced .
|
||||
' --link-map=pg_stat=../pg_stat --link-map=postgresql.conf=../pg_config/postgresql.conf');
|
||||
'restore all links by mapping', $strFullBackup,
|
||||
{rhExpectedManifest => \%oManifest, bDelta => true,
|
||||
strOptionalParam =>
|
||||
$strLogReduced . ' --link-map=pg_stat=../pg_stat --link-map=postgresql.conf=../pg_config/postgresql.conf'});
|
||||
|
||||
# Error when links overlap
|
||||
$oHostDbMaster->restore(
|
||||
$strFullBackup, \%oManifest, undef, $bDelta, $bForce, undef, undef, undef, undef, undef, undef,
|
||||
'restore all links by mapping', ERROR_LINK_DESTINATION, '--log-level-console=warn' .
|
||||
' --link-map=pg_stat=../pg_stat --link-map=postgresql.conf=../pg_stat/postgresql.conf');
|
||||
'restore all links by mapping', $strFullBackup,
|
||||
{rhExpectedManifest => \%oManifest, bDelta => true, iExpectedExitStatus => ERROR_LINK_DESTINATION,
|
||||
strOptionalParam =>
|
||||
'--log-level-console=warn --link-map=pg_stat=../pg_stat ' .
|
||||
'--link-map=postgresql.conf=../pg_stat/postgresql.conf'});
|
||||
|
||||
# Error when links still exist on non-delta restore
|
||||
$bDelta = false;
|
||||
|
||||
executeTest('rm -rf ' . $oHostDbMaster->dbBasePath() . "/*");
|
||||
|
||||
$oHostDbMaster->restore(
|
||||
$strFullBackup, \%oManifest, undef, $bDelta, $bForce, undef, undef, undef, undef, undef, undef,
|
||||
'error on existing linked path', ERROR_PATH_NOT_EMPTY, '--log-level-console=warn --link-all');
|
||||
'error on existing linked path', $strFullBackup,
|
||||
{rhExpectedManifest => \%oManifest, iExpectedExitStatus => ERROR_PATH_NOT_EMPTY,
|
||||
strOptionalParam => '--log-level-console=warn --link-all'});
|
||||
|
||||
executeTest('rm -rf ' . $oHostDbMaster->dbPath() . "/pg_stat/*");
|
||||
|
||||
$oHostDbMaster->restore(
|
||||
$strFullBackup, \%oManifest, undef, $bDelta, $bForce, undef, undef, undef, undef, undef, undef,
|
||||
'error on existing linked file', ERROR_PATH_NOT_EMPTY, '--log-level-console=warn --link-all');
|
||||
'error on existing linked file', $strFullBackup,
|
||||
{rhExpectedManifest => \%oManifest, iExpectedExitStatus => ERROR_PATH_NOT_EMPTY,
|
||||
strOptionalParam => '--log-level-console=warn --link-all'});
|
||||
|
||||
# Error when postmaster.pid is present
|
||||
executeTest('touch ' . $oHostDbMaster->dbBasePath() . qw(/) . DB_FILE_POSTMASTERPID);
|
||||
|
||||
$oHostDbMaster->restore(
|
||||
$strFullBackup, \%oManifest, undef, $bDelta, $bForce, undef, undef, undef, undef, undef, undef,
|
||||
'error on postmaster.pid exists', ERROR_POSTMASTER_RUNNING, '--log-level-console=warn');
|
||||
'error on postmaster.pid exists', $strFullBackup,
|
||||
{rhExpectedManifest => \%oManifest, iExpectedExitStatus => ERROR_POSTMASTER_RUNNING,
|
||||
strOptionalParam => '--log-level-console=warn'});
|
||||
|
||||
executeTest('rm ' . $oHostDbMaster->dbBasePath() . qw(/) . DB_FILE_POSTMASTERPID);
|
||||
|
||||
# Now a combination of remapping
|
||||
$bDelta = true;
|
||||
|
||||
$oHostDbMaster->restore(
|
||||
$strFullBackup, \%oManifest, undef, $bDelta, $bForce, undef, undef, undef, undef, undef, undef,
|
||||
'restore all links --link-all and mapping', undef,
|
||||
"${strLogReduced} --link-map=pg_stat=../pg_stat --link-all");
|
||||
'restore all links --link-all and mapping', $strFullBackup,
|
||||
{rhExpectedManifest => \%oManifest, bDelta => true,
|
||||
strOptionalParam => "${strLogReduced} --link-map=pg_stat=../pg_stat --link-all"});
|
||||
}
|
||||
|
||||
# Restore - test errors when $PGDATA cannot be verified
|
||||
#---------------------------------------------------------------------------------------------------------------------------
|
||||
$bDelta = true;
|
||||
$bForce = true;
|
||||
|
||||
# Remove PG_VERSION
|
||||
$oHostDbMaster->dbFileRemove(\%oManifest, MANIFEST_TARGET_PGDATA, DB_FILE_PGVERSION);
|
||||
|
||||
# Attempt the restore
|
||||
$oHostDbMaster->restore(
|
||||
$strFullBackup, \%oManifest, undef, $bDelta, $bForce, undef, undef, undef, undef, undef, undef,
|
||||
'fail on missing ' . DB_FILE_PGVERSION, ERROR_PATH_NOT_EMPTY, $strLogReduced);
|
||||
'fail on missing ' . DB_FILE_PGVERSION, $strFullBackup,
|
||||
{rhExpectedManifest => \%oManifest, bDelta => true, bForce => true, iExpectedExitStatus => ERROR_PATH_NOT_EMPTY,
|
||||
strOptionalParam => $strLogReduced});
|
||||
|
||||
# Write a backup.manifest file to make $PGDATA valid
|
||||
testFileCreate($oHostDbMaster->dbBasePath() . '/backup.manifest', 'BOGUS');
|
||||
@ -652,8 +651,9 @@ sub run
|
||||
$oHostDbMaster->manifestLinkMap(\%oManifest, MANIFEST_TARGET_PGDATA . '/postgresql.conf');
|
||||
|
||||
$oHostDbMaster->restore(
|
||||
$strFullBackup, \%oManifest, undef, $bDelta, $bForce, undef, undef, undef, undef, undef, undef,
|
||||
'restore succeeds with backup.manifest file', undef, $strLogReduced);
|
||||
'restore succeeds with backup.manifest file', $strFullBackup,
|
||||
{rhExpectedManifest => \%oManifest, bDelta => true, bForce => true,
|
||||
strOptionalParam => $strLogReduced});
|
||||
|
||||
# Various broken info tests
|
||||
#---------------------------------------------------------------------------------------------------------------------------
|
||||
@ -898,13 +898,11 @@ sub run
|
||||
|
||||
# Restore
|
||||
#---------------------------------------------------------------------------------------------------------------------------
|
||||
$bDelta = false;
|
||||
$bForce = false;
|
||||
|
||||
# Fail on used path
|
||||
$oHostDbMaster->restore(
|
||||
$strBackup, \%oManifest, undef, $bDelta, $bForce, undef, undef, undef, undef, undef, undef,
|
||||
'fail on used path', ERROR_PATH_NOT_EMPTY, $strLogReduced);
|
||||
'fail on used path', $strBackup,
|
||||
{rhExpectedManifest => \%oManifest, iExpectedExitStatus => ERROR_PATH_NOT_EMPTY,
|
||||
strOptionalParam => $strLogReduced});
|
||||
|
||||
# Remap the base and tablespace paths
|
||||
my %oRemapHash;
|
||||
@ -920,16 +918,15 @@ sub run
|
||||
}
|
||||
|
||||
$oHostDbMaster->restore(
|
||||
$strBackup, \%oManifest, \%oRemapHash, $bDelta, $bForce, undef, undef, undef, undef, undef, undef,
|
||||
'remap all paths', undef, $strLogReduced);
|
||||
'remap all paths', $strBackup,
|
||||
{rhExpectedManifest => \%oManifest, rhRemapHash => \%oRemapHash, strOptionalParam => $strLogReduced});
|
||||
|
||||
# Restore (make sure file in root tablespace path is not deleted by --delta)
|
||||
#---------------------------------------------------------------------------------------------------------------------------
|
||||
$bDelta = true;
|
||||
|
||||
$oHostDbMaster->restore(
|
||||
$strBackup, \%oManifest, \%oRemapHash, $bDelta, $bForce, undef, undef, undef, undef, undef, undef,
|
||||
'ensure file in tblspc root remains after --delta', undef, $strLogReduced);
|
||||
'ensure file in tblspc root remains after --delta', $strBackup,
|
||||
{rhExpectedManifest => \%oManifest, rhRemapHash => \%oRemapHash, bDelta => true,
|
||||
strOptionalParam => $strLogReduced});
|
||||
|
||||
if (!-e $strDoNotDeleteFile)
|
||||
{
|
||||
@ -1132,8 +1129,6 @@ sub run
|
||||
|
||||
# Selective Restore
|
||||
#---------------------------------------------------------------------------------------------------------------------------
|
||||
$bDelta = true;
|
||||
|
||||
# Remove mapping for tablespace 1
|
||||
delete($oRemapHash{&MANIFEST_TARGET_PGTBLSPC . '/1'});
|
||||
|
||||
@ -1146,8 +1141,9 @@ sub run
|
||||
{&MANIFEST_SUBKEY_CHECKSUM});
|
||||
|
||||
$oHostDbMaster->restore(
|
||||
cfgDefOptionDefault(CFGCMD_RESTORE, CFGOPT_SET), \%oManifest, \%oRemapHash, $bDelta, $bForce, undef, undef, undef,
|
||||
undef, undef, undef, 'selective restore 16384', undef, "${strLogReduced} --db-include=16384");
|
||||
'selective restore 16384', cfgDefOptionDefault(CFGCMD_RESTORE, CFGOPT_SET),
|
||||
{rhExpectedManifest => \%oManifest, rhRemapHash => \%oRemapHash, bDelta => true,
|
||||
strOptionalParam => "${strLogReduced} --db-include=16384"});
|
||||
|
||||
# Restore checksum values for next test
|
||||
$oManifest{&MANIFEST_SECTION_TARGET_FILE}{'pg_data/base/32768/33000'}{&MANIFEST_SUBKEY_CHECKSUM} =
|
||||
@ -1163,24 +1159,25 @@ sub run
|
||||
delete($oManifest{&MANIFEST_SECTION_TARGET_FILE}{'pg_data/base/16384/17000'}{&MANIFEST_SUBKEY_CHECKSUM});
|
||||
|
||||
$oHostDbMaster->restore(
|
||||
cfgDefOptionDefault(CFGCMD_RESTORE, CFGOPT_SET), \%oManifest, \%oRemapHash, $bDelta, $bForce, undef, undef, undef,
|
||||
undef, undef, undef, 'selective restore 32768', undef, "${strLogReduced} --db-include=32768");
|
||||
'selective restore 32768', cfgDefOptionDefault(CFGCMD_RESTORE, CFGOPT_SET),
|
||||
{rhExpectedManifest => \%oManifest, rhRemapHash => \%oRemapHash, bDelta => true,
|
||||
strOptionalParam => "${strLogReduced} --db-include=32768"});
|
||||
|
||||
$oManifest{&MANIFEST_SECTION_TARGET_FILE}{'pg_data/base/16384/17000'}{&MANIFEST_SUBKEY_CHECKSUM} =
|
||||
'7579ada0808d7f98087a0a586d0df9de009cdc33';
|
||||
|
||||
$oHostDbMaster->restore(
|
||||
cfgDefOptionDefault(CFGCMD_RESTORE, CFGOPT_SET), \%oManifest, \%oRemapHash, $bDelta, $bForce, undef, undef, undef,
|
||||
undef, undef, undef, 'error on invalid id', ERROR_DB_MISSING, '--log-level-console=warn --db-include=7777');
|
||||
'error on invalid id', cfgDefOptionDefault(CFGCMD_RESTORE, CFGOPT_SET),
|
||||
{rhExpectedManifest => \%oManifest, rhRemapHash => \%oRemapHash, bDelta => true,
|
||||
iExpectedExitStatus => ERROR_DB_MISSING, strOptionalParam => '--log-level-console=warn --db-include=7777'});
|
||||
|
||||
$oHostDbMaster->restore(
|
||||
cfgDefOptionDefault(CFGCMD_RESTORE, CFGOPT_SET), \%oManifest, \%oRemapHash, $bDelta, $bForce, undef, undef, undef,
|
||||
undef, undef, undef, 'error on system id', ERROR_DB_INVALID, '--log-level-console=warn --db-include=1');
|
||||
'error on system id', cfgDefOptionDefault(CFGCMD_RESTORE, CFGOPT_SET),
|
||||
{rhExpectedManifest => \%oManifest, rhRemapHash => \%oRemapHash, bDelta => true,
|
||||
iExpectedExitStatus => ERROR_DB_INVALID, strOptionalParam => '--log-level-console=warn --db-include=1'});
|
||||
|
||||
# Compact Restore
|
||||
#---------------------------------------------------------------------------------------------------------------------------
|
||||
$bDelta = false;
|
||||
|
||||
executeTest('rm -rf ' . $oHostDbMaster->dbBasePath(2) . "/*");
|
||||
|
||||
my $strDbPath = $oHostDbMaster->dbBasePath(2) . '/base';
|
||||
@ -1190,15 +1187,16 @@ sub run
|
||||
delete($oRemapHash{&MANIFEST_TARGET_PGTBLSPC . '/2'});
|
||||
|
||||
$oHostDbMaster->restore(
|
||||
cfgDefOptionDefault(CFGCMD_RESTORE, CFGOPT_SET), \%oManifest, \%oRemapHash, $bDelta, $bForce, undef, undef, undef,
|
||||
undef, undef, undef, 'no tablespace remap - error when tablespace dir does not exist', ERROR_PATH_MISSING,
|
||||
"${strLogReduced} --tablespace-map-all=../../tablespace", false);
|
||||
'no tablespace remap - error when tablespace dir does not exist', cfgDefOptionDefault(CFGCMD_RESTORE, CFGOPT_SET),
|
||||
{rhExpectedManifest => \%oManifest, rhRemapHash => \%oRemapHash, iExpectedExitStatus => ERROR_PATH_MISSING,
|
||||
bTablespace => false, strOptionalParam => "${strLogReduced} --tablespace-map-all=../../tablespace"});
|
||||
|
||||
storageTest()->pathCreate($oHostDbMaster->dbBasePath(2) . '/tablespace', {strMode => '0700'});
|
||||
|
||||
$oHostDbMaster->restore(
|
||||
cfgDefOptionDefault(CFGCMD_RESTORE, CFGOPT_SET), \%oManifest, undef, $bDelta, $bForce, undef, undef, undef, undef,
|
||||
undef, undef, 'no tablespace remap', undef, "--tablespace-map-all=../../tablespace ${strLogReduced}", false);
|
||||
'no tablespace remap', cfgDefOptionDefault(CFGCMD_RESTORE, CFGOPT_SET),
|
||||
{rhExpectedManifest => \%oManifest, bTablespace => false,
|
||||
strOptionalParam => "--tablespace-map-all=../../tablespace ${strLogReduced}"});
|
||||
|
||||
$oManifest{&MANIFEST_SECTION_BACKUP_TARGET}{'pg_tblspc/2'}{&MANIFEST_SUBKEY_PATH} = '../../tablespace/ts2';
|
||||
$oManifest{&MANIFEST_SECTION_TARGET_LINK}{'pg_data/pg_tblspc/2'}{&MANIFEST_SUBKEY_DESTINATION} = '../../tablespace/ts2';
|
||||
|
@ -124,18 +124,6 @@ sub run
|
||||
# Static backup parameters
|
||||
my $fTestDelay = 1;
|
||||
|
||||
# Variable backup parameters
|
||||
my $bDelta = true;
|
||||
my $bForce = false;
|
||||
my $strType = undef;
|
||||
my $strTarget = undef;
|
||||
my $bTargetExclusive = false;
|
||||
my $strTargetAction;
|
||||
my $strTargetTimeline = undef;
|
||||
my $oRecoveryHashRef = undef;
|
||||
my $strComment = undef;
|
||||
my $iExpectedExitStatus = undef;
|
||||
|
||||
# Restore test string
|
||||
my $strDefaultMessage = 'default';
|
||||
my $strFullMessage = 'full';
|
||||
@ -157,7 +145,8 @@ sub run
|
||||
#---------------------------------------------------------------------------------------------------------------------------
|
||||
if ($bTestExtra)
|
||||
{
|
||||
$strType = CFGOPTVAL_BACKUP_TYPE_FULL;
|
||||
# In this section the same comment can be used multiple times so make it a variable that can be set once and reused
|
||||
my $strComment = undef;
|
||||
|
||||
# Remove the files in the archive directory
|
||||
forceStorageRemove(storageRepo(), STORAGE_REPO_ARCHIVE, {bRecurse => true});
|
||||
@ -173,7 +162,7 @@ sub run
|
||||
$strComment = 'fail on archive_mode=off';
|
||||
$oHostDbMaster->clusterRestart({bIgnoreLogError => true, bArchiveEnabled => false});
|
||||
|
||||
$oHostBackup->backup($strType, $strComment, {iExpectedExitStatus => ERROR_ARCHIVE_DISABLED});
|
||||
$oHostBackup->backup(CFGOPTVAL_BACKUP_TYPE_FULL, $strComment, {iExpectedExitStatus => ERROR_ARCHIVE_DISABLED});
|
||||
$oHostDbMaster->check($strComment, {iTimeout => 0.1, iExpectedExitStatus => ERROR_ARCHIVE_DISABLED});
|
||||
|
||||
# If running the remote tests then also need to run check locally
|
||||
@ -186,7 +175,7 @@ sub run
|
||||
$strComment = 'fail on invalid archive_command';
|
||||
$oHostDbMaster->clusterRestart({bIgnoreLogError => true, bArchive => false});
|
||||
|
||||
$oHostBackup->backup($strType, $strComment, {iExpectedExitStatus => ERROR_ARCHIVE_COMMAND_INVALID});
|
||||
$oHostBackup->backup(CFGOPTVAL_BACKUP_TYPE_FULL, $strComment, {iExpectedExitStatus => ERROR_ARCHIVE_COMMAND_INVALID});
|
||||
$oHostDbMaster->check($strComment, {iTimeout => 0.1, iExpectedExitStatus => ERROR_ARCHIVE_COMMAND_INVALID});
|
||||
|
||||
# If running the remote tests then also need to run check locally
|
||||
@ -373,8 +362,6 @@ sub run
|
||||
|
||||
# Full backup
|
||||
#---------------------------------------------------------------------------------------------------------------------------
|
||||
$strType = CFGOPTVAL_BACKUP_TYPE_FULL;
|
||||
|
||||
# Create the table where test messages will be stored
|
||||
$oHostDbMaster->sqlExecute("create table test (message text not null)");
|
||||
$oHostDbMaster->sqlWalRotate();
|
||||
@ -388,7 +375,8 @@ sub run
|
||||
confess 'unable to acquire advisory lock for testing';
|
||||
}
|
||||
|
||||
$oHostBackup->backup($strType, 'fail on backup lock exists', {iExpectedExitStatus => ERROR_LOCK_ACQUIRE});
|
||||
$oHostBackup->backup(
|
||||
CFGOPTVAL_BACKUP_TYPE_FULL, 'fail on backup lock exists', {iExpectedExitStatus => ERROR_LOCK_ACQUIRE});
|
||||
|
||||
# Release the backup advisory lock so the next backup will succeed
|
||||
if (!$oHostDbMaster->sqlSelectOne('select pg_advisory_unlock(' . DB_BACKUP_ADVISORY_LOCK . ')'))
|
||||
@ -398,7 +386,7 @@ sub run
|
||||
}
|
||||
|
||||
my $oExecuteBackup = $oHostBackup->backupBegin(
|
||||
$strType, 'update during backup',
|
||||
CFGOPTVAL_BACKUP_TYPE_FULL, 'update during backup',
|
||||
{strTest => TEST_MANIFEST_BUILD, fTestDelay => $fTestDelay,
|
||||
strOptionalParam => ' --' . cfgOptionName(CFGOPT_BUFFER_SIZE) . '=16384'});
|
||||
|
||||
@ -407,7 +395,7 @@ sub run
|
||||
# Required to set hint bits to be sent to the standby to make the heap match on both sides
|
||||
$oHostDbMaster->sqlSelectOneTest('select message from test', $strFullMessage);
|
||||
|
||||
my $strFullBackup = $oHostBackup->backupEnd($strType, $oExecuteBackup);
|
||||
my $strFullBackup = $oHostBackup->backupEnd(CFGOPTVAL_BACKUP_TYPE_FULL, $oExecuteBackup);
|
||||
|
||||
# Enabled async archiving
|
||||
$oHostBackup->configUpdate({&CFGDEF_SECTION_GLOBAL => {cfgOptionName(CFGOPT_ARCHIVE_ASYNC) => 'y'}});
|
||||
@ -433,19 +421,6 @@ sub run
|
||||
#---------------------------------------------------------------------------------------------------------------------------
|
||||
if ($bHostStandby)
|
||||
{
|
||||
$bDelta = false;
|
||||
$bForce = false;
|
||||
$strType = CFGOPTVAL_RESTORE_TYPE_DEFAULT;
|
||||
$strTarget = undef;
|
||||
$bTargetExclusive = undef;
|
||||
$strTargetAction = undef;
|
||||
$strTargetTimeline = undef;
|
||||
$oRecoveryHashRef = undef;
|
||||
$strComment = undef;
|
||||
$iExpectedExitStatus = undef;
|
||||
|
||||
$strComment = 'restore backup on replica';
|
||||
|
||||
my %oRemapHash;
|
||||
$oRemapHash{&MANIFEST_TARGET_PGDATA} = $oHostDbStandby->dbBasePath();
|
||||
|
||||
@ -455,11 +430,11 @@ sub run
|
||||
}
|
||||
|
||||
$oHostDbStandby->restore(
|
||||
cfgDefOptionDefault(CFGCMD_RESTORE, CFGOPT_SET), undef, \%oRemapHash, $bDelta, $bForce, $strType, $strTarget,
|
||||
$bTargetExclusive, $strTargetAction, $strTargetTimeline, $oRecoveryHashRef, $strComment, $iExpectedExitStatus,
|
||||
' --recovery-option=standby_mode=on' .
|
||||
' --recovery-option="primary_conninfo=host=' . HOST_DB_MASTER .
|
||||
' port=' . $oHostDbMaster->pgPort() . ' user=replicator"');
|
||||
'restore backup on replica', cfgDefOptionDefault(CFGCMD_RESTORE, CFGOPT_SET),
|
||||
{rhRemapHash => \%oRemapHash,
|
||||
strOptionalParam =>
|
||||
' --recovery-option=standby_mode=on --recovery-option="primary_conninfo=host=' . HOST_DB_MASTER .
|
||||
' port=' . $oHostDbMaster->pgPort() . ' user=replicator"'});
|
||||
|
||||
$oHostDbStandby->clusterStart({bHotStandby => true});
|
||||
|
||||
@ -521,13 +496,11 @@ sub run
|
||||
# the logs will to be deleted to avoid causing issues further down the line.
|
||||
if ($bTestExtra && !$bS3)
|
||||
{
|
||||
$strType = CFGOPTVAL_BACKUP_TYPE_INCR;
|
||||
|
||||
$oHostDbMaster->clusterRestart();
|
||||
|
||||
$oHostDbMaster->stop();
|
||||
|
||||
$oHostBackup->backup($strType, 'attempt backup when stopped', {iExpectedExitStatus => ERROR_STOP});
|
||||
$oHostBackup->backup(CFGOPTVAL_BACKUP_TYPE_INCR, 'attempt backup when stopped', {iExpectedExitStatus => ERROR_STOP});
|
||||
|
||||
$oHostDbMaster->start();
|
||||
}
|
||||
@ -543,12 +516,11 @@ sub run
|
||||
#---------------------------------------------------------------------------------------------------------------------------
|
||||
if ($bTestLocal && $oHostDbMaster->pgVersion() >= PG_VERSION_95)
|
||||
{
|
||||
$strType = CFGOPTVAL_BACKUP_TYPE_INCR;
|
||||
|
||||
# Set archive_mode=always
|
||||
$oHostDbMaster->clusterRestart({bArchiveAlways => true});
|
||||
|
||||
$oHostBackup->backup($strType, 'fail on archive_mode=always', {iExpectedExitStatus => ERROR_FEATURE_NOT_SUPPORTED});
|
||||
$oHostBackup->backup(
|
||||
CFGOPTVAL_BACKUP_TYPE_INCR, 'fail on archive_mode=always', {iExpectedExitStatus => ERROR_FEATURE_NOT_SUPPORTED});
|
||||
|
||||
# Reset the cluster to a normal state so the next test will work
|
||||
$oHostDbMaster->clusterRestart();
|
||||
@ -556,8 +528,6 @@ sub run
|
||||
|
||||
# Incr backup
|
||||
#---------------------------------------------------------------------------------------------------------------------------
|
||||
$strType = CFGOPTVAL_BACKUP_TYPE_INCR;
|
||||
|
||||
# Create a tablespace directory
|
||||
storageTest()->pathCreate($oHostDbMaster->tablespacePath(1), {strMode => '0700', bCreateParent => true});
|
||||
|
||||
@ -587,7 +557,8 @@ sub run
|
||||
$oHostDbMaster->sqlSelectOne("select pg_start_backup('test backup that will cause an error', true)");
|
||||
|
||||
# Verify that an error is returned if the backup is already running
|
||||
$oHostBackup->backup($strType, 'fail on backup already running', {iExpectedExitStatus => ERROR_DB_QUERY});
|
||||
$oHostBackup->backup(
|
||||
CFGOPTVAL_BACKUP_TYPE_INCR, 'fail on backup already running', {iExpectedExitStatus => ERROR_DB_QUERY});
|
||||
|
||||
# Restart the cluster ignoring any errors in the postgresql log
|
||||
$oHostDbMaster->clusterRestart({bIgnoreLogError => true});
|
||||
@ -597,7 +568,7 @@ sub run
|
||||
}
|
||||
|
||||
$oExecuteBackup = $oHostBackup->backupBegin(
|
||||
$strType, 'update during backup',
|
||||
CFGOPTVAL_BACKUP_TYPE_INCR, 'update during backup',
|
||||
{strTest => TEST_MANIFEST_BUILD, fTestDelay => $fTestDelay,
|
||||
strOptionalParam => '--' . cfgOptionName(CFGOPT_STOP_AUTO) . ' --no-' . cfgOptionName(CFGOPT_ARCHIVE_CHECK) .
|
||||
' --' . cfgOptionName(CFGOPT_BUFFER_SIZE) . '=32768'});
|
||||
@ -622,7 +593,7 @@ sub run
|
||||
}
|
||||
}
|
||||
|
||||
my $strIncrBackup = $oHostBackup->backupEnd($strType, $oExecuteBackup);
|
||||
my $strIncrBackup = $oHostBackup->backupEnd(CFGOPTVAL_BACKUP_TYPE_INCR, $oExecuteBackup);
|
||||
|
||||
# Setup the xid target
|
||||
#---------------------------------------------------------------------------------------------------------------------------
|
||||
@ -669,26 +640,12 @@ sub run
|
||||
|
||||
# Restore (type = default)
|
||||
#---------------------------------------------------------------------------------------------------------------------------
|
||||
$bDelta = false;
|
||||
$bForce = false;
|
||||
$strType = CFGOPTVAL_RESTORE_TYPE_DEFAULT;
|
||||
$strTarget = undef;
|
||||
$bTargetExclusive = undef;
|
||||
$strTargetAction = undef;
|
||||
$strTargetTimeline = undef;
|
||||
$oRecoveryHashRef = undef;
|
||||
$strComment = undef;
|
||||
$iExpectedExitStatus = undef;
|
||||
|
||||
if ($bTestLocal)
|
||||
{
|
||||
# Expect failure because postmaster.pid exists
|
||||
$strComment = 'postmaster running';
|
||||
$iExpectedExitStatus = ERROR_POSTMASTER_RUNNING;
|
||||
|
||||
$oHostDbMaster->restore(
|
||||
cfgDefOptionDefault(CFGCMD_RESTORE, CFGOPT_SET), undef, undef, $bDelta, $bForce, $strType, $strTarget,
|
||||
$bTargetExclusive, $strTargetAction, $strTargetTimeline, $oRecoveryHashRef, $strComment, $iExpectedExitStatus);
|
||||
'postmaster running', cfgDefOptionDefault(CFGCMD_RESTORE, CFGOPT_SET),
|
||||
{iExpectedExitStatus => ERROR_POSTMASTER_RUNNING});
|
||||
}
|
||||
|
||||
$oHostDbMaster->clusterStop();
|
||||
@ -696,12 +653,8 @@ sub run
|
||||
if ($bTestLocal)
|
||||
{
|
||||
# Expect failure because db path is not empty
|
||||
$strComment = 'path not empty';
|
||||
$iExpectedExitStatus = ERROR_PATH_NOT_EMPTY;
|
||||
|
||||
$oHostDbMaster->restore(
|
||||
cfgDefOptionDefault(CFGCMD_RESTORE, CFGOPT_SET), undef, undef, $bDelta, $bForce, $strType, $strTarget,
|
||||
$bTargetExclusive, $strTargetAction, $strTargetTimeline, $oRecoveryHashRef, $strComment, $iExpectedExitStatus);
|
||||
'path not empty', cfgDefOptionDefault(CFGCMD_RESTORE, CFGOPT_SET), {iExpectedExitStatus => ERROR_PATH_NOT_EMPTY});
|
||||
}
|
||||
|
||||
# Drop and recreate db path
|
||||
@ -713,13 +666,9 @@ sub run
|
||||
storageTest()->pathCreate($oHostDbMaster->tablespacePath(1), {strMode => '0700'});
|
||||
|
||||
# Now the restore should work
|
||||
$strComment = undef;
|
||||
$iExpectedExitStatus = undef;
|
||||
|
||||
$oHostDbMaster->restore(
|
||||
cfgDefOptionDefault(CFGCMD_RESTORE, CFGOPT_SET), undef, undef, $bDelta, $bForce, $strType, $strTarget,
|
||||
$bTargetExclusive, $strTargetAction, $strTargetTimeline, $oRecoveryHashRef, $strComment, $iExpectedExitStatus,
|
||||
($bTestLocal ? ' --db-include=test1' : '') . ' --buffer-size=16384');
|
||||
undef, cfgDefOptionDefault(CFGCMD_RESTORE, CFGOPT_SET),
|
||||
{strOptionalParam => ($bTestLocal ? ' --db-include=test1' : '') . ' --buffer-size=16384'});
|
||||
|
||||
$oHostDbMaster->clusterStart();
|
||||
$oHostDbMaster->sqlSelectOneTest('select message from test', $bTestLocal ? $strNameMessage : $strIncrMessage);
|
||||
@ -789,24 +738,11 @@ sub run
|
||||
#---------------------------------------------------------------------------------------------------------------------------
|
||||
if (($bTestLocal || $bHostStandby) && $oHostDbMaster->pgVersion() >= PG_VERSION_94)
|
||||
{
|
||||
$bDelta = false;
|
||||
$bForce = true;
|
||||
$strType = CFGOPTVAL_RESTORE_TYPE_IMMEDIATE;
|
||||
$strTarget = undef;
|
||||
$bTargetExclusive = undef;
|
||||
$strTargetAction = undef;
|
||||
$strTargetTimeline = undef;
|
||||
$oRecoveryHashRef = undef;
|
||||
$strComment = undef;
|
||||
$iExpectedExitStatus = undef;
|
||||
|
||||
&log(INFO, " testing recovery type = ${strType}");
|
||||
&log(INFO, ' testing recovery type = ' . CFGOPTVAL_RESTORE_TYPE_IMMEDIATE);
|
||||
|
||||
$oHostDbMaster->clusterStop();
|
||||
|
||||
$oHostDbMaster->restore(
|
||||
$strFullBackup, undef, undef, $bDelta, $bForce, $strType, $strTarget, $bTargetExclusive, $strTargetAction,
|
||||
$strTargetTimeline, $oRecoveryHashRef, $strComment, $iExpectedExitStatus, undef);
|
||||
$oHostDbMaster->restore(undef, $strFullBackup, {bForce => true, strType => CFGOPTVAL_RESTORE_TYPE_IMMEDIATE});
|
||||
|
||||
$oHostDbMaster->clusterStart();
|
||||
$oHostDbMaster->sqlSelectOneTest(
|
||||
@ -817,18 +753,7 @@ sub run
|
||||
#---------------------------------------------------------------------------------------------------------------------------
|
||||
if ($bTestLocal)
|
||||
{
|
||||
$bDelta = false;
|
||||
$bForce = true;
|
||||
$strType = CFGOPTVAL_RESTORE_TYPE_XID;
|
||||
$strTarget = $strXidTarget;
|
||||
$bTargetExclusive = undef;
|
||||
$strTargetAction = $oHostDbMaster->pgVersion() >= PG_VERSION_91 ? 'promote' : undef;
|
||||
$strTargetTimeline = undef;
|
||||
$oRecoveryHashRef = undef;
|
||||
$strComment = undef;
|
||||
$iExpectedExitStatus = undef;
|
||||
|
||||
&log(INFO, " testing recovery type = ${strType}");
|
||||
&log(INFO, ' testing recovery type = ' . CFGOPTVAL_RESTORE_TYPE_XID);
|
||||
|
||||
$oHostDbMaster->clusterStop();
|
||||
|
||||
@ -836,9 +761,10 @@ sub run
|
||||
executeTest('rm -rf ' . $oHostDbMaster->dbPath() . qw{/} . $oManifest->walPath() . '/*');
|
||||
|
||||
$oHostDbMaster->restore(
|
||||
$strIncrBackup, undef, undef, $bDelta, $bForce, $strType, $strTarget, $bTargetExclusive, $strTargetAction,
|
||||
$strTargetTimeline, $oRecoveryHashRef, $strComment, $iExpectedExitStatus,
|
||||
'--tablespace-map-all=../../tablespace', false);
|
||||
undef, $strIncrBackup,
|
||||
{bForce => true, strType => CFGOPTVAL_RESTORE_TYPE_XID, strTarget => $strXidTarget,
|
||||
strTargetAction => $oHostDbMaster->pgVersion() >= PG_VERSION_91 ? 'promote' : undef,
|
||||
strOptionalParam => '--tablespace-map-all=../../tablespace', bTablespace => false});
|
||||
|
||||
# Save recovery file to test so we can use it in the next test
|
||||
storageDb()->copy(
|
||||
@ -854,18 +780,7 @@ sub run
|
||||
#---------------------------------------------------------------------------------------------------------------------------
|
||||
if ($bTestLocal)
|
||||
{
|
||||
$bDelta = false;
|
||||
$bForce = false;
|
||||
$strType = CFGOPTVAL_RESTORE_TYPE_PRESERVE;
|
||||
$strTarget = undef;
|
||||
$bTargetExclusive = undef;
|
||||
$strTargetAction = undef;
|
||||
$strTargetTimeline = undef;
|
||||
$oRecoveryHashRef = undef;
|
||||
$strComment = undef;
|
||||
$iExpectedExitStatus = undef;
|
||||
|
||||
&log(INFO, " testing recovery type = ${strType}");
|
||||
&log(INFO, ' testing recovery type = ' . CFGOPTVAL_RESTORE_TYPE_PRESERVE);
|
||||
|
||||
$oHostDbMaster->clusterStop();
|
||||
|
||||
@ -877,8 +792,7 @@ sub run
|
||||
storageDb()->move($self->testPath . '/recovery.conf', $oHostDbMaster->dbBasePath() . '/recovery.conf');
|
||||
|
||||
$oHostDbMaster->restore(
|
||||
cfgDefOptionDefault(CFGCMD_RESTORE, CFGOPT_SET), undef, undef, $bDelta, $bForce, $strType, $strTarget,
|
||||
$bTargetExclusive, $strTargetAction, $strTargetTimeline, $oRecoveryHashRef, $strComment, $iExpectedExitStatus);
|
||||
undef, cfgDefOptionDefault(CFGCMD_RESTORE, CFGOPT_SET), {strType => CFGOPTVAL_RESTORE_TYPE_PRESERVE});
|
||||
|
||||
$oHostDbMaster->clusterStart();
|
||||
$oHostDbMaster->sqlSelectOneTest('select message from test', $strXidMessage);
|
||||
@ -889,24 +803,12 @@ sub run
|
||||
# Restore (restore type = time, inclusive) - there is no exclusive time test because I can't find a way to find the
|
||||
# exact commit time of a transaction.
|
||||
#---------------------------------------------------------------------------------------------------------------------------
|
||||
$bDelta = true;
|
||||
$bForce = false;
|
||||
$strType = CFGOPTVAL_RESTORE_TYPE_TIME;
|
||||
$strTarget = $strTimeTarget;
|
||||
$bTargetExclusive = undef;
|
||||
$strTargetAction = undef;
|
||||
$strTargetTimeline = undef;
|
||||
$oRecoveryHashRef = undef;
|
||||
$strComment = undef;
|
||||
$iExpectedExitStatus = undef;
|
||||
|
||||
&log(INFO, " testing recovery type = ${strType}");
|
||||
&log(INFO, ' testing recovery type = ' . CFGOPTVAL_RESTORE_TYPE_TIME);
|
||||
|
||||
$oHostDbMaster->clusterStop();
|
||||
|
||||
$oHostDbMaster->restore(
|
||||
$strFullBackup, undef, undef, $bDelta, $bForce, $strType, $strTarget, $bTargetExclusive, $strTargetAction,
|
||||
$strTargetTimeline, $oRecoveryHashRef, $strComment, $iExpectedExitStatus);
|
||||
undef, $strFullBackup, {bDelta => true, strType => CFGOPTVAL_RESTORE_TYPE_TIME, strTarget => $strTimeTarget});
|
||||
|
||||
$oHostDbMaster->clusterStart();
|
||||
$oHostDbMaster->sqlSelectOneTest('select message from test', $strTimeMessage);
|
||||
@ -915,24 +817,13 @@ sub run
|
||||
#---------------------------------------------------------------------------------------------------------------------------
|
||||
if ($bTestLocal)
|
||||
{
|
||||
$bDelta = true;
|
||||
$bForce = false;
|
||||
$strType = CFGOPTVAL_RESTORE_TYPE_XID;
|
||||
$strTarget = $strXidTarget;
|
||||
$bTargetExclusive = true;
|
||||
$strTargetAction = undef;
|
||||
$strTargetTimeline = undef;
|
||||
$oRecoveryHashRef = undef;
|
||||
$strComment = undef;
|
||||
$iExpectedExitStatus = undef;
|
||||
|
||||
&log(INFO, " testing recovery type = ${strType}");
|
||||
&log(INFO, ' testing recovery type = ' . CFGOPTVAL_RESTORE_TYPE_XID);
|
||||
|
||||
$oHostDbMaster->clusterStop();
|
||||
|
||||
$oHostDbMaster->restore(
|
||||
$strIncrBackup, undef, undef, $bDelta, $bForce, $strType, $strTarget, $bTargetExclusive, $strTargetAction,
|
||||
$strTargetTimeline, $oRecoveryHashRef, $strComment, $iExpectedExitStatus);
|
||||
undef, $strIncrBackup,
|
||||
{bDelta => true, strType => CFGOPTVAL_RESTORE_TYPE_XID, strTarget => $strXidTarget, bTargetExclusive => true});
|
||||
|
||||
$oHostDbMaster->clusterStart();
|
||||
$oHostDbMaster->sqlSelectOneTest('select message from test', $strIncrMessage);
|
||||
@ -942,24 +833,13 @@ sub run
|
||||
#---------------------------------------------------------------------------------------------------------------------------
|
||||
if ($bTestLocal && $oHostDbMaster->pgVersion() >= PG_VERSION_91)
|
||||
{
|
||||
$bDelta = true;
|
||||
$bForce = true;
|
||||
$strType = CFGOPTVAL_RESTORE_TYPE_NAME;
|
||||
$strTarget = $strNameTarget;
|
||||
$bTargetExclusive = undef;
|
||||
$strTargetAction = undef;
|
||||
$strTargetTimeline = undef;
|
||||
$oRecoveryHashRef = undef;
|
||||
$strComment = undef;
|
||||
$iExpectedExitStatus = undef;
|
||||
|
||||
&log(INFO, " testing recovery type = ${strType}");
|
||||
&log(INFO, ' testing recovery type = ' . CFGOPTVAL_RESTORE_TYPE_NAME);
|
||||
|
||||
$oHostDbMaster->clusterStop();
|
||||
|
||||
$oHostDbMaster->restore(
|
||||
cfgDefOptionDefault(CFGCMD_RESTORE, CFGOPT_SET), undef, undef, $bDelta, $bForce, $strType, $strTarget,
|
||||
$bTargetExclusive, $strTargetAction, $strTargetTimeline, $oRecoveryHashRef, $strComment, $iExpectedExitStatus);
|
||||
undef, cfgDefOptionDefault(CFGCMD_RESTORE, CFGOPT_SET),
|
||||
{bDelta => true, bForce => true, strType => CFGOPTVAL_RESTORE_TYPE_NAME, strTarget => $strNameTarget});
|
||||
|
||||
$oHostDbMaster->clusterStart();
|
||||
$oHostDbMaster->sqlSelectOneTest('select message from test', $strNameMessage);
|
||||
@ -969,24 +849,14 @@ sub run
|
||||
#---------------------------------------------------------------------------------------------------------------------------
|
||||
if ($bTestLocal && $oHostDbMaster->pgVersion() >= PG_VERSION_84)
|
||||
{
|
||||
$bDelta = true;
|
||||
$bForce = false;
|
||||
$strType = CFGOPTVAL_RESTORE_TYPE_DEFAULT;
|
||||
$strTarget = undef;
|
||||
$bTargetExclusive = undef;
|
||||
$strTargetAction = undef;
|
||||
$strTargetTimeline = 4;
|
||||
$oRecoveryHashRef = $oHostDbMaster->pgVersion() >= PG_VERSION_90 ? {'standby-mode' => 'on'} : undef;
|
||||
$strComment = undef;
|
||||
$iExpectedExitStatus = undef;
|
||||
|
||||
&log(INFO, " testing recovery type = ${strType}");
|
||||
&log(INFO, ' testing recovery type = ' . CFGOPTVAL_RESTORE_TYPE_DEFAULT);
|
||||
|
||||
$oHostDbMaster->clusterStop();
|
||||
|
||||
$oHostDbMaster->restore(
|
||||
$strIncrBackup, undef, undef, $bDelta, $bForce, $strType, $strTarget, $bTargetExclusive, $strTargetAction,
|
||||
$strTargetTimeline, $oRecoveryHashRef, $strComment, $iExpectedExitStatus);
|
||||
undef, $strIncrBackup,
|
||||
{bDelta => true, strType => CFGOPTVAL_RESTORE_TYPE_DEFAULT, strTargetTimeline => 4,
|
||||
rhRecoveryHash => $oHostDbMaster->pgVersion() >= PG_VERSION_90 ? {'standby-mode' => 'on'} : undef});
|
||||
|
||||
$oHostDbMaster->clusterStart({bHotStandby => true});
|
||||
$oHostDbMaster->sqlSelectOneTest('select message from test', $strTimelineMessage, {iTimeout => 120});
|
||||
@ -1005,18 +875,15 @@ sub run
|
||||
|
||||
# Incr backup - make sure a --no-online backup fails
|
||||
#-----------------------------------------------------------------------------------------------------------------------
|
||||
$strType = CFGOPTVAL_BACKUP_TYPE_INCR;
|
||||
|
||||
$oHostBackup->backup(
|
||||
$strType, 'fail on --no-' . cfgOptionName(CFGOPT_ONLINE),
|
||||
CFGOPTVAL_BACKUP_TYPE_INCR, 'fail on --no-' . cfgOptionName(CFGOPT_ONLINE),
|
||||
{iExpectedExitStatus => ERROR_POSTMASTER_RUNNING, strOptionalParam => '--no-' . cfgOptionName(CFGOPT_ONLINE)});
|
||||
|
||||
# Incr backup - allow --no-online backup to succeed with --force
|
||||
#-----------------------------------------------------------------------------------------------------------------------
|
||||
$strType = CFGOPTVAL_BACKUP_TYPE_INCR;
|
||||
|
||||
$oHostBackup->backup(
|
||||
$strType, 'succeed on --no-' . cfgOptionName(CFGOPT_ONLINE) . ' with --' . cfgOptionName(CFGOPT_FORCE),
|
||||
CFGOPTVAL_BACKUP_TYPE_INCR,
|
||||
'succeed on --no-' . cfgOptionName(CFGOPT_ONLINE) . ' with --' . cfgOptionName(CFGOPT_FORCE),
|
||||
{strOptionalParam => '--no-' . cfgOptionName(CFGOPT_ONLINE) . ' --' . cfgOptionName(CFGOPT_FORCE)});
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user