1
0
mirror of https://github.com/pgbackrest/pgbackrest.git synced 2024-12-14 10:13:05 +02:00

Split command-line parameter processing out into a separate file. This is in preparation allowing all parameters to be specified/overridden on the command line, with pg_backrest.conf being option.

This commit is contained in:
David Steele 2015-03-08 13:26:09 -04:00
parent f115e01b71
commit ae6bdecfaf
10 changed files with 1253 additions and 372 deletions

View File

@ -15,6 +15,7 @@ use Pod::Usage;
use lib dirname($0) . '/../lib';
use BackRest::Utility;
use BackRest::Param;
use BackRest::Config;
use BackRest::Remote;
use BackRest::File;
@ -196,7 +197,7 @@ else
####################################################################################################################################
# ARCHIVE-PUSH Command
####################################################################################################################################
if (operation_get() eq OP_ARCHIVE_PUSH)
if (operationTest(OP_ARCHIVE_PUSH))
{
# Make sure the archive push operation happens on the db side
if ($strRemote eq DB)
@ -222,7 +223,7 @@ if (operation_get() eq OP_ARCHIVE_PUSH)
if ($bArchiveLocal)
{
$strStopFile = "${strArchivePath}/lock/" . param_get(PARAM_STANZA) . "-archive.stop";
$strStopFile = "${strArchivePath}/lock/" . optionGet(OPTION_STANZA) . "-archive.stop";
}
# If an archive file is defined, then push it
@ -244,7 +245,7 @@ if (operation_get() eq OP_ARCHIVE_PUSH)
# Create the file object
my $oFile = new BackRest::File
(
param_get(PARAM_STANZA),
optionGet(OPTION_STANZA),
config_key_load($strSection, CONFIG_KEY_PATH, true),
$bArchiveLocal ? NONE : $strRemote,
remote_get($bArchiveLocal, config_key_load(CONFIG_SECTION_ARCHIVE, CONFIG_KEY_COMPRESS_LEVEL),
@ -272,7 +273,7 @@ if (operation_get() eq OP_ARCHIVE_PUSH)
}
# Fork and exit the parent process so the async process can continue
if (!param_get(PARAM_TEST_NO_FORK))
if (!optionTest(OPTION_TEST_NO_FORK))
{
if (fork())
{
@ -295,7 +296,7 @@ if (operation_get() eq OP_ARCHIVE_PUSH)
&log(INFO, 'starting async archive-push');
# Create a lock file to make sure async archive-push does not run more than once
my $strLockPath = "${strArchivePath}/lock/" . param_get(PARAM_STANZA) . "-archive.lock";
my $strLockPath = "${strArchivePath}/lock/" . optionGet(OPTION_STANZA) . "-archive.lock";
if (!lock_file_create($strLockPath))
{
@ -304,7 +305,7 @@ if (operation_get() eq OP_ARCHIVE_PUSH)
}
# Build the basic command string that will be used to modify the command during processing
my $strCommand = $^X . ' ' . $0 . " --stanza=" . param_get(PARAM_STANZA);
my $strCommand = $^X . ' ' . $0 . " --stanza=" . optionGet(OPTION_STANZA);
# Get the new operational flags
my $bCompress = config_key_load(CONFIG_SECTION_BACKUP, CONFIG_KEY_COMPRESS, true, 'y') eq 'y' ? true : false;
@ -315,7 +316,7 @@ if (operation_get() eq OP_ARCHIVE_PUSH)
# Create the file object
my $oFile = new BackRest::File
(
param_get(PARAM_STANZA),
optionGet(OPTION_STANZA),
config_key_load(CONFIG_SECTION_BACKUP, CONFIG_KEY_PATH, true),
$strRemote,
remote_get(false, config_key_load(CONFIG_SECTION_ARCHIVE, CONFIG_KEY_COMPRESS_LEVEL),
@ -340,7 +341,7 @@ if (operation_get() eq OP_ARCHIVE_PUSH)
while (!defined($iLogTotal) || $iLogTotal > 0)
{
$iLogTotal = archive_xfer($strArchivePath . "/archive/" . param_get(PARAM_STANZA), $strStopFile,
$iLogTotal = archive_xfer($strArchivePath . "/archive/" . optionGet(OPTION_STANZA), $strStopFile,
$strCommand, $iArchiveMaxMB);
if ($iLogTotal > 0)
@ -402,7 +403,7 @@ if (operation_get() eq OP_ARCHIVE_PUSH)
####################################################################################################################################
# ARCHIVE-GET Command
####################################################################################################################################
if (operation_get() eq OP_ARCHIVE_GET)
if (operationTest(OP_ARCHIVE_GET))
{
# Make sure the archive file is defined
if (!defined($ARGV[1]))
@ -419,7 +420,7 @@ if (operation_get() eq OP_ARCHIVE_GET)
# Init the file object
my $oFile = new BackRest::File
(
param_get(PARAM_STANZA),
optionGet(OPTION_STANZA),
config_key_load(CONFIG_SECTION_BACKUP, CONFIG_KEY_PATH, true),
$strRemote,
remote_get(false,
@ -446,7 +447,7 @@ if (operation_get() eq OP_ARCHIVE_GET)
####################################################################################################################################
my $oFile = new BackRest::File
(
param_get(PARAM_STANZA),
optionGet(OPTION_STANZA),
config_key_load(CONFIG_SECTION_BACKUP, CONFIG_KEY_PATH, true),
$strRemote,
remote_get(false,
@ -457,7 +458,7 @@ my $oFile = new BackRest::File
####################################################################################################################################
# RESTORE
####################################################################################################################################
if (operation_get() eq OP_RESTORE)
if (operationTest(OP_RESTORE))
{
if ($strRemote eq DB)
{
@ -465,31 +466,31 @@ if (operation_get() eq OP_RESTORE)
}
# Open the log file
log_file_set(config_key_load(CONFIG_SECTION_RESTORE, CONFIG_KEY_PATH, true) . '/log/' . param_get(PARAM_STANZA) . '-restore');
log_file_set(config_key_load(CONFIG_SECTION_RESTORE, CONFIG_KEY_PATH, true) . '/log/' . optionGet(OPTION_STANZA) . '-restore');
# Set the lock path
my $strLockPath = config_key_load(CONFIG_SECTION_RESTORE, CONFIG_KEY_PATH, true) . '/lock/' .
param_get(PARAM_STANZA) . '-' . operation_get() . '.lock';
optionGet(OPTION_STANZA) . '-' . operationGet() . '.lock';
# Do the restore
new BackRest::Restore
(
config_key_load(CONFIG_SECTION_STANZA, CONFIG_KEY_PATH, true),
param_get(PARAM_SET),
optionGet(OPTION_SET),
config_section_load(CONFIG_SECTION_TABLESPACE_MAP),
$oFile,
config_key_load(CONFIG_SECTION_RESTORE, CONFIG_KEY_THREAD_MAX, true),
param_get(PARAM_DELTA),
param_get(PARAM_FORCE),
param_get(PARAM_TYPE),
param_get(PARAM_TARGET),
param_get(PARAM_TARGET_EXCLUSIVE),
param_get(PARAM_TARGET_RESUME),
param_get(PARAM_TARGET_TIMELINE),
optionGet(OPTION_DELTA),
optionGet(OPTION_FORCE),
optionGet(OPTION_TYPE),
optionGet(OPTION_TARGET, false),
optionGet(OPTION_TARGET_EXCLUSIVE, false),
optionGet(OPTION_TARGET_RESUME, false),
optionGet(OPTION_TARGET_TIMELINE, false),
config_section_load(CONFIG_SECTION_RECOVERY_OPTION),
param_get(PARAM_STANZA),
optionGet(OPTION_STANZA),
$0,
param_get(PARAM_CONFIG)
optionGet(OPTION_CONFIG)
)->restore;
remote_exit(0);
@ -499,7 +500,7 @@ if (operation_get() eq OP_RESTORE)
# GET MORE CONFIG INFO
####################################################################################################################################
# Open the log file
log_file_set(config_key_load(CONFIG_SECTION_BACKUP, CONFIG_KEY_PATH, true) . '/log/' . param_get(PARAM_STANZA));
log_file_set(config_key_load(CONFIG_SECTION_BACKUP, CONFIG_KEY_PATH, true) . '/log/' . optionGet(OPTION_STANZA));
# Make sure backup and expire operations happen on the backup side
if ($strRemote eq BACKUP)
@ -512,18 +513,18 @@ my $bCompress = config_key_load(CONFIG_SECTION_BACKUP, CONFIG_KEY_COMPRESS, true
# Set the lock path
my $strLockPath = config_key_load(CONFIG_SECTION_BACKUP, CONFIG_KEY_PATH, true) . '/lock/' .
param_get(PARAM_STANZA) . '-' . operation_get() . '.lock';
optionGet(OPTION_STANZA) . '-' . operationGet() . '.lock';
if (!lock_file_create($strLockPath))
{
&log(ERROR, 'backup process is already running for stanza ' . param_get(PARAM_STANZA) . ' - exiting');
&log(ERROR, 'backup process is already running for stanza ' . optionGet(OPTION_STANZA) . ' - exiting');
remote_exit(0);
}
# Initialize the db object
my $oDb;
if (!param_get(PARAM_NO_START_STOP))
if (!optionGet(OPTION_NO_START_STOP))
{
$oDb = new BackRest::Db
(
@ -538,31 +539,31 @@ backup_init
(
$oDb,
$oFile,
param_get(PARAM_TYPE),
optionGet(OPTION_TYPE),
config_key_load(CONFIG_SECTION_BACKUP, CONFIG_KEY_COMPRESS, true, 'y') eq 'y' ? true : false,
config_key_load(CONFIG_SECTION_BACKUP, CONFIG_KEY_HARDLINK, true, 'y') eq 'y' ? true : false,
config_key_load(CONFIG_SECTION_BACKUP, CONFIG_KEY_THREAD_MAX),
config_key_load(CONFIG_SECTION_BACKUP, CONFIG_KEY_ARCHIVE_REQUIRED, true, 'y') eq 'y' ? true : false,
config_key_load(CONFIG_SECTION_BACKUP, CONFIG_KEY_THREAD_TIMEOUT),
param_get(PARAM_NO_START_STOP),
param_get(PARAM_FORCE)
optionGet(OPTION_NO_START_STOP),
optionTest(OPTION_FORCE)
);
####################################################################################################################################
# BACKUP
####################################################################################################################################
if (operation_get() eq OP_BACKUP)
if (operationTest(OP_BACKUP))
{
backup(config_key_load(CONFIG_SECTION_STANZA, CONFIG_KEY_PATH),
config_key_load(CONFIG_SECTION_BACKUP, CONFIG_KEY_START_FAST, true, 'n') eq 'y' ? true : false);
operation_set(OP_EXPIRE);
operationSet(OP_EXPIRE);
}
####################################################################################################################################
# EXPIRE
####################################################################################################################################
if (operation_get() eq OP_EXPIRE)
if (operationTest(OP_EXPIRE))
{
backup_expire
(

View File

@ -16,6 +16,7 @@ use Thread::Queue;
use lib dirname($0);
use BackRest::Utility;
use BackRest::Exception;
use BackRest::Param;
use BackRest::Config;
use BackRest::Manifest;
use BackRest::File;

View File

@ -7,33 +7,21 @@ use strict;
use warnings FATAL => qw(all);
use Carp qw(confess);
use Pod::Usage;
use File::Basename;
use Getopt::Long;
use lib dirname($0) . '/../lib';
use BackRest::Exception;
use BackRest::Utility;
use BackRest::Param;
use Exporter qw(import);
our @EXPORT = qw(config_load config_key_load config_section_load operation_get operation_set param_get
our @EXPORT = qw(config_load config_key_load config_section_load
FILE_MANIFEST FILE_VERSION FILE_POSTMASTER_PID FILE_RECOVERY_CONF
PATH_LATEST
OP_ARCHIVE_GET OP_ARCHIVE_PUSH OP_BACKUP OP_RESTORE OP_EXPIRE
BACKUP_TYPE_FULL BACKUP_TYPE_DIFF BACKUP_TYPE_INCR
RECOVERY_TYPE_NAME RECOVERY_TYPE_TIME RECOVERY_TYPE_XID RECOVERY_TYPE_PRESERVE RECOVERY_TYPE_NONE
RECOVERY_TYPE_DEFAULT
PARAM_CONFIG PARAM_STANZA PARAM_TYPE PARAM_DELTA PARAM_SET PARAM_NO_START_STOP PARAM_FORCE PARAM_TARGET
PARAM_TARGET_EXCLUSIVE PARAM_TARGET_RESUME PARAM_TARGET_TIMELINE CONFIG_SECTION_RECOVERY
PARAM_VERSION PARAM_HELP PARAM_TEST PARAM_TEST_DELAY PARAM_TEST_NO_FORK
CONFIG_SECTION_COMMAND CONFIG_SECTION_GENERAL CONFIG_SECTION_COMMAND_OPTION CONFIG_SECTION_LOG CONFIG_SECTION_BACKUP
CONFIG_SECTION_RESTORE CONFIG_SECTION_RECOVERY CONFIG_SECTION_RECOVERY_OPTION CONFIG_SECTION_TABLESPACE_MAP
CONFIG_SECTION_ARCHIVE CONFIG_SECTION_RETENTION CONFIG_SECTION_STANZA
@ -65,68 +53,6 @@ use constant
FILE_VERSION => 'version',
FILE_POSTMASTER_PID => 'postmaster.pid',
FILE_RECOVERY_CONF => 'recovery.conf',
PATH_LATEST => 'latest'
};
####################################################################################################################################
# Operation constants - basic operations that are allowed in backrest
####################################################################################################################################
use constant
{
OP_ARCHIVE_GET => 'archive-get',
OP_ARCHIVE_PUSH => 'archive-push',
OP_BACKUP => 'backup',
OP_RESTORE => 'restore',
OP_EXPIRE => 'expire'
};
####################################################################################################################################
# BACKUP Type Constants
####################################################################################################################################
use constant
{
BACKUP_TYPE_FULL => 'full',
BACKUP_TYPE_DIFF => 'diff',
BACKUP_TYPE_INCR => 'incr'
};
####################################################################################################################################
# RECOVERY Type Constants
####################################################################################################################################
use constant
{
RECOVERY_TYPE_NAME => 'name',
RECOVERY_TYPE_TIME => 'time',
RECOVERY_TYPE_XID => 'xid',
RECOVERY_TYPE_PRESERVE => 'preserve',
RECOVERY_TYPE_NONE => 'none',
RECOVERY_TYPE_DEFAULT => 'default'
};
####################################################################################################################################
# Parameter constants
####################################################################################################################################
use constant
{
PARAM_CONFIG => 'config',
PARAM_STANZA => 'stanza',
PARAM_TYPE => 'type',
PARAM_NO_START_STOP => 'no-start-stop',
PARAM_DELTA => 'delta',
PARAM_SET => 'set',
PARAM_FORCE => 'force',
PARAM_VERSION => 'version',
PARAM_HELP => 'help',
PARAM_TARGET => 'target',
PARAM_TARGET_EXCLUSIVE => 'target-exclusive',
PARAM_TARGET_RESUME => 'target-resume',
PARAM_TARGET_TIMELINE => 'target-timeline',
PARAM_TEST => 'test',
PARAM_TEST_DELAY => 'test-delay',
PARAM_TEST_NO_FORK => 'no-fork'
};
####################################################################################################################################
@ -216,8 +142,6 @@ use constant
# Global variables
####################################################################################################################################
my %oConfig; # Configuration hash
my %oParam = (); # Parameter hash
my $strOperation; # Operation (backup, archive-get, ...)
####################################################################################################################################
# CONFIG_LOAD
@ -228,61 +152,9 @@ sub config_load
{
my $strFile = shift; # Full path to ini file to load from
# Default for general parameters
param_set(PARAM_NO_START_STOP, false); # Do not perform start/stop backup (and archive-required gets set to false)
param_set(PARAM_FORCE, false); # Force an action that would not normally be allowed (varies by action)
param_set(PARAM_VERSION, false); # Display version and exit
param_set(PARAM_HELP, false); # Display help and exit
# Defaults for test parameters - not for general use
param_set(PARAM_TEST_NO_FORK, false); # Prevents the archive process from forking when local archiving is enabled
param_set(PARAM_TEST, false); # Enters test mode - not harmful, but adds special logging and pauses for unit testing
param_set(PARAM_TEST_DELAY, 5); # Seconds to delay after a test point (default is not enough for manual tests)
# Get command line parameters
GetOptions (\%oParam, PARAM_CONFIG . '=s', PARAM_STANZA . '=s', PARAM_TYPE . '=s', PARAM_DELTA, PARAM_SET . '=s',
PARAM_NO_START_STOP, PARAM_FORCE, PARAM_TARGET . '=s', PARAM_TARGET_EXCLUSIVE, PARAM_TARGET_RESUME,
PARAM_TARGET_TIMELINE . '=s', PARAM_VERSION, PARAM_HELP,
PARAM_TEST, PARAM_TEST_DELAY . '=s', PARAM_TEST_NO_FORK)
or pod2usage(2);
# Display version and exit if requested
if (param_get(PARAM_VERSION) || param_get(PARAM_HELP))
{
print 'pg_backrest ' . version_get() . "\n";
if (!param_get(PARAM_HELP))
{
exit 0;
}
}
# Display help and exit if requested
if (param_get(PARAM_HELP))
{
print "\n";
pod2usage();
}
# Get and validate the operation
$strOperation = $ARGV[0];
# Validate params
param_valid();
# # Validate thread parameter
# if (defined(param_get(PARAM_THREAD)) && !(param_get(PARAM_THREAD) >= 1))
# {
# confess &log(ERROR, 'thread parameter should be >= 1');
# }
# Get configuration parameter and load it
if (!defined(param_get(PARAM_CONFIG)))
{
param_set(PARAM_CONFIG, '/etc/pg_backrest.conf');
}
ini_load(param_get(PARAM_CONFIG), \%oConfig);
# Load parameters
configLoad();
ini_load(optionGet(OPTION_CONFIG), \%oConfig);
# If this is a restore, then try to default config
if (!defined(config_key_load(CONFIG_SECTION_RESTORE, CONFIG_KEY_PATH)))
@ -304,9 +176,6 @@ sub config_load
# Validate config
config_valid();
# Set test parameters
test_set(param_get(PARAM_TEST), param_get(PARAM_TEST_DELAY));
}
####################################################################################################################################
@ -316,7 +185,7 @@ sub config_section_load
{
my $strSection = shift;
$strSection = param_get(PARAM_STANZA) . ':' . $strSection;
$strSection = optionGet(OPTION_STANZA) . ':' . $strSection;
return $oConfig{$strSection};
}
@ -342,13 +211,13 @@ sub config_key_load
# Look in the default stanza section
if ($strSection eq CONFIG_SECTION_STANZA)
{
$strValue = $oConfig{param_get(PARAM_STANZA)}{"${strKey}"};
$strValue = $oConfig{optionGet(OPTION_STANZA)}{"${strKey}"};
}
# Else look in the supplied section
else
{
# First check the stanza section
$strValue = $oConfig{param_get(PARAM_STANZA) . ":${strSection}"}{"${strKey}"};
$strValue = $oConfig{optionGet(OPTION_STANZA) . ":${strSection}"}{"${strKey}"};
# If the stanza section value is undefined then check global
if (!defined($strValue))
@ -396,7 +265,7 @@ sub config_key_set
}
# Set the value
$strSection = param_get(PARAM_STANZA) . ':' . $strSection;
$strSection = optionGet(OPTION_STANZA) . ':' . $strSection;
$oConfig{$strSection}{$strKey} = $strValue;
}
@ -413,7 +282,7 @@ sub config_valid
my $oSectionHashRef;
# Check [stanza]:recovery:option section
$strSection = param_get(PARAM_STANZA) . ':' . CONFIG_SECTION_RECOVERY_OPTION;
$strSection = optionGet(OPTION_STANZA) . ':' . CONFIG_SECTION_RECOVERY_OPTION;
$oSectionHashRef = $oConfig{$strSection};
if (defined($oSectionHashRef) && keys($oSectionHashRef) != 0)
@ -511,7 +380,7 @@ sub config_key_valid
$oConfig{$strSection}{$strKey} = $strValue;
# Also do validation for the stanza section
my $strStanza = param_get(PARAM_STANZA);
my $strStanza = optionGet(OPTION_STANZA);
if (substr($strSection, 0, length($strStanza) + 1) ne "${strStanza}:")
{
@ -519,177 +388,4 @@ sub config_key_valid
}
}
####################################################################################################################################
# PARAM_VALID
#
# Make sure the command-line parameters are valid.
####################################################################################################################################
sub param_valid
{
# Check the stanza
if (!defined(param_get(PARAM_STANZA)))
{
confess 'a backup stanza must be specified';
}
# Check that the operation is present and valid
if (!defined($strOperation))
{
confess &log(ERROR, "operation must be specified", ERROR_PARAM);
}
if ($strOperation ne OP_ARCHIVE_GET &&
$strOperation ne OP_ARCHIVE_PUSH &&
$strOperation ne OP_BACKUP &&
$strOperation ne OP_RESTORE &&
$strOperation ne OP_EXPIRE)
{
confess &log(ERROR, "invalid operation ${strOperation}");
}
# Check type param
my $strParam = PARAM_TYPE;
my $strType = param_get($strParam);
# Type is only valid for backup and restore operations
if (operation_test(OP_BACKUP) || operation_test(OP_RESTORE))
{
# Check types for backup
if (operation_test(OP_BACKUP))
{
# If type is not defined set to BACKUP_TYPE_INCR
if (!defined($strType))
{
$strType = BACKUP_TYPE_INCR;
param_set($strParam, $strType);
}
# Check that type is in valid list
if (!($strType eq BACKUP_TYPE_FULL || $strType eq BACKUP_TYPE_DIFF || $strType eq BACKUP_TYPE_INCR))
{
confess &log(ERROR, "invalid type '${strType}' for ${strOperation}, must be: '" . BACKUP_TYPE_FULL . "', '" .
BACKUP_TYPE_DIFF . "', '" . BACKUP_TYPE_INCR . "'", ERROR_PARAM);
}
}
# Check types for restore
elsif (operation_test(OP_RESTORE))
{
# If type is not defined set to RECOVERY_TYPE_DEFAULT
if (!defined($strType))
{
$strType = RECOVERY_TYPE_DEFAULT;
param_set($strParam, $strType);
}
if (!($strType eq RECOVERY_TYPE_NAME || $strType eq RECOVERY_TYPE_TIME || $strType eq RECOVERY_TYPE_XID ||
$strType eq RECOVERY_TYPE_PRESERVE || $strType eq RECOVERY_TYPE_NONE || $strType eq RECOVERY_TYPE_DEFAULT))
{
confess &log(ERROR, "invalid type '${strType}' for ${strOperation}, must be: '" . RECOVERY_TYPE_NAME .
"', '" . RECOVERY_TYPE_TIME . "', '" . RECOVERY_TYPE_XID . "', '" . RECOVERY_TYPE_PRESERVE .
"', '" . RECOVERY_TYPE_NONE . "', '" . RECOVERY_TYPE_DEFAULT . "'", ERROR_PARAM);
}
}
}
else
{
if (defined($strType))
{
confess &log(ERROR, PARAM_TYPE . ' is only valid for '. OP_BACKUP . ' and ' . OP_RESTORE . ' operations', ERROR_PARAM);
}
}
# Check target param
$strParam = PARAM_TARGET;
my $strTarget = param_get($strParam);
my $strTargetMessage = 'for ' . OP_RESTORE . " operations where type is '" . RECOVERY_TYPE_NAME .
"', '" . RECOVERY_TYPE_TIME . "', or '" . RECOVERY_TYPE_XID . "'";
if (operation_test(OP_RESTORE) &&
($strType eq RECOVERY_TYPE_NAME || $strType eq RECOVERY_TYPE_TIME || $strType eq RECOVERY_TYPE_XID))
{
if (!defined($strTarget))
{
confess &log(ERROR, PARAM_TARGET . ' is required ' . $strTargetMessage, ERROR_PARAM);
}
}
elsif (defined($strTarget))
{
confess &log(ERROR, PARAM_TARGET . ' is only required ' . $strTargetMessage, ERROR_PARAM);
}
# Check target-resume - can only be used when target is specified
if (defined(param_get(PARAM_TARGET_RESUME)) && !defined($strTarget))
{
confess &log(ERROR, PARAM_TARGET_RESUME . ' and ' . PARAM_TARGET_TIMELINE .
' are only valid when target is specified', ERROR_PARAM);
}
# Check target-exclusive - can only be used when target is time or xid
if (defined(param_get(PARAM_TARGET_EXCLUSIVE)) && !($strType eq RECOVERY_TYPE_TIME || $strType eq RECOVERY_TYPE_XID))
{
confess &log(ERROR, PARAM_TARGET_EXCLUSIVE . ' is only valid when target is specified and recovery type is ' .
RECOVERY_TYPE_TIME . ' or ' . RECOVERY_TYPE_XID, ERROR_PARAM);
}
}
####################################################################################################################################
# OPERATION_GET
#
# Get the current operation.
####################################################################################################################################
sub operation_get
{
return $strOperation;
}
####################################################################################################################################
# OPERATION_TEST
#
# Test the current operation.
####################################################################################################################################
sub operation_test
{
my $strOperationTest = shift;
return $strOperationTest eq $strOperation;
}
####################################################################################################################################
# OPERATION_SET
#
# Set current operation (usually for triggering follow-on operations).
####################################################################################################################################
sub operation_set
{
my $strValue = shift;
$strOperation = $strValue;
}
####################################################################################################################################
# PARAM_GET
#
# Get param value.
####################################################################################################################################
sub param_get
{
my $strParam = shift;
return $oParam{$strParam};
}
####################################################################################################################################
# PARAM_SET
#
# Set param value.
####################################################################################################################################
sub param_set
{
my $strParam = shift;
my $strValue = shift;
$oParam{$strParam} = $strValue;
}
1;

View File

@ -11,21 +11,25 @@ use Carp qw(confess);
# Exports
####################################################################################################################################
use Exporter qw(import);
our @EXPORT = qw(ERROR_CHECKSUM ERROR_CONFIG ERROR_PARAM ERROR_POSTMASTER_RUNNING ERROR_PROTOCOL ERROR_RESTORE_PATH_NOT_EMPTY
ERROR_FORMAT);
our @EXPORT = qw(ERROR_ASSERT ERROR_CHECKSUM ERROR_CONFIG ERROR_OPERATION_REQUIRED ERROR_OPTION_REQUIRED ERROR_OPTION_INVALID
ERROR_OPTION_INVALID_VALUE ERROR_POSTMASTER_RUNNING ERROR_PROTOCOL ERROR_RESTORE_PATH_NOT_EMPTY ERROR_FORMAT);
####################################################################################################################################
# Exception Codes
####################################################################################################################################
use constant
{
ERROR_CHECKSUM => 100,
ERROR_CONFIG => 101,
ERROR_PARAM => 102,
ERROR_RESTORE_PATH_NOT_EMPTY => 103,
ERROR_POSTMASTER_RUNNING => 104,
ERROR_PROTOCOL => 105,
ERROR_FORMAT => 106
ERROR_ASSERT => 100,
ERROR_CHECKSUM => 101,
ERROR_CONFIG => 102,
ERROR_FORMAT => 103,
ERROR_OPERATION_REQUIRED => 104,
ERROR_OPTION_REQUIRED => 105,
ERROR_OPTION_INVALID => 106,
ERROR_OPTION_INVALID_VALUE => 107,
ERROR_POSTMASTER_RUNNING => 108,
ERROR_PROTOCOL => 109,
ERROR_RESTORE_PATH_NOT_EMPTY => 110
};
####################################################################################################################################

785
lib/BackRest/Param.pm Normal file
View File

@ -0,0 +1,785 @@
####################################################################################################################################
# PARAM MODULE
####################################################################################################################################
package BackRest::Param;
use strict;
use warnings FATAL => qw(all);
use Carp qw(confess);
use Pod::Usage;
use File::Basename;
use Getopt::Long qw(GetOptions);
use Storable qw(dclone);
use lib dirname($0) . '/../lib';
use BackRest::Exception;
use BackRest::Utility;
use Exporter qw(import);
our @EXPORT = qw(configLoad optionGet optionTest optionRuleGet operationGet operationTest operationSet
OP_ARCHIVE_GET OP_ARCHIVE_PUSH OP_BACKUP OP_RESTORE OP_EXPIRE
BACKUP_TYPE_FULL BACKUP_TYPE_DIFF BACKUP_TYPE_INCR
RECOVERY_TYPE_NAME RECOVERY_TYPE_TIME RECOVERY_TYPE_XID RECOVERY_TYPE_PRESERVE RECOVERY_TYPE_NONE
RECOVERY_TYPE_DEFAULT
OPTION_CONFIG OPTION_STANZA OPTION_TYPE OPTION_DELTA OPTION_SET OPTION_NO_START_STOP OPTION_FORCE OPTION_TARGET
OPTION_TARGET_EXCLUSIVE OPTION_TARGET_RESUME OPTION_TARGET_TIMELINE OPTION_THREAD_MAX
OPTION_VERSION OPTION_HELP OPTION_TEST OPTION_TEST_DELAY OPTION_TEST_NO_FORK
OPTION_DEFAULT_RESTORE_SET);
####################################################################################################################################
# Operation constants - basic operations that are allowed in backrest
####################################################################################################################################
use constant
{
OP_ARCHIVE_GET => 'archive-get',
OP_ARCHIVE_PUSH => 'archive-push',
OP_BACKUP => 'backup',
OP_RESTORE => 'restore',
OP_EXPIRE => 'expire'
};
####################################################################################################################################
# BACKUP Type Constants
####################################################################################################################################
use constant
{
BACKUP_TYPE_FULL => 'full',
BACKUP_TYPE_DIFF => 'diff',
BACKUP_TYPE_INCR => 'incr'
};
####################################################################################################################################
# RECOVERY Type Constants
####################################################################################################################################
use constant
{
RECOVERY_TYPE_NAME => 'name',
RECOVERY_TYPE_TIME => 'time',
RECOVERY_TYPE_XID => 'xid',
RECOVERY_TYPE_PRESERVE => 'preserve',
RECOVERY_TYPE_NONE => 'none',
RECOVERY_TYPE_DEFAULT => 'default'
};
####################################################################################################################################
# Option constants
####################################################################################################################################
use constant
{
# Command-line-only options
OPTION_CONFIG => 'config',
OPTION_DELTA => 'delta',
OPTION_FORCE => 'force',
OPTION_NO_START_STOP => 'no-start-stop',
OPTION_SET => 'set',
OPTION_STANZA => 'stanza',
OPTION_TARGET => 'target',
OPTION_TARGET_EXCLUSIVE => 'target-exclusive',
OPTION_TARGET_RESUME => 'target-resume',
OPTION_TARGET_TIMELINE => 'target-timeline',
OPTION_TYPE => 'type',
# Command-line-only/conf file options
OPTION_THREAD_MAX => 'thread-max',
# Command-line-only help/version options
OPTION_HELP => 'help',
OPTION_VERSION => 'version',
# Command-line-only test options
OPTION_TEST => 'test',
OPTION_TEST_DELAY => 'test-delay',
OPTION_TEST_NO_FORK => 'no-fork'
};
####################################################################################################################################
# Option Defaults
####################################################################################################################################
use constant
{
OPTION_DEFAULT_CONFIG => '/etc/pg_backrest.conf',
OPTION_DEFAULT_THREAD_MAX => 1,
OPTION_DEFAULT_BACKUP_FORCE => false,
OPTION_DEFAULT_BACKUP_NO_START_STOP => false,
OPTION_DEFAULT_BACKUP_TYPE => BACKUP_TYPE_INCR,
OPTION_DEFAULT_RESTORE_DELTA => false,
OPTION_DEFAULT_RESTORE_FORCE => false,
OPTION_DEFAULT_RESTORE_SET => 'latest',
OPTION_DEFAULT_RESTORE_TYPE => RECOVERY_TYPE_DEFAULT,
OPTION_DEFAULT_RESTORE_TARGET_EXCLUSIVE => false,
OPTION_DEFAULT_RESTORE_TARGET_RESUME => false,
OPTION_DEFAULT_HELP => false,
OPTION_DEFAULT_VERSION => false,
OPTION_DEFAULT_TEST => false,
OPTION_DEFAULT_TEST_DELAY => 5,
OPTION_DEFAULT_TEST_NO_FORK => false
};
####################################################################################################################################
# Option Rules
####################################################################################################################################
use constant
{
OPTION_RULE_ALLOW_LIST => 'allow-list',
OPTION_RULE_DEFAULT => 'default',
OPTION_RULE_DEPEND => 'depend',
OPTION_RULE_DEPEND_OPTION => 'depend-option',
OPTION_RULE_DEPEND_LIST => 'depend-list',
OPTION_RULE_DEPEND_VALUE => 'depend-value',
OPTION_RULE_REQUIRED => 'required',
OPTION_RULE_OPERATION => 'operation',
OPTION_RULE_TYPE => 'type'
};
####################################################################################################################################
# Option Types
####################################################################################################################################
use constant
{
OPTION_TYPE_STRING => 'string',
OPTION_TYPE_BOOLEAN => 'boolean',
OPTION_TYPE_INTEGER => 'integer',
OPTION_TYPE_FLOAT => 'float'
};
####################################################################################################################################
# Option Rule Hash
####################################################################################################################################
my %oOptionRule =
(
&OPTION_CONFIG =>
{
&OPTION_RULE_TYPE => OPTION_TYPE_STRING,
&OPTION_RULE_DEFAULT => OPTION_DEFAULT_CONFIG
},
&OPTION_DELTA =>
{
&OPTION_RULE_TYPE => OPTION_TYPE_BOOLEAN,
&OPTION_RULE_OPERATION =>
{
&OP_RESTORE =>
{
&OPTION_RULE_DEFAULT => OPTION_DEFAULT_RESTORE_DELTA,
}
}
},
&OPTION_FORCE =>
{
&OPTION_RULE_TYPE => OPTION_TYPE_BOOLEAN,
&OPTION_RULE_OPERATION =>
{
&OP_RESTORE =>
{
&OPTION_RULE_DEFAULT => OPTION_DEFAULT_RESTORE_FORCE,
},
&OP_BACKUP =>
{
&OPTION_RULE_DEFAULT => OPTION_DEFAULT_BACKUP_FORCE,
&OPTION_RULE_DEPEND =>
{
&OPTION_RULE_DEPEND_OPTION => OPTION_NO_START_STOP,
&OPTION_RULE_DEPEND_VALUE => true
}
}
}
},
&OPTION_NO_START_STOP =>
{
&OPTION_RULE_TYPE => OPTION_TYPE_BOOLEAN,
&OPTION_RULE_OPERATION =>
{
&OP_BACKUP =>
{
&OPTION_RULE_DEFAULT => OPTION_DEFAULT_BACKUP_NO_START_STOP
}
}
},
&OPTION_SET =>
{
&OPTION_RULE_TYPE => OPTION_TYPE_STRING,
&OPTION_RULE_OPERATION =>
{
&OP_RESTORE =>
{
&OPTION_RULE_DEFAULT => OPTION_DEFAULT_RESTORE_TYPE,
}
}
},
&OPTION_STANZA =>
{
&OPTION_RULE_TYPE => OPTION_TYPE_STRING
},
&OPTION_TARGET =>
{
&OPTION_RULE_TYPE => OPTION_TYPE_STRING,
&OPTION_RULE_OPERATION =>
{
&OP_RESTORE =>
{
&OPTION_RULE_DEPEND =>
{
&OPTION_RULE_DEPEND_OPTION => OPTION_TYPE,
&OPTION_RULE_DEPEND_LIST =>
{
&RECOVERY_TYPE_NAME => true,
&RECOVERY_TYPE_TIME => true,
&RECOVERY_TYPE_XID => true
}
}
}
}
},
&OPTION_TARGET_EXCLUSIVE =>
{
&OPTION_RULE_TYPE => OPTION_TYPE_BOOLEAN,
&OPTION_RULE_OPERATION =>
{
&OP_RESTORE =>
{
&OPTION_RULE_DEFAULT => OPTION_DEFAULT_RESTORE_TARGET_EXCLUSIVE,
&OPTION_RULE_DEPEND =>
{
&OPTION_RULE_DEPEND_OPTION => OPTION_TYPE,
&OPTION_RULE_DEPEND_LIST =>
{
&RECOVERY_TYPE_TIME => true,
&RECOVERY_TYPE_XID => true
}
}
}
}
},
&OPTION_TARGET_RESUME =>
{
&OPTION_RULE_TYPE => OPTION_TYPE_BOOLEAN,
&OPTION_RULE_OPERATION =>
{
&OP_RESTORE =>
{
&OPTION_RULE_DEFAULT => OPTION_DEFAULT_RESTORE_TARGET_RESUME,
&OPTION_RULE_DEPEND =>
{
&OPTION_RULE_DEPEND_OPTION => OPTION_TYPE,
&OPTION_RULE_DEPEND_LIST =>
{
&RECOVERY_TYPE_NAME => true,
&RECOVERY_TYPE_TIME => true,
&RECOVERY_TYPE_XID => true
}
}
}
}
},
&OPTION_TARGET_TIMELINE =>
{
&OPTION_RULE_TYPE => OPTION_TYPE_STRING,
&OPTION_RULE_OPERATION =>
{
&OP_RESTORE =>
{
&OPTION_RULE_REQUIRED => false,
&OPTION_RULE_DEPEND =>
{
&OPTION_RULE_DEPEND_OPTION => OPTION_TYPE,
&OPTION_RULE_DEPEND_LIST =>
{
&RECOVERY_TYPE_DEFAULT => true,
&RECOVERY_TYPE_NAME => true,
&RECOVERY_TYPE_TIME => true,
&RECOVERY_TYPE_XID => true
}
}
}
}
},
&OPTION_TYPE =>
{
&OPTION_RULE_TYPE => OPTION_TYPE_STRING,
&OPTION_RULE_OPERATION =>
{
&OP_BACKUP =>
{
&OPTION_RULE_DEFAULT => OPTION_DEFAULT_BACKUP_TYPE,
&OPTION_RULE_ALLOW_LIST =>
{
&BACKUP_TYPE_FULL => true,
&BACKUP_TYPE_DIFF => true,
&BACKUP_TYPE_INCR => true,
}
},
&OP_RESTORE =>
{
&OPTION_RULE_DEFAULT => OPTION_DEFAULT_RESTORE_TYPE,
&OPTION_RULE_ALLOW_LIST =>
{
&RECOVERY_TYPE_NAME => true,
&RECOVERY_TYPE_TIME => true,
&RECOVERY_TYPE_XID => true,
&RECOVERY_TYPE_PRESERVE => true,
&RECOVERY_TYPE_NONE => true,
&RECOVERY_TYPE_DEFAULT => true
}
}
}
},
&OPTION_THREAD_MAX =>
{
&OPTION_RULE_TYPE => OPTION_TYPE_INTEGER,
&OPTION_RULE_DEFAULT => OPTION_DEFAULT_THREAD_MAX,
&OPTION_RULE_OPERATION =>
{
&OP_BACKUP => true,
&OP_RESTORE => true
}
},
&OPTION_HELP =>
{
&OPTION_RULE_TYPE => OPTION_TYPE_BOOLEAN,
&OPTION_RULE_DEFAULT => OPTION_DEFAULT_HELP
},
&OPTION_VERSION =>
{
&OPTION_RULE_TYPE => OPTION_TYPE_BOOLEAN,
&OPTION_RULE_DEFAULT => OPTION_DEFAULT_VERSION
},
&OPTION_TEST =>
{
&OPTION_RULE_TYPE => OPTION_TYPE_BOOLEAN,
&OPTION_RULE_DEFAULT => OPTION_DEFAULT_TEST
},
&OPTION_TEST_DELAY =>
{
&OPTION_RULE_TYPE => OPTION_TYPE_FLOAT,
&OPTION_RULE_DEFAULT => OPTION_DEFAULT_TEST_DELAY,
&OPTION_RULE_DEPEND =>
{
&OPTION_RULE_DEPEND_OPTION => OPTION_TEST,
&OPTION_RULE_DEPEND_VALUE => true
}
},
&OPTION_TEST_NO_FORK =>
{
&OPTION_RULE_TYPE => OPTION_TYPE_BOOLEAN,
&OPTION_RULE_DEFAULT => OPTION_DEFAULT_TEST_NO_FORK,
&OPTION_RULE_DEPEND =>
{
&OPTION_RULE_DEPEND_OPTION => OPTION_TEST
}
}
);
####################################################################################################################################
# Global variables
####################################################################################################################################
my %oOption; # Option hash
my $strOperation; # Operation (backup, archive-get, ...)
####################################################################################################################################
# configLoad
#
# Load configuration.
####################################################################################################################################
sub configLoad
{
# Clear option in case it was loaded before
%oOption = ();
# Build hash with all valid command-line options
my %oOption;
foreach my $strKey (keys(%oOptionRule))
{
my $strOption = $strKey;
if (!defined($oOptionRule{$strKey}{&OPTION_RULE_TYPE}))
{
confess &log(ASSERT, "Option ${strKey} does not have a defined type", ERROR_ASSERT);
}
elsif ($oOptionRule{$strKey}{&OPTION_RULE_TYPE} ne OPTION_TYPE_BOOLEAN)
{
$strOption .= '=s';
}
$oOption{$strOption} = $strOption;
}
# Get command-line options
my %oOptionTest;
GetOptions(\%oOptionTest, %oOption)
or pod2usage(2);
# Validate and store options
optionValid(\%oOptionTest);
# Display version and exit if requested
if (optionGet(OPTION_VERSION) || optionGet(OPTION_HELP))
{
print 'pg_backrest ' . version_get() . "\n";
if (!OPTION_get(OPTION_HELP))
{
exit 0;
}
}
# Display help and exit if requested
if (optionGet(OPTION_HELP))
{
print "\n";
pod2usage();
}
# Set test options
!optionGet(OPTION_TEST) or test_set(optionGet(OPTION_TEST), optionGet(OPTION_TEST_DELAY));
}
####################################################################################################################################
# optionValid
#
# Make sure the command-line options are valid based on the operation.
####################################################################################################################################
sub optionValid
{
my $oOptionTest = shift;
# Check that the operation is present and valid
$strOperation = $ARGV[0];
if (!defined($strOperation))
{
confess &log(ERROR, "operation must be specified", ERROR_OPERATION_REQUIRED);
}
if ($strOperation ne OP_ARCHIVE_GET &&
$strOperation ne OP_ARCHIVE_PUSH &&
$strOperation ne OP_BACKUP &&
$strOperation ne OP_RESTORE &&
$strOperation ne OP_EXPIRE)
{
confess &log(ERROR, "invalid operation ${strOperation}");
}
# Keep track of unresolved dependencies
my $bDependUnresolved = true;
my %oOptionResolved;
# Loop through all possible options
while ($bDependUnresolved)
{
# Assume that all dependencies will be resolved in this loop
$bDependUnresolved = false;
foreach my $strOption (sort(keys(%oOptionRule)))
{
# Skip the option if it has been resolved in a prior loop
if (defined($oOptionResolved{$strOption}))
{
next;
}
# Check dependency for the operation then for the option
my $oDepend;
my $bDependResolved = true;
if (defined($oOptionRule{$strOption}{&OPTION_RULE_OPERATION}) &&
defined($oOptionRule{$strOption}{&OPTION_RULE_OPERATION}{$strOperation}) &&
ref($oOptionRule{$strOption}{&OPTION_RULE_OPERATION}{$strOperation}) eq 'HASH')
{
$oDepend = $oOptionRule{$strOption}{&OPTION_RULE_OPERATION}{$strOperation}{&OPTION_RULE_DEPEND};
}
if (!defined($oDepend))
{
$oDepend = $oOptionRule{$strOption}{&OPTION_RULE_DEPEND};
}
if (defined($oDepend))
{
# Make sure the depend option has been resolved, otherwise skip this option for now
my $strDependOption = $$oDepend{&OPTION_RULE_DEPEND_OPTION};
if (!defined($oOptionResolved{$strDependOption}))
{
$bDependUnresolved = true;
next;
}
# Check if the depend option has a value
my $strDependValue = $oOption{$strDependOption};
my $strError = "option '${strOption}' not valid without option '${strDependOption}'";
$bDependResolved = defined($strDependValue) ? true : false;
if (!$bDependResolved && defined($$oOptionTest{$strOption}))
{
confess &log(ERROR, $strError, ERROR_OPTION_INVALID);
}
# If a depend value exists, make sure the option value matches
if ($bDependResolved && defined($$oDepend{&OPTION_RULE_DEPEND_VALUE}) &&
$$oDepend{&OPTION_RULE_DEPEND_VALUE} ne $strDependValue)
{
$bDependResolved = false;
if (defined($$oOptionTest{$strOption}))
{
if ($oOptionRule{$strDependOption}{&OPTION_RULE_TYPE} eq OPTION_TYPE_BOOLEAN)
{
if (!$$oDepend{&OPTION_RULE_DEPEND_VALUE})
{
confess &log(ASSERT, "no error has been created for unused case where depend value = false");
}
}
else
{
$strError .= " = '$$oDepend{&OPTION_RULE_DEPEND_VALUE}'";
}
confess &log(ERROR, $strError, ERROR_OPTION_INVALID);
}
}
# If a depend list exists, make sure the value is in the list
if ($bDependResolved && defined($$oDepend{&OPTION_RULE_DEPEND_LIST}) &&
!defined($$oDepend{&OPTION_RULE_DEPEND_LIST}{$strDependValue}))
{
$bDependResolved = false;
if (defined($$oOptionTest{$strOption}))
{
my @oyValue;
foreach my $strValue (sort(keys($$oDepend{&OPTION_RULE_DEPEND_LIST})))
{
push(@oyValue, "'${strValue}'");
}
$strError .= @oyValue == 1 ? " = $oyValue[0]" : " in (" . join(", ", @oyValue) . ")";
confess &log(ERROR, $strError, ERROR_OPTION_INVALID);
}
}
}
# Is the option defined?
if (defined($$oOptionTest{$strOption}))
{
my $strValue = $$oOptionTest{$strOption};
# Test option type
if ($oOptionRule{$strOption}{&OPTION_RULE_TYPE} eq OPTION_TYPE_INTEGER ||
$oOptionRule{$strOption}{&OPTION_RULE_TYPE} eq OPTION_TYPE_FLOAT)
{
# Test that the string is a valid float or integer by adding 1 to it. It's pretty hokey but it works and it
# beats requiring Scalar::Util::Numeric to do it properly.
eval
{
my $strTest = $strValue + 1;
};
my $bError = $@ ? true : false;
# Check that integers are really integers
if (!$bError && $oOptionRule{$strOption}{&OPTION_RULE_TYPE} eq OPTION_TYPE_INTEGER &&
(int($strValue) . 'S') ne ($strValue . 'S'))
{
$bError = true;
}
# Error if the value did not pass tests
!$bError
or confess &log(ERROR, "'${strValue}' is not valid for '${strOption}' option", ERROR_OPTION_INVALID_VALUE);
}
# Process an allow list for the operation then for the option
my $oAllow;
if (defined($oOptionRule{$strOption}{&OPTION_RULE_OPERATION}) &&
defined($oOptionRule{$strOption}{&OPTION_RULE_OPERATION}{$strOperation}) &&
ref($oOptionRule{$strOption}{&OPTION_RULE_OPERATION}{$strOperation}) eq 'HASH')
{
$oAllow = $oOptionRule{$strOption}{&OPTION_RULE_OPERATION}{$strOperation}{&OPTION_RULE_ALLOW_LIST};
}
if (!defined($oAllow))
{
$oAllow = $oOptionRule{$strOption}{&OPTION_RULE_ALLOW_LIST};
}
if (defined($oAllow) && !defined($$oAllow{$$oOptionTest{$strOption}}))
{
confess &log(ERROR, "'${strValue}' is not valid for '${strOption}' option", ERROR_OPTION_INVALID_VALUE);
}
# Set option value
$oOption{$strOption} = $strValue;
}
# Else set the default if required
elsif (!defined($oOptionRule{$strOption}{&OPTION_RULE_OPERATION}) ||
defined($oOptionRule{$strOption}{&OPTION_RULE_OPERATION}{$strOperation}))
{
# Check for default in operation then option
my $strDefault;
if (defined($oOptionRule{$strOption}{&OPTION_RULE_OPERATION}) &&
defined($oOptionRule{$strOption}{&OPTION_RULE_OPERATION}{$strOperation}) &&
ref($oOptionRule{$strOption}{&OPTION_RULE_OPERATION}{$strOperation}) eq 'HASH')
{
$strDefault = $oOptionRule{$strOption}{&OPTION_RULE_OPERATION}{$strOperation}{&OPTION_RULE_DEFAULT}
}
if (!defined($strDefault))
{
$strDefault = $oOptionRule{$strOption}{&OPTION_RULE_DEFAULT};
}
# If default is defined
if (defined($strDefault))
{
# Only set default if dependency is resolved
$oOption{$strOption} = $strDefault if $bDependResolved;
}
# Else error
else
{
# Check for required in operation then option
my $bRequired;
if (defined($oOptionRule{$strOption}{&OPTION_RULE_OPERATION}) &&
defined($oOptionRule{$strOption}{&OPTION_RULE_OPERATION}{$strOperation}) &&
ref($oOptionRule{$strOption}{&OPTION_RULE_OPERATION}{$strOperation}) eq 'HASH')
{
$bRequired = $oOptionRule{$strOption}{&OPTION_RULE_OPERATION}{$strOperation}{&OPTION_RULE_REQUIRED};
}
if (!defined($bRequired))
{
$bRequired = $oOptionRule{$strOption}{&OPTION_RULE_REQUIRED};
}
if (!defined($bRequired) || $bRequired)
{
if ($bDependResolved)
{
confess &log(ERROR, "${strOperation} operation requires option: ${strOption}", ERROR_OPTION_REQUIRED);
}
}
}
}
$oOptionResolved{$strOption} = true;
}
}
}
####################################################################################################################################
# operationGet
#
# Get the current operation.
####################################################################################################################################
sub operationGet
{
return $strOperation;
}
####################################################################################################################################
# operationTest
#
# Test the current operation.
####################################################################################################################################
sub operationTest
{
my $strOperationTest = shift;
return $strOperationTest eq $strOperation;
}
####################################################################################################################################
# operationSet
#
# Set current operation (usually for triggering follow-on operations).
####################################################################################################################################
sub operationSet
{
my $strValue = shift;
$strOperation = $strValue;
}
####################################################################################################################################
# optionGet
#
# Get option value.
####################################################################################################################################
sub optionGet
{
my $strOption = shift;
my $bRequired = shift;
if (!defined($oOption{$strOption}) && (!defined($bRequired) || $bRequired))
{
confess &log(ASSERT, "option ${strOption} is required");
}
return $oOption{$strOption};
}
####################################################################################################################################
# optionTest
#
# Test a option value.
####################################################################################################################################
sub optionTest
{
my $strOption = shift;
my $strValue = shift;
if (defined($strValue))
{
return optionGet($strOption) eq $strValue;
}
return defined($oOption{$strOption});
}
####################################################################################################################################
# optionRuleGet
#
# Get the option rules.
####################################################################################################################################
sub optionRuleGet
{
return dclone(\%oOptionRule);
}
1;

View File

@ -17,6 +17,7 @@ use lib dirname($0);
use BackRest::Exception;
use BackRest::Utility;
use BackRest::ThreadGroup;
use BackRest::Param;
use BackRest::Config;
use BackRest::Manifest;
use BackRest::File;
@ -50,6 +51,7 @@ sub new
# Initialize variables
$self->{strDbClusterPath} = $strDbClusterPath;
$self->{strBackupPath} = $strBackupPath;
$self->{oRemapRef} = $oRemapRef;
$self->{oFile} = $oFile;
$self->{iThreadTotal} = defined($iThreadTotal) ? $iThreadTotal : 1;
@ -65,16 +67,6 @@ sub new
$self->{strBackRestBin} = $strBackRestBin;
$self->{strConfigFile} = $strConfigFile;
# If backup path is not specified then default to latest
if (defined($strBackupPath))
{
$self->{strBackupPath} = $strBackupPath;
}
else
{
$self->{strBackupPath} = PATH_LATEST;
}
return $self;
}
@ -196,7 +188,7 @@ sub manifest_load
# If backup is latest then set it equal to backup label, else verify that requested backup and label match
my $strBackupLabel = $oManifest->get(MANIFEST_SECTION_BACKUP, MANIFEST_KEY_LABEL);
if ($self->{strBackupPath} eq PATH_LATEST)
if ($self->{strBackupPath} eq OPTION_DEFAULT_RESTORE_SET)
{
$self->{strBackupPath} = $strBackupLabel;
}

View File

@ -1,6 +1,6 @@
#!/usr/bin/perl
####################################################################################################################################
# BackupTest.pl - Unit Tests for BackRest::File
# BackupTest.pl - Unit Tests for BackRest::Backup and BackRest::Restore
####################################################################################################################################
package BackRestTest::BackupTest;
@ -21,6 +21,7 @@ use DBI;
use lib dirname($0) . '/../lib';
use BackRest::Exception;
use BackRest::Utility;
use BackRest::Param;
use BackRest::Config;
use BackRest::Manifest;
use BackRest::File;
@ -1963,7 +1964,7 @@ sub BackRestTestBackup_Test
# Static backup parameters
my $bSynthetic = false;
my $fTestDelay = .25;
my $fTestDelay = 1;
# Variable backup parameters
my $bDelta = true;

View File

@ -0,0 +1,392 @@
#!/usr/bin/perl
####################################################################################################################################
# ConfigTest.pl - Unit Tests for BackRest::Param and BackRest::Config
####################################################################################################################################
package BackRestTest::ConfigTest;
####################################################################################################################################
# Perl includes
####################################################################################################################################
use strict;
use warnings FATAL => qw(all);
use Carp qw(confess);
use File::Basename qw(dirname);
use Scalar::Util 'blessed';
#use Data::Dumper qw(Dumper);
#use Scalar::Util qw(blessed);
# use Test::More qw(no_plan);
# use Test::Deep;
use lib dirname($0) . '/../lib';
use BackRest::Exception;
use BackRest::Utility;
use BackRest::Param;
use BackRestTest::CommonTest;
use Exporter qw(import);
our @EXPORT = qw(BackRestTestConfig_Test);
sub optionSetTest
{
my $oOption = shift;
my $strKey = shift;
my $strValue = shift;
$$oOption{option}{$strKey} = $strValue;
}
sub optionSetBoolTest
{
my $oOption = shift;
my $strKey = shift;
$$oOption{boolean}{$strKey} = true;
}
sub operationSetTest
{
my $oOption = shift;
my $strOperation = shift;
$$oOption{operation} = $strOperation;
}
sub optionRemoveTest
{
my $oOption = shift;
my $strKey = shift;
delete($$oOption{option}{$strKey});
delete($$oOption{boolean}{$strKey});
}
sub argvWriteTest
{
my $oOption = shift;
@ARGV = ();
if (defined($$oOption{boolean}))
{
foreach my $strKey (keys $$oOption{boolean})
{
if ($$oOption{boolean}{$strKey})
{
$ARGV[@ARGV] = "--${strKey}";
}
else
{
$ARGV[@ARGV] = "--no-${strKey}";
}
}
}
if (defined($$oOption{option}))
{
foreach my $strKey (keys $$oOption{option})
{
$ARGV[@ARGV] = "--${strKey}=";
if (defined($$oOption{option}{$strKey}))
{
$ARGV[@ARGV - 1] .= $$oOption{option}{$strKey};
}
}
}
$ARGV[@ARGV] = $$oOption{operation};
&log(INFO, " command line: " . join(" ", @ARGV));
%$oOption = ();
}
sub configLoadExpectError
{
my $oOption = shift;
my $strOperation = shift;
my $iExpectedError = shift;
my $strErrorParam1 = shift;
my $strErrorParam2 = shift;
my $strErrorParam3 = shift;
my $oOptionRuleExpected = optionRuleGet();
operationSetTest($oOption, $strOperation);
argvWriteTest($oOption);
eval
{
configLoad();
};
if ($@)
{
if (!defined($iExpectedError))
{
confess $@;
}
my $oMessage = $@;
if (blessed($oMessage) && $oMessage->isa('BackRest::Exception'))
{
if ($oMessage->code() != $iExpectedError)
{
confess "expected error ${iExpectedError} from configLoad but got " . $oMessage->code();
}
my $strError;
if ($iExpectedError == ERROR_OPTION_REQUIRED)
{
$strError = "backup operation requires option: ${strErrorParam1}";
}
elsif ($iExpectedError == ERROR_OPERATION_REQUIRED)
{
$strError = "operation must be specified";
}
elsif ($iExpectedError == ERROR_OPTION_INVALID)
{
$strError = "option '${strErrorParam1}' not valid without option '${strErrorParam2}'";
if (defined($strErrorParam3))
{
$strError .= @{$strErrorParam3} == 1 ? " = '$$strErrorParam3[0]'" :
" in ('" . join("', '",@{ $strErrorParam3}) . "')";
}
}
elsif ($iExpectedError == ERROR_OPTION_INVALID_VALUE)
{
$strError = "'${strErrorParam1}' is not valid for '${strErrorParam2}' option";
}
else
{
confess "must construct message for error ${iExpectedError}, use this as an example: '" . $oMessage->message() . "'";
}
if ($oMessage->message() ne $strError)
{
confess "expected error message \"${strError}\" from configLoad but got \"" . $oMessage->message() . "\"";
}
}
else
{
confess "configLoad should throw BackRest::Exception:\n$oMessage";
}
}
else
{
if (defined($iExpectedError))
{
confess "expected error ${iExpectedError} from configLoad but got success";
}
}
# cmp_deeply(OPTION_rule_get(), $oOptionRuleExpected, 'compare original and new rule hashes')
# or die 'comparison failed';
}
sub optionTestExpect
{
my $strOption = shift;
my $strExpectedValue = shift;
if (defined($strExpectedValue))
{
my $strActualValue = optionGet($strOption);
$strActualValue eq $strExpectedValue
or confess "expected option ${strOption} to have value ${strExpectedValue}, but ${strActualValue} found instead";
}
elsif (optionTest($strOption))
{
confess "expected option ${strOption} to be [undef], but " . optionGet($strOption) . ' found instead';
}
}
####################################################################################################################################
# BackRestTestConfig_Test
####################################################################################################################################
sub BackRestTestConfig_Test
{
my $strTest = shift;
# Setup test variables
my $iRun;
my $bCreate;
my $strStanza = 'main';
my $oOption = {};
my @oyArray;
use constant BOGUS => 'bogus';
# Print test banner
&log(INFO, 'CONFIG MODULE ******************************************************************');
#-------------------------------------------------------------------------------------------------------------------------------
# Test config
#-------------------------------------------------------------------------------------------------------------------------------
if ($strTest eq 'all' || $strTest eq 'option')
{
&log(INFO, "Option module\n");
if (BackRestTestCommon_Run(++$iRun, 'backup with no stanza'))
{
configLoadExpectError($oOption, OP_BACKUP , ERROR_OPTION_REQUIRED, OPTION_STANZA);
}
if (BackRestTestCommon_Run(++$iRun, 'backup with boolean stanza'))
{
optionSetBoolTest($oOption, OPTION_STANZA);
configLoadExpectError($oOption, OP_BACKUP, , ERROR_OPERATION_REQUIRED);
}
if (BackRestTestCommon_Run(++$iRun, 'backup type defaults to ' . BACKUP_TYPE_INCR))
{
optionSetTest($oOption, OPTION_STANZA, $strStanza);
configLoadExpectError($oOption, OP_BACKUP);
optionTestExpect(OPTION_TYPE, BACKUP_TYPE_INCR);
}
if (BackRestTestCommon_Run(++$iRun, 'backup type set to ' . BACKUP_TYPE_FULL))
{
optionSetTest($oOption, OPTION_STANZA, $strStanza);
optionSetTest($oOption, OPTION_TYPE, BACKUP_TYPE_FULL);
configLoadExpectError($oOption, OP_BACKUP);
optionTestExpect(OPTION_TYPE, BACKUP_TYPE_FULL);
}
if (BackRestTestCommon_Run(++$iRun, 'backup type invalid'))
{
optionSetTest($oOption, OPTION_STANZA, $strStanza);
optionSetTest($oOption, OPTION_TYPE, BOGUS);
configLoadExpectError($oOption, OP_BACKUP , ERROR_OPTION_INVALID_VALUE, BOGUS, OPTION_TYPE);
}
if (BackRestTestCommon_Run(++$iRun, 'backup invalid force'))
{
# $oOption = {};
optionSetTest($oOption, OPTION_STANZA, $strStanza);
optionSetBoolTest($oOption, OPTION_FORCE);
configLoadExpectError($oOption, OP_BACKUP, ERROR_OPTION_INVALID, OPTION_FORCE, OPTION_NO_START_STOP);
}
if (BackRestTestCommon_Run(++$iRun, 'backup valid force'))
{
# $oOption = {};
optionSetTest($oOption, OPTION_STANZA, $strStanza);
optionSetBoolTest($oOption, OPTION_NO_START_STOP);
optionSetBoolTest($oOption, OPTION_FORCE);
configLoadExpectError($oOption, OP_BACKUP);
optionTestExpect(OPTION_NO_START_STOP, true);
optionTestExpect(OPTION_FORCE, true);
}
if (BackRestTestCommon_Run(++$iRun, 'backup invalid value for ' . OPTION_TEST_DELAY))
{
optionSetTest($oOption, OPTION_STANZA, $strStanza);
optionSetBoolTest($oOption, OPTION_TEST);
optionSetTest($oOption, OPTION_TEST_DELAY, BOGUS);
configLoadExpectError($oOption, OP_BACKUP , ERROR_OPTION_INVALID_VALUE, BOGUS, OPTION_TEST_DELAY);
}
if (BackRestTestCommon_Run(++$iRun, 'backup invalid ' . OPTION_TEST_DELAY))
{
optionSetTest($oOption, OPTION_STANZA, $strStanza);
optionSetTest($oOption, OPTION_TEST_DELAY, 5);
configLoadExpectError($oOption, OP_BACKUP , ERROR_OPTION_INVALID, OPTION_TEST_DELAY, OPTION_TEST);
}
if (BackRestTestCommon_Run(++$iRun, 'backup check ' . OPTION_TEST_DELAY . ' undef'))
{
optionSetTest($oOption, OPTION_STANZA, $strStanza);
configLoadExpectError($oOption, OP_BACKUP);
optionTestExpect(OPTION_TEST_DELAY);
}
if (BackRestTestCommon_Run(++$iRun, 'restore invalid ' . OPTION_TARGET))
{
optionSetTest($oOption, OPTION_STANZA, $strStanza);
optionSetTest($oOption, OPTION_TYPE, RECOVERY_TYPE_DEFAULT);
optionSetTest($oOption, OPTION_TARGET, BOGUS);
@oyArray = (RECOVERY_TYPE_NAME, RECOVERY_TYPE_TIME, RECOVERY_TYPE_XID);
configLoadExpectError($oOption, OP_RESTORE , ERROR_OPTION_INVALID, OPTION_TARGET, OPTION_TYPE, \@oyArray);
}
if (BackRestTestCommon_Run(++$iRun, 'restore ' . OPTION_TARGET))
{
optionSetTest($oOption, OPTION_STANZA, $strStanza);
optionSetTest($oOption, OPTION_TYPE, RECOVERY_TYPE_NAME);
optionSetTest($oOption, OPTION_TARGET, BOGUS);
configLoadExpectError($oOption, OP_RESTORE);
optionTestExpect(OPTION_TYPE, RECOVERY_TYPE_NAME);
optionTestExpect(OPTION_TARGET, BOGUS);
optionTestExpect(OPTION_TARGET_TIMELINE);
}
if (BackRestTestCommon_Run(++$iRun, 'invalid string ' . OPTION_THREAD_MAX))
{
optionSetTest($oOption, OPTION_STANZA, $strStanza);
optionSetTest($oOption, OPTION_THREAD_MAX, BOGUS);
configLoadExpectError($oOption, OP_BACKUP , ERROR_OPTION_INVALID_VALUE, BOGUS, OPTION_THREAD_MAX);
}
if (BackRestTestCommon_Run(++$iRun, 'invalid float ' . OPTION_THREAD_MAX))
{
optionSetTest($oOption, OPTION_STANZA, $strStanza);
optionSetTest($oOption, OPTION_THREAD_MAX, '0.0');
configLoadExpectError($oOption, OP_BACKUP , ERROR_OPTION_INVALID_VALUE, '0.0', OPTION_THREAD_MAX);
}
if (BackRestTestCommon_Run(++$iRun, 'valid ' . OPTION_THREAD_MAX))
{
optionSetTest($oOption, OPTION_STANZA, $strStanza);
optionSetTest($oOption, OPTION_THREAD_MAX, '2');
configLoadExpectError($oOption, OP_BACKUP);
}
if (BackRestTestCommon_Run(++$iRun, 'valid float ' . OPTION_TEST_DELAY))
{
optionSetTest($oOption, OPTION_STANZA, $strStanza);
optionSetBoolTest($oOption, OPTION_TEST);
optionSetTest($oOption, OPTION_TEST_DELAY, '0.25');
configLoadExpectError($oOption, OP_BACKUP);
}
if (BackRestTestCommon_Run(++$iRun, 'valid int ' . OPTION_TEST_DELAY))
{
optionSetTest($oOption, OPTION_STANZA, $strStanza);
optionSetBoolTest($oOption, OPTION_TEST);
optionSetTest($oOption, OPTION_TEST_DELAY, 3);
configLoadExpectError($oOption, OP_BACKUP);
}
if (BackRestTestCommon_Run(++$iRun, 'restore valid ' . OPTION_TARGET_TIMELINE))
{
optionSetTest($oOption, OPTION_STANZA, $strStanza);
optionSetTest($oOption, OPTION_TARGET_TIMELINE, 2);
configLoadExpectError($oOption, OP_RESTORE);
}
}
}
1;

View File

@ -1,6 +1,6 @@
#!/usr/bin/perl
####################################################################################################################################
# BackupTest.pl - Unit Tests for BackRest::File
# UtilityTest.pl - Unit Tests for BackRest::Utility
####################################################################################################################################
package BackRestTest::UtilityTest;

View File

@ -14,6 +14,7 @@ use File::Basename;
use Getopt::Long;
use Cwd 'abs_path';
use Pod::Usage;
#use Test::More;
use lib dirname($0) . '/../lib';
use BackRest::Utility;
@ -21,6 +22,7 @@ use BackRest::Utility;
use lib dirname($0) . '/lib';
use BackRestTest::CommonTest;
use BackRestTest::UtilityTest;
use BackRestTest::ConfigTest;
use BackRestTest::FileTest;
use BackRestTest::BackupTest;
@ -102,6 +104,8 @@ if ($bVersion || $bHelp)
exit 0;
}
# Test::More->builder->output('/dev/null');
####################################################################################################################################
# Setup
####################################################################################################################################
@ -229,6 +233,11 @@ do
BackRestTestUtility_Test($strModuleTest);
}
if ($strModule eq 'all' || $strModule eq 'config')
{
BackRestTestConfig_Test($strModuleTest);
}
if ($strModule eq 'all' || $strModule eq 'file')
{
BackRestTestFile_Test($strModuleTest);