1
0
mirror of https://github.com/pgbackrest/pgbackrest.git synced 2024-12-12 10:04:14 +02:00
pgbackrest/lib/pgBackRest/Stanza.pm
2016-11-30 14:15:11 -05:00

250 lines
8.7 KiB
Perl

####################################################################################################################################
# STANZA MODULE
#
# Contains functions for adding, upgrading and removing a stanza.
####################################################################################################################################
package pgBackRest::Stanza;
use strict;
use warnings FATAL => qw(all);
use Carp qw(confess);
use English '-no_match_vars';
use Exporter qw(import);
our @EXPORT = qw();
use pgBackRest::Common::Exception;
use pgBackRest::Common::Log;
use pgBackRest::Config::Config;
use pgBackRest::Archive;
use pgBackRest::ArchiveInfo;
use pgBackRest::BackupInfo;
use pgBackRest::Db;
use pgBackRest::DbVersion;
use pgBackRest::File;
use pgBackRest::FileCommon;
use pgBackRest::Protocol::Common;
use pgBackRest::Protocol::Protocol;
use IO::Uncompress::Gunzip qw($GunzipError);
####################################################################################################################################
# CONSTRUCTOR
####################################################################################################################################
sub new
{
my $class = shift; # Class name
# Create the class hash
my $self = {};
bless $self, $class;
# Assign function parameters, defaults, and log debug info
my $strOperation = logDebugParam(__PACKAGE__ . '->new');
# Return from function and log return values if any
return logDebugReturn
(
$strOperation,
{name => 'self', value => $self}
);
}
####################################################################################################################################
# Process Stanza Commands
####################################################################################################################################
sub process
{
my $self = shift;
# Assign function parameters, defaults, and log debug info
my ($strOperation) = logDebugParam(__PACKAGE__ . '->process');
my $iResult;
# Process stanza create
if (commandTest(CMD_STANZA_CREATE))
{
$iResult = $self->stanzaCreate();
}
# Else error if any other command is found
else
{
confess &log(ASSERT, "Stanza->process() called with invalid command: " . commandGet());
}
# Return from function and log return values if any
return logDebugReturn
(
$strOperation,
{name => 'iResult', value => $iResult, trace => true}
);
}
####################################################################################################################################
# stanzaCreate
#
# Creates the required data for the stanza.
####################################################################################################################################
sub stanzaCreate
{
my $self = shift;
# Assign function parameters, defaults, and log debug info
my ($strOperation) = logDebugParam(__PACKAGE__ . '->stanzaCreate');
# Initialize default file object with protocol set to NONE meaning strictly local
my $oFile = new pgBackRest::File
(
optionGet(OPTION_STANZA),
optionGet(OPTION_REPO_PATH),
protocolGet(NONE)
);
# Initialize the database object
my $oDb = dbMasterGet();
# Validate the database configuration - if the db-path in pgbackrest.conf does not match the pg_control
# then this will error alerting the user to fix the pgbackrest.conf
$oDb->configValidate();
my ($strDbVersion, $iControlVersion, $iCatalogVersion, $ullDbSysId) = $oDb->info();
# Initialize the result variables
my $iResult = 0;
my $strResultMessage = undef;
# Since restore points are only supported in PG>= 9.1 the check command may fail for older versions if there has been no write
# activity since the last log rotation. Therefore, manually create the files to be sure the backup and archive.info files
# get created. If the command is twice, just check the files if the DB version is older than 9.1.
# If the backup path does not exist, create it
if (!fileExists($oFile->pathGet(PATH_BACKUP_CLUSTER)))
{
# Create the cluster backup path
$oFile->pathCreate(PATH_BACKUP_CLUSTER, undef, undef, true, true);
}
# If the cluster backup path is empty then create the backup info file
my @stryBackupList = fileList($oFile->pathGet(PATH_BACKUP_CLUSTER), undef, 'forward', true);
my $strBackupInfo = &FILE_BACKUP_INFO;
my $oBackupInfo = new pgBackRest::BackupInfo($oFile->pathGet(PATH_BACKUP_CLUSTER));
if (!@stryBackupList)
{
# Create the backup.info file
$oBackupInfo->check($strDbVersion, $iControlVersion, $iCatalogVersion, $ullDbSysId);
$oBackupInfo->save();
}
elsif (!grep(/^$strBackupInfo$/i, @stryBackupList))
{
$iResult = &ERROR_BACKUP_DIR_INVALID;
$strResultMessage = "the backup directory is not empty but the backup.info file is missing\n" .
"HINT: Has the directory been copied from another location and the copy has not completed?"
}
elsif ($strDbVersion < PG_VERSION_91)
{
# Turn off console logging to control when to display the error
logLevelSet(undef, OFF);
eval
{
# Check that the backup info file is valid for the current database of the stanza
$oBackupInfo->check($strDbVersion, $iControlVersion, $iCatalogVersion, $ullDbSysId);
return true;
}
or do
{
# Confess unhandled errors
if (!isException($EVAL_ERROR))
{
confess $EVAL_ERROR;
}
# If this is a backrest error then capture the last code and message
$iResult = $EVAL_ERROR->code();
$strResultMessage = $EVAL_ERROR->message();
};
# Reset the console logging
logLevelSet(undef, optionGet(OPTION_LOG_LEVEL_CONSOLE));
}
if ($iResult == 0)
{
# If the archive path does not exist, create it
if (!fileExists($oFile->pathGet(PATH_BACKUP_ARCHIVE)))
{
# Create the cluster archive path
$oFile->pathCreate(PATH_BACKUP_ARCHIVE, undef, undef, true, true);
}
my @stryArchiveList = fileList($oFile->pathGet(PATH_BACKUP_ARCHIVE), undef, 'forward', true);
my $strArchiveInfo = &ARCHIVE_INFO_FILE;
my $oArchiveInfo = new pgBackRest::ArchiveInfo($oFile->pathGet(PATH_BACKUP_ARCHIVE), false);
if (!@stryArchiveList)
{
$oArchiveInfo->check($strDbVersion, $ullDbSysId);
}
elsif (!grep(/^$strArchiveInfo/i, @stryArchiveList ))
{
$iResult = &ERROR_ARCHIVE_DIR_INVALID;
$strResultMessage = "the archive directory is not empty but the archive.info file is missing\n" .
"HINT: Has the directory been copied from another location and the copy has not completed?"
}
elsif ($strDbVersion < PG_VERSION_91)
{
# Turn off console logging to control when to display the error
logLevelSet(undef, OFF);
eval
{
# check that the archive info file is valid for the current database of the stanza
$oArchiveInfo->check($strDbVersion, $ullDbSysId);
return true;
}
or do
{
# Confess unhandled errors
if (!isException($EVAL_ERROR))
{
confess $EVAL_ERROR;
}
# If this is a backrest error then capture the last code and message
$iResult = $EVAL_ERROR->code();
$strResultMessage = $EVAL_ERROR->message();
};
# Reset the console logging
logLevelSet(undef, optionGet(OPTION_LOG_LEVEL_CONSOLE));
}
}
# The DB function xlogSwitch checks for the version >= 9.1 and only performs the restore point if met so check would fail here
# on initial stanza-create for those systems, so only run the full check on systems > 9.1.
if (($iResult == 0) && ($strDbVersion >= PG_VERSION_91))
{
$iResult = new pgBackRest::Archive()->check();
}
if ($iResult == 0)
{
&log(INFO, "successfully created stanza " . optionGet(OPTION_STANZA));
}
else
{
&log(ERROR, $strResultMessage, $iResult);
&log(WARN, "the stanza " . optionGet(OPTION_STANZA) . " could not be created");
}
# Return from function and log return values if any
return logDebugReturn
(
$strOperation,
{name => 'iResult', value => $iResult, trace => true}
);
}
1;