1
0
mirror of https://github.com/pgbackrest/pgbackrest.git synced 2025-12-21 23:47:33 +02:00
Files
pgbackrest/lib/BackRest/Info.pm
David Steele a59284a4cc v0.80: DBI Support, Stability, and Convenience Features
* Fixed an issue that caused the formatted timestamp for both the oldest and newest backups to be reported as the current time by the info command.  Only text output was affected -- json output reported the correct epoch values.  Reported by Michael Renner.

* Fixed protocol issue that was preventing ssh errors (especially on connection) from being logged.

* Now using Perl DBI and DBD::Pg for connections to PostgreSQL rather than psql.  The cmd-psql and cmd-psql-option settings have been removed and replaced with db-port and db-socket-path.

* Add stop-auto option to allow failed backups to automatically be stopped when a new backup starts.

* Add db-timeout option to limit the amount of time pgBackRest will wait for pg_start_backup() and pg_stop_backup() to return.

* Remove pg_control file at the beginning of the restore and copy it back at the very end.  This prevents the possibility that a partial restore can be started by PostgreSQL.

* The repository is now created and updated with consistent directory and file modes.  By default umask is set to 0000 but this can be disabled with the neutral-umask setting.

* Added checks to be sure the db-path setting is consistent with db-port by comparing the data_directory as reported by the cluster against the db-path setting and the version as reported by the cluster against the value read from pg_control.  The db-socket-path setting is checked to be sure it is an absolute path.

* Experimental support for PostgreSQL 9.5 alpha1.  This may break when the control version or WAL magic changes in future versions but will be updated in each pgBackRest release to keep pace.  All regression tests pass except for --target-resume tests (this functionality has changed in 9.5) and there is no testing yet for .partial WAL segments.

* Major refactoring of the protocol layer to support future development.

* Added vagrant test configurations for Ubuntu 14.04 and CentOS 7.

* Split most of README.md out into USERGUIDE.md and CHANGELOG.md because it was becoming unwieldy.  Changed most references to "database" in the user guide to "database cluster" for clarity.
2015-08-09 11:20:43 -04:00

344 lines
14 KiB
Perl

####################################################################################################################################
# INFO MODULE
####################################################################################################################################
package BackRest::Info;
use strict;
use warnings FATAL => qw(all);
use Carp qw(confess);
use Exporter qw(import);
use File::Basename qw(dirname);
use lib dirname($0);
use BackRest::BackupCommon;
use BackRest::BackupInfo;
use BackRest::Config;
use BackRest::File;
use BackRest::Ini;
use BackRest::Manifest;
use BackRest::Utility;
####################################################################################################################################
# Operation constants
####################################################################################################################################
use constant OP_INFO_LIST_STANZA => 'Info->listStanza';
our @EXPORT = qw(OP_INFO_LIST_STANZA);
####################################################################################################################################
# Info constants
####################################################################################################################################
use constant INFO_SECTION_BACKREST => 'backrest';
use constant INFO_SECTION_ARCHIVE => 'archive';
use constant INFO_SECTION_DB => 'database';
use constant INFO_SECTION_INFO => 'info';
use constant INFO_SECTION_REPO => 'repository';
use constant INFO_SECTION_TIMESTAMP => 'timestamp';
use constant INFO_SECTION_STATUS => 'status';
use constant INFO_STANZA_NAME => 'name';
use constant INFO_STANZA_STATUS_OK => 'ok';
use constant INFO_STANZA_STATUS_ERROR => 'error';
use constant INFO_STANZA_STATUS_OK_CODE => 0;
use constant INFO_STANZA_STATUS_OK_MESSAGE => INFO_STANZA_STATUS_OK;
use constant INFO_STANZA_STATUS_MISSING_STANZA_CODE => 1;
use constant INFO_STANZA_STATUS_MISSING_STANZA_MESSAGE => 'missing stanza path';
use constant INFO_STANZA_STATUS_NO_BACKUP_CODE => 2;
use constant INFO_STANZA_STATUS_NO_BACKUP_MESSAGE => 'no valid backups';
use constant INFO_KEY_CODE => 'code';
use constant INFO_KEY_DELTA => 'delta';
use constant INFO_KEY_FORMAT => 'format';
use constant INFO_KEY_ID => 'id';
use constant INFO_KEY_LABEL => 'label';
use constant INFO_KEY_MESSAGE => 'message';
use constant INFO_KEY_PRIOR => 'prior';
use constant INFO_KEY_REFERENCE => 'reference';
use constant INFO_KEY_SIZE => 'size';
use constant INFO_KEY_START => 'start';
use constant INFO_KEY_STOP => 'stop';
use constant INFO_KEY_SYSTEM_ID => 'system-id';
use constant INFO_KEY_TYPE => 'type';
use constant INFO_KEY_VERSION => 'version';
####################################################################################################################################
# CONSTRUCTOR
####################################################################################################################################
sub new
{
my $class = shift; # Class name
my $oFile = shift; # Remote object
# Create the class hash
my $self = {};
bless $self, $class;
# Set variables
$self->{oFile} = $oFile;
return $self;
}
####################################################################################################################################
# info
####################################################################################################################################
sub info
{
my $self = shift;
# Get stanza if specified
my $strStanza = optionTest(OPTION_STANZA) ? optionGet(OPTION_STANZA) : undef;
# Create the file object
my $oFile = new BackRest::File
(
$strStanza,
optionRemoteTypeTest(BACKUP) ? optionGet(OPTION_REPO_REMOTE_PATH) : optionGet(OPTION_REPO_PATH),
optionRemoteTypeTest(BACKUP) ? BACKUP : NONE,
protocolGet(!optionRemoteTypeTest(BACKUP))
);
# Get the stanza list with all info
my $oStanzaList = $self->listStanza($oFile, $strStanza);
if (optionTest(OPTION_OUTPUT, INFO_OUTPUT_TEXT))
{
my $strOutput;
foreach my $oStanzaInfo (@{$oStanzaList})
{
$strOutput = defined($strOutput) ? $strOutput .= "\n" : '';
$strOutput .= 'stanza ' . $$oStanzaInfo{&INFO_STANZA_NAME} . "\n";
$strOutput .= ' status: ' . ($$oStanzaInfo{&INFO_SECTION_STATUS}{&INFO_KEY_CODE} == 0 ? INFO_STANZA_STATUS_OK :
INFO_STANZA_STATUS_ERROR . ' (' . $$oStanzaInfo{&INFO_SECTION_STATUS}{&INFO_KEY_MESSAGE} . ')') . "\n";
if (@{$$oStanzaInfo{&INFO_BACKUP_SECTION_BACKUP}} > 0)
{
my $oOldestBackup = $$oStanzaInfo{&INFO_BACKUP_SECTION_BACKUP}[0];
$strOutput .= ' oldest backup label: ' . $$oOldestBackup{&INFO_KEY_LABEL} . "\n";
$strOutput .= ' oldest backup timestamp: ' .
timestamp_string_get(undef, $$oOldestBackup{&INFO_SECTION_TIMESTAMP}{&INFO_KEY_START}) . "\n";
my $oLatestBackup = $$oStanzaInfo{&INFO_BACKUP_SECTION_BACKUP}[@{$$oStanzaInfo{&INFO_BACKUP_SECTION_BACKUP}} - 1];
$strOutput .= ' latest backup label: ' . $$oLatestBackup{&INFO_KEY_LABEL} . "\n";
$strOutput .= ' latest backup timestamp: ' .
timestamp_string_get(undef, $$oLatestBackup{&INFO_SECTION_TIMESTAMP}{&INFO_KEY_START}) . "\n";
}
}
syswrite(*STDOUT, $strOutput);
}
elsif (optionTest(OPTION_OUTPUT, INFO_OUTPUT_JSON))
{
my $oJSON = JSON::PP->new()->canonical()->pretty()->indent_length(4);
my $strJSON = $oJSON->encode($oStanzaList);
syswrite(*STDOUT, $strJSON);
# On some systems a linefeed will be appended by encode() but others will not have it. In our case there should always
# be a terminating linefeed.
if ($strJSON !~ /\n$/)
{
syswrite(*STDOUT, "\n");
}
}
else
{
confess &log(ASSERT, "invalid info output option '" . optionGet(OPTION_OUTPUT) . "'");
}
return 0;
}
####################################################################################################################################
# listStanza
####################################################################################################################################
sub listStanza
{
my $self = shift;
my $oFile = shift;
my $strStanza = shift;
# Set operation and debug strings
my $strOperation = OP_INFO_LIST_STANZA;
&log(DEBUG, "${strOperation}". (defined($strStanza) ? ": stanza = ${strStanza}" : ''));
my @oStanzaList;
if ($oFile->is_remote(PATH_BACKUP))
{
# Build param hash
my $oParamHash = undef;
if (defined($strStanza))
{
$$oParamHash{'stanza'} = $strStanza;
}
# Trace the remote parameters
&log(TRACE, "${strOperation}: remote (" . $oFile->{oProtocol}->commandParamString($oParamHash) . ')');
# Execute the command
my $strStanzaList = $oFile->{oProtocol}->cmdExecute($strOperation, $oParamHash, true);
# Trace the remote response
&log(TRACE, "${strOperation}: remote json response (${strStanzaList})");
my $oJSON = JSON::PP->new();
return $oJSON->decode($strStanzaList);
}
else
{
my @stryStanza = $oFile->list(PATH_BACKUP, CMD_BACKUP, undef, undef, true);
foreach my $strStanzaFound (@stryStanza)
{
if (defined($strStanza) && $strStanza ne $strStanzaFound)
{
next;
}
my $oStanzaInfo = {};
$$oStanzaInfo{&INFO_STANZA_NAME} = $strStanzaFound;
($$oStanzaInfo{&INFO_BACKUP_SECTION_BACKUP}, $$oStanzaInfo{&INFO_BACKUP_SECTION_DB}) =
$self->listBackup($oFile, $strStanzaFound);
if (@{$$oStanzaInfo{&INFO_BACKUP_SECTION_BACKUP}} == 0)
{
$$oStanzaInfo{&INFO_SECTION_STATUS} =
{
&INFO_KEY_CODE => INFO_STANZA_STATUS_NO_BACKUP_CODE,
&INFO_KEY_MESSAGE => INFO_STANZA_STATUS_NO_BACKUP_MESSAGE
};
}
else
{
$$oStanzaInfo{&INFO_SECTION_STATUS} =
{
&INFO_KEY_CODE => INFO_STANZA_STATUS_OK_CODE,
&INFO_KEY_MESSAGE => INFO_STANZA_STATUS_OK_MESSAGE
};
}
push @oStanzaList, $oStanzaInfo;
}
if (defined($strStanza) && @oStanzaList == 0)
{
my $oStanzaInfo = {};
$$oStanzaInfo{&INFO_STANZA_NAME} = $strStanza;
$$oStanzaInfo{&INFO_SECTION_STATUS} =
{
&INFO_KEY_CODE => INFO_STANZA_STATUS_MISSING_STANZA_CODE,
&INFO_KEY_MESSAGE => INFO_STANZA_STATUS_MISSING_STANZA_MESSAGE
};
$$oStanzaInfo{&INFO_BACKUP_SECTION_BACKUP} = [];
$$oStanzaInfo{&INFO_BACKUP_SECTION_DB} = [];
push @oStanzaList, $oStanzaInfo;
}
}
return \@oStanzaList;
}
####################################################################################################################################
# listBackup
###################################################################################################################################
sub listBackup
{
my $self = shift;
my $oFile = shift;
my $strStanza = shift;
# Load or build backup.info
my $oBackupInfo = new BackRest::BackupInfo($oFile->path_get(PATH_BACKUP, CMD_BACKUP . "/${strStanza}"));
# Build the db list
my @oyDbList;
foreach my $iHistoryId ($oBackupInfo->keys(INFO_BACKUP_SECTION_DB_HISTORY))
{
my $oDbHash =
{
&INFO_KEY_ID => $iHistoryId,
&INFO_KEY_VERSION =>
$oBackupInfo->get(INFO_BACKUP_SECTION_DB_HISTORY, $iHistoryId, INFO_BACKUP_KEY_DB_VERSION),
&INFO_KEY_SYSTEM_ID =>
$oBackupInfo->get(INFO_BACKUP_SECTION_DB_HISTORY, $iHistoryId, INFO_BACKUP_KEY_SYSTEM_ID)
};
push(@oyDbList, $oDbHash);
}
# Build the backup list
my @oyBackupList;
foreach my $strBackup ($oBackupInfo->keys(INFO_BACKUP_SECTION_BACKUP_CURRENT))
{
my $oBackupHash =
{
&INFO_SECTION_ARCHIVE =>
{
&INFO_KEY_START =>
$oBackupInfo->get(INFO_BACKUP_SECTION_BACKUP_CURRENT, $strBackup, INFO_BACKUP_KEY_ARCHIVE_START, false),
&INFO_KEY_STOP =>
$oBackupInfo->get(INFO_BACKUP_SECTION_BACKUP_CURRENT, $strBackup, INFO_BACKUP_KEY_ARCHIVE_STOP, false),
},
&INFO_SECTION_BACKREST =>
{
&INFO_KEY_FORMAT =>
$oBackupInfo->getNumeric(INFO_BACKUP_SECTION_BACKUP_CURRENT, $strBackup, INI_KEY_FORMAT),
&INFO_KEY_VERSION =>
$oBackupInfo->get(INFO_BACKUP_SECTION_BACKUP_CURRENT, $strBackup, INI_KEY_VERSION)
},
&INFO_SECTION_DB =>
{
&INFO_KEY_ID =>
$oBackupInfo->getNumeric(INFO_BACKUP_SECTION_BACKUP_CURRENT, $strBackup, INFO_BACKUP_KEY_HISTORY_ID)
},
&INFO_SECTION_INFO =>
{
&INFO_SECTION_REPO =>
{
&INFO_KEY_SIZE =>
$oBackupInfo->get(INFO_BACKUP_SECTION_BACKUP_CURRENT, $strBackup, INFO_BACKUP_KEY_BACKUP_REPO_SIZE),
&INFO_KEY_DELTA =>
$oBackupInfo->get(INFO_BACKUP_SECTION_BACKUP_CURRENT, $strBackup, INFO_BACKUP_KEY_BACKUP_REPO_SIZE_DELTA),
},
&INFO_KEY_SIZE =>
$oBackupInfo->get(INFO_BACKUP_SECTION_BACKUP_CURRENT, $strBackup, INFO_BACKUP_KEY_BACKUP_SIZE),
&INFO_KEY_DELTA =>
$oBackupInfo->get(INFO_BACKUP_SECTION_BACKUP_CURRENT, $strBackup, INFO_BACKUP_KEY_BACKUP_SIZE_DELTA),
},
&INFO_SECTION_TIMESTAMP =>
{
&INFO_KEY_START =>
$oBackupInfo->getNumeric(INFO_BACKUP_SECTION_BACKUP_CURRENT, $strBackup, INFO_BACKUP_KEY_TIMESTAMP_START),
&INFO_KEY_STOP =>
$oBackupInfo->getNumeric(INFO_BACKUP_SECTION_BACKUP_CURRENT, $strBackup, INFO_BACKUP_KEY_TIMESTAMP_STOP),
},
&INFO_KEY_LABEL => $strBackup,
&INFO_KEY_PRIOR =>
$oBackupInfo->get(INFO_BACKUP_SECTION_BACKUP_CURRENT, $strBackup, INFO_BACKUP_KEY_PRIOR, false),
&INFO_KEY_REFERENCE =>
$oBackupInfo->get(INFO_BACKUP_SECTION_BACKUP_CURRENT, $strBackup, INFO_BACKUP_KEY_REFERENCE, false),
&INFO_KEY_TYPE =>
$oBackupInfo->get(INFO_BACKUP_SECTION_BACKUP_CURRENT, $strBackup, INFO_BACKUP_KEY_TYPE)
};
push(@oyBackupList, $oBackupHash);
}
return \@oyBackupList, \@oyDbList;
}
1;