mirror of
https://github.com/pgbackrest/pgbackrest.git
synced 2024-12-14 10:13:05 +02:00
275 lines
11 KiB
Perl
275 lines
11 KiB
Perl
####################################################################################################################################
|
|
# PROTOCOL MODULE
|
|
####################################################################################################################################
|
|
package pgBackRest::Protocol::Protocol;
|
|
|
|
use strict;
|
|
use warnings FATAL => qw(all);
|
|
use Carp qw(confess);
|
|
|
|
use Exporter qw(import);
|
|
our @EXPORT = qw();
|
|
|
|
use pgBackRest::Common::Log;
|
|
use pgBackRest::Config::Config;
|
|
use pgBackRest::Protocol::Common;
|
|
use pgBackRest::Protocol::RemoteMaster;
|
|
use pgBackRest::Version;
|
|
|
|
####################################################################################################################################
|
|
# Module variables
|
|
####################################################################################################################################
|
|
my $hProtocol = {}; # Global remote hash that is created on first request
|
|
|
|
####################################################################################################################################
|
|
# isRepoLocal
|
|
#
|
|
# Is the backup/archive repository local? This does not take into account the spool path.
|
|
####################################################################################################################################
|
|
sub isRepoLocal
|
|
{
|
|
# Not valid for remote
|
|
if (commandTest(CMD_REMOTE))
|
|
{
|
|
confess &log(ASSERT, 'isRepoLocal() not valid on remote');
|
|
}
|
|
|
|
return optionTest(OPTION_BACKUP_HOST) ? false : true;
|
|
}
|
|
|
|
push @EXPORT, qw(isRepoLocal);
|
|
|
|
####################################################################################################################################
|
|
# isDbLocal
|
|
#
|
|
# Is the database local?
|
|
####################################################################################################################################
|
|
sub isDbLocal
|
|
{
|
|
# Not valid for remote
|
|
if (commandTest(CMD_REMOTE))
|
|
{
|
|
confess &log(ASSERT, 'isDbLocal() not valid on remote');
|
|
}
|
|
|
|
return optionTest(OPTION_DB_HOST) ? false : true;
|
|
}
|
|
|
|
push @EXPORT, qw(isDbLocal);
|
|
|
|
####################################################################################################################################
|
|
# protocolGet
|
|
#
|
|
# Get the protocol object or create it if does not exist. Shared protocol objects are used because they create an SSH connection
|
|
# to the remote host and the number of these connections should be minimized.
|
|
####################################################################################################################################
|
|
sub protocolGet
|
|
{
|
|
# Assign function parameters, defaults, and log debug info
|
|
my
|
|
(
|
|
$strOperation,
|
|
$strRemoteType,
|
|
$iRemoteIdx,
|
|
$oParam,
|
|
) =
|
|
logDebugParam
|
|
(
|
|
__PACKAGE__ . '::protocolGet', \@_,
|
|
{name => 'strRemoteType'},
|
|
{name => 'iRemoteIdx', default => 1},
|
|
{name => 'oParam', required => false},
|
|
);
|
|
|
|
# Protocol object
|
|
my $oProtocol;
|
|
|
|
# If no remote requested or if the requested remote type is local then return a local protocol object
|
|
my $strRemoteHost = $strRemoteType eq NONE ? undef : optionIndex("${strRemoteType}-host", $iRemoteIdx);
|
|
|
|
if ($strRemoteType eq NONE || !optionTest($strRemoteHost))
|
|
{
|
|
logDebugMisc($strOperation, 'create local protocol');
|
|
|
|
$oProtocol = new pgBackRest::Protocol::Common
|
|
(
|
|
optionGet(OPTION_BUFFER_SIZE),
|
|
commandTest(CMD_EXPIRE) ? OPTION_DEFAULT_COMPRESS_LEVEL : optionGet(OPTION_COMPRESS_LEVEL),
|
|
commandTest(CMD_EXPIRE) ? OPTION_DEFAULT_COMPRESS_LEVEL_NETWORK : optionGet(OPTION_COMPRESS_LEVEL_NETWORK),
|
|
commandTest(CMD_EXPIRE) ? OPTION_PROTOCOL_TIMEOUT : optionGet(OPTION_PROTOCOL_TIMEOUT)
|
|
);
|
|
}
|
|
# Else create the remote protocol
|
|
else
|
|
{
|
|
my $bCache = defined($$oParam{bCache}) ? $$oParam{bCache} : true;
|
|
|
|
# Set protocol to cached value
|
|
$oProtocol =
|
|
$bCache && defined($$hProtocol{$strRemoteType}{$iRemoteIdx}) ? $$hProtocol{$strRemoteType}{$iRemoteIdx} : undef;
|
|
|
|
if ($bCache && $$hProtocol{$strRemoteType}{$iRemoteIdx})
|
|
{
|
|
$oProtocol = $$hProtocol{$strRemoteType}{$iRemoteIdx};
|
|
logDebugMisc($strOperation, 'found cached protocol');
|
|
}
|
|
|
|
# If protocol was not returned from cache then create it
|
|
if (!defined($oProtocol))
|
|
{
|
|
logDebugMisc($strOperation, 'create (' . ($bCache ? '' : 'un') . 'cached) remote protocol');
|
|
|
|
# Return the remote when required
|
|
my $strOptionCmd = OPTION_BACKUP_CMD;
|
|
my $strOptionConfig = OPTION_BACKUP_CONFIG;
|
|
my $strOptionHost = OPTION_BACKUP_HOST;
|
|
my $strOptionUser = OPTION_BACKUP_USER;
|
|
my $strOptionDbPort = undef;
|
|
my $strOptionDbSocketPath = undef;
|
|
|
|
if ($strRemoteType eq DB)
|
|
{
|
|
$strOptionCmd = optionIndex(OPTION_DB_CMD, $iRemoteIdx);
|
|
$strOptionConfig = optionIndex(OPTION_DB_CONFIG, $iRemoteIdx);
|
|
$strOptionHost = optionIndex(OPTION_DB_HOST, $iRemoteIdx);
|
|
$strOptionUser = optionIndex(OPTION_DB_USER, $iRemoteIdx);
|
|
}
|
|
|
|
# Db socket is not valid in all contexts (restore, for instance)
|
|
if (optionValid(optionIndex(OPTION_DB_PORT, $iRemoteIdx)))
|
|
{
|
|
$strOptionDbPort =
|
|
optionSource(optionIndex(OPTION_DB_PORT, $iRemoteIdx)) eq SOURCE_DEFAULT ?
|
|
undef : optionGet(optionIndex(OPTION_DB_PORT, $iRemoteIdx));
|
|
}
|
|
|
|
# Db socket is not valid in all contexts (restore, for instance)
|
|
if (optionValid(optionIndex(OPTION_DB_SOCKET_PATH, $iRemoteIdx)))
|
|
{
|
|
$strOptionDbSocketPath =
|
|
optionSource(optionIndex(OPTION_DB_SOCKET_PATH, $iRemoteIdx)) eq SOURCE_DEFAULT ?
|
|
undef : optionGet(optionIndex(OPTION_DB_SOCKET_PATH, $iRemoteIdx));
|
|
}
|
|
|
|
$oProtocol = new pgBackRest::Protocol::RemoteMaster
|
|
(
|
|
$strRemoteType,
|
|
optionGet(OPTION_CMD_SSH),
|
|
commandWrite(
|
|
CMD_REMOTE, true,
|
|
defined($oParam->{strBackRestBin}) ? $oParam->{strBackRestBin} : optionGet($strOptionCmd), undef,
|
|
{
|
|
&OPTION_COMMAND => {value => commandGet()},
|
|
&OPTION_PROCESS => {value => $$oParam{iProcessIdx}},
|
|
&OPTION_CONFIG => {
|
|
value => optionSource($strOptionConfig) eq SOURCE_DEFAULT ? undef : optionGet($strOptionConfig)},
|
|
&OPTION_TYPE => {value => $strRemoteType},
|
|
&OPTION_LOG_PATH => {},
|
|
&OPTION_LOCK_PATH => {},
|
|
&OPTION_DB_PORT => {value => $strOptionDbPort},
|
|
&OPTION_DB_SOCKET_PATH => {value => $strOptionDbSocketPath},
|
|
|
|
# ??? Not very pretty but will work until there is nicer code in commandWrite to handle this case. It
|
|
# doesn't hurt to pass these params but it's messy and distracting for debugging.
|
|
optionIndex(OPTION_DB_PORT, 2) => {},
|
|
optionIndex(OPTION_DB_SOCKET_PATH, 2) => {},
|
|
|
|
# Set protocol options explicitly so values are not picked up from remote config files
|
|
&OPTION_BUFFER_SIZE => {value => optionGet(OPTION_BUFFER_SIZE)},
|
|
&OPTION_COMPRESS_LEVEL => {value => optionGet(OPTION_COMPRESS_LEVEL)},
|
|
&OPTION_COMPRESS_LEVEL_NETWORK => {value => optionGet(OPTION_COMPRESS_LEVEL_NETWORK)},
|
|
&OPTION_PROTOCOL_TIMEOUT => {value => optionGet(OPTION_PROTOCOL_TIMEOUT)}
|
|
}),
|
|
optionGet(OPTION_BUFFER_SIZE),
|
|
optionGet(OPTION_COMPRESS_LEVEL),
|
|
optionGet(OPTION_COMPRESS_LEVEL_NETWORK),
|
|
optionGet($strOptionHost),
|
|
optionGet($strOptionUser),
|
|
optionGet(OPTION_PROTOCOL_TIMEOUT)
|
|
);
|
|
|
|
# Cache the protocol
|
|
if ($bCache)
|
|
{
|
|
$$hProtocol{$strRemoteType}{$iRemoteIdx} = $oProtocol;
|
|
}
|
|
}
|
|
}
|
|
|
|
# Return from function and log return values if any
|
|
return logDebugReturn
|
|
(
|
|
$strOperation,
|
|
{name => 'oProtocol', value => $oProtocol, trace => true}
|
|
);
|
|
}
|
|
|
|
push @EXPORT, qw(protocolGet);
|
|
|
|
####################################################################################################################################
|
|
# protocolDestroy
|
|
#
|
|
# Undefine the protocol if it is stored locally.
|
|
####################################################################################################################################
|
|
sub protocolDestroy
|
|
{
|
|
# Assign function parameters, defaults, and log debug info
|
|
my
|
|
(
|
|
$strOperation,
|
|
$strRemoteType,
|
|
$iRemoteIdx,
|
|
$bComplete,
|
|
) =
|
|
logDebugParam
|
|
(
|
|
__PACKAGE__ . '::protocolDestroy', \@_,
|
|
{name => 'strRemoteType', required => false},
|
|
{name => 'iRemoteIdx', required => false},
|
|
{name => 'bComplete', default => false},
|
|
);
|
|
|
|
my $iExitStatus = 0;
|
|
|
|
if (defined($strRemoteType))
|
|
{
|
|
$iRemoteIdx = defined($iRemoteIdx) ? $iRemoteIdx : 1;
|
|
|
|
if (defined($$hProtocol{$strRemoteType}{$iRemoteIdx}))
|
|
{
|
|
$iExitStatus = ($$hProtocol{$strRemoteType}{$iRemoteIdx})->close($bComplete);
|
|
delete($$hProtocol{$strRemoteType}{$iRemoteIdx});
|
|
}
|
|
}
|
|
else
|
|
{
|
|
foreach my $strRemoteType (sort(keys(%{$hProtocol})))
|
|
{
|
|
foreach my $iRemoteIdx (sort(keys(%{$$hProtocol{$strRemoteType}})))
|
|
{
|
|
if (defined($$hProtocol{$strRemoteType}{$iRemoteIdx}))
|
|
{
|
|
logDebugMisc(
|
|
$strOperation, 'found cached protocol',
|
|
{name => 'strRemoteType', value => $strRemoteType},
|
|
{name => 'iRemoteIdx', value => $iRemoteIdx});
|
|
|
|
$iExitStatus = ($$hProtocol{$strRemoteType}{$iRemoteIdx})->close($bComplete);
|
|
delete($$hProtocol{$strRemoteType}{$iRemoteIdx});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
# Return from function and log return values if any
|
|
return logDebugReturn
|
|
(
|
|
$strOperation,
|
|
{name => 'iExitStatus', value => $iExitStatus}
|
|
);
|
|
}
|
|
|
|
push @EXPORT, qw(protocolDestroy);
|
|
|
|
1;
|