1
0
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:
David Steele 2017-12-03 17:08:49 -05:00
parent 915ae5662a
commit 59fd92fdd5
5 changed files with 690 additions and 797 deletions

View File

@ -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>

View File

@ -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
####################################################################################################################################

View File

@ -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
####################################################################################################################################

View File

@ -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';

View File

@ -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)});
}
}