2013-12-11 20:39:07 -05:00
#!/usr/bin/perl
2013-11-17 13:58:21 -05:00
2013-12-02 15:10:18 -05:00
use strict ;
2013-12-11 20:39:07 -05:00
use warnings ;
2013-12-02 21:23:10 -05:00
use File::Basename ;
use Getopt::Long ;
2013-12-04 21:37:45 -05:00
use Config::IniFiles ;
2013-12-15 20:04:07 -05:00
use IPC::System::Simple qw( capture ) ;
2013-12-14 13:14:59 -05:00
use JSON ;
2013-11-17 13:58:21 -05:00
2013-12-02 21:23:10 -05:00
# Process flags
my $ bNoCompression ;
my $ bNoChecksum ;
2013-12-04 21:37:45 -05:00
my $ strConfigFile ;
2013-12-11 19:22:41 -05:00
my $ strCluster ;
2013-12-02 21:23:10 -05:00
GetOptions ( "no-compression" = > \ $ bNoCompression ,
2013-12-04 21:37:45 -05:00
"no-checksum" = > \ $ bNoChecksum ,
2013-12-11 19:22:41 -05:00
"config=s" = > \ $ strConfigFile ,
"cluster=s" = > \ $ strCluster )
2013-12-02 21:23:10 -05:00
or die ( "Error in command line arguments\n" ) ;
####################################################################################################################################
# TRIM - trim whitespace off strings
####################################################################################################################################
sub trim
{
my $ strBuffer = shift ;
2013-12-09 17:26:47 -05:00
$ strBuffer =~ s/^\s+|\s+$//g ;
2013-12-02 21:23:10 -05:00
2013-12-09 17:26:47 -05:00
return $ strBuffer ;
2013-12-02 21:23:10 -05:00
}
2013-12-11 19:22:41 -05:00
####################################################################################################################################
# LOG - log messages
####################################################################################################################################
use constant
{
DEBUG = > 'DEBUG' ,
INFO = > 'INFO' ,
WARNING = > 'WARNING' ,
ERROR = > 'ERROR'
} ;
sub log
{
my $ strLevel = shift ;
my $ strMessage = shift ;
print "${strLevel}: ${strMessage}\n" ;
return $ strMessage ;
}
2013-12-02 21:23:10 -05:00
####################################################################################################################################
# EXECUTE - execute a command
####################################################################################################################################
2013-11-17 13:58:21 -05:00
sub execute
{
2013-12-02 15:10:18 -05:00
my $ strCommand = shift ;
2013-12-15 20:04:07 -05:00
my $ strOutput ;
# print("$strCommand");
$ strOutput = capture ( $ strCommand ) ;
# if ($strOutput eq "")
# {
# print(" ... complete\n\n");
# }
# else
# {
# print(" ... complete\n$strOutput\n\n");
# }
return $ strOutput ;
2013-11-17 21:48:53 -05:00
}
2013-12-11 20:39:07 -05:00
####################################################################################################################################
# CONFIG_LOAD - Get a value from the config and be sure that it is defined (unless bRequired is false)
####################################################################################################################################
sub config_load
{
my $ oConfigRef = shift ;
my $ strSection = shift ;
my $ strKey = shift ;
my $ bRequired = shift ;
if ( ! defined ( $ bRequired ) )
{
$ bRequired = 1 ;
}
my $ strValue = $ { $ oConfigRef } { "${strSection}" } { "${strKey}" } ;
if ( $ bRequired && ! defined ( $ strValue ) )
{
die & log ( ERROR , 'config value ${strSection}->${strKey} is undefined' ) ;
}
return $ strValue ;
}
2013-12-02 21:23:10 -05:00
####################################################################################################################################
2013-11-23 19:16:09 -05:00
# FILE_HASH_GET - get the sha1 hash for a file
2013-12-02 21:23:10 -05:00
####################################################################################################################################
2013-11-17 21:48:53 -05:00
sub file_hash_get
{
2013-12-04 21:37:45 -05:00
my $ strCommand = shift ;
2013-12-02 15:10:18 -05:00
my $ strFile = shift ;
2013-11-17 21:48:53 -05:00
$ strCommand =~ s/\%file\%/$strFile/g ;
2013-12-02 21:23:10 -05:00
my $ strHash = trim ( execute ( $ strCommand ) ) ;
2013-11-17 21:48:53 -05:00
return ( $ strHash ) ;
}
2013-12-14 13:14:59 -05:00
####################################################################################################################################
# DATA_HASH_BUILD - Hash a delimited file with header
####################################################################################################################################
sub data_hash_build
{
my $ strData = shift ;
my $ strDelimiter = shift ;
my $ strUndefinedKey = shift ;
my @ stryFile = split ( "\n" , $ strData ) ;
my @ stryHeader = split ( $ strDelimiter , $ stryFile [ 0 ] ) ;
# print "data: $strData\n";
my % oHash ;
for ( my $ iLineIdx = 1 ; $ iLineIdx < scalar @ stryFile ; $ iLineIdx + + )
{
my @ stryLine = split ( $ strDelimiter , $ stryFile [ $ iLineIdx ] ) ;
if ( ! defined ( $ stryLine [ 0 ] ) || $ stryLine [ 0 ] eq "" )
{
$ stryLine [ 0 ] = $ strUndefinedKey ;
}
# print "line: @stryLine\n";
for ( my $ iColumnIdx = 1 ; $ iColumnIdx < scalar @ stryHeader ; $ iColumnIdx + + )
{
if ( defined ( $ oHash { "$stryHeader[0]" } { "$stryLine[0]" } { "$stryHeader[$iColumnIdx]" } ) )
{
die "the first column must be unique to build the hash" ;
}
$ oHash { "$stryHeader[0]" } { "$stryLine[0]" } { "$stryHeader[$iColumnIdx]" } = $ stryLine [ $ iColumnIdx ] ;
2013-12-15 20:04:07 -05:00
# print "Hash {$stryHeader[0]}{$stryLine[0]}{$stryHeader[$iColumnIdx]} = " . (defined $oHash{"$stryHeader[0]"}{"$stryLine[0]"}{"$stryHeader[$iColumnIdx]"} ? $oHash{"$stryHeader[0]"}{"$stryLine[0]"}{"$stryHeader[$iColumnIdx]"} : "null") . "\n";
2013-12-14 13:14:59 -05:00
#$oHash{$stryHeader[$iColumnIdx]}{$stryLine[$iColumnIdx]}{$stryHeader[0]} = $stryLine[0];
}
}
return % oHash ;
}
####################################################################################################################################
# TABLESPACE_MAP_GET - Get the mapping between oid and tablespace name
####################################################################################################################################
sub tablespace_map_get
{
2013-12-15 20:04:07 -05:00
my $ strCommandPsql = shift ;
2013-12-14 13:14:59 -05:00
2013-12-15 20:04:07 -05:00
my % oTablespaceMap = data_hash_build ( "oid\tname\n" . execute ( $ strCommandPsql .
" -c 'copy (select oid, spcname from pg_tablespace) to stdout' postgres" ) , "\t" ) ;
2013-12-14 13:14:59 -05:00
return % oTablespaceMap ;
}
####################################################################################################################################
# MANIFEST_GET - Get a directory manifest
####################################################################################################################################
sub manifest_get
{
my $ strCommandManifest = shift ;
my $ strPath = shift ;
my $ strCommand = $ strCommandManifest ;
$ strCommand =~ s/\%path\%/$strPath/g ;
my % oManifest = data_hash_build ( "name\ttype\tuser\tgroup\tpermission\tmodification_time\tinode\tsize\tlink_destination\n" .
execute ( $ strCommand ) , "\t" , "." ) ;
return % oManifest ;
}
2013-12-14 15:02:47 -05:00
####################################################################################################################################
# BACKUP_MANIFEST_LOAD - Load the backup manifest
####################################################################################################################################
sub backup_manifest_load
{
my $ strBackupManifestFile = shift ;
my % oBackupManifestFile ;
2013-12-15 20:04:07 -05:00
tie % oBackupManifestFile , 'Config::IniFiles' , ( - file = > $ strBackupManifestFile ) or die & log ( ERROR , "backup manifest '${strBackupManifestFile}' could not be loaded" ) ;
2013-12-14 15:02:47 -05:00
my % oBackupManifest ;
my $ strSection ;
foreach $ strSection ( sort ( keys % oBackupManifestFile ) )
{
my $ strKey ;
#&log(DEBUG, "section: ${strSection}");
foreach $ strKey ( sort ( keys $ { oBackupManifestFile } { "${strSection}" } ) )
{
my $ strValue = $ { oBackupManifestFile } { "${strSection}" } { "$strKey" } ;
#&log(DEBUG, " key: ${strKey}=${strValue}");
$ oBackupManifest { "${strSection}" } { "$strKey" } = decode_json ( $ strValue ) ;
}
}
return % oBackupManifest ;
}
####################################################################################################################################
# BACKUP_MANIFEST_SAVE - Save the backup manifest
####################################################################################################################################
sub backup_manifest_save
{
my $ strBackupManifestFile = shift ;
my $ oBackupManifestRef = shift ;
my % oBackupManifest ;
tie % oBackupManifest , 'Config::IniFiles' or die & log ( ERROR , "Unable to create backup config" ) ;
my $ strSection ;
foreach $ strSection ( sort ( keys $ oBackupManifestRef ) )
{
my $ strKey ;
#&log(DEBUG, "section: ${strSection}");
foreach $ strKey ( sort ( keys $ { $ oBackupManifestRef } { "${strSection}" } ) )
{
my $ strValue = encode_json ( $ { $ oBackupManifestRef } { "${strSection}" } { "$strKey" } ) ;
#&log(DEBUG, " key: ${strKey}=${strValue}");
$ oBackupManifest { "${strSection}" } { "$strKey" } = $ strValue ;
}
}
tied ( % oBackupManifest ) - > WriteConfig ( $ strBackupManifestFile ) ;
}
2013-12-06 20:24:14 -05:00
####################################################################################################################################
2013-12-14 14:03:08 -05:00
# BACKUP_MANIFEST_BUILD - Create the backup manifest
2013-12-06 20:24:14 -05:00
####################################################################################################################################
2013-12-14 14:03:08 -05:00
sub backup_manifest_build
2013-12-06 20:24:14 -05:00
{
my $ strCommandManifest = shift ;
2013-12-11 19:22:41 -05:00
my $ strClusterDataPath = shift ;
2013-12-14 13:14:59 -05:00
my $ oBackupManifestRef = shift ;
my $ oTablespaceMapRef = shift ;
2013-12-06 20:24:14 -05:00
my $ strLevel = shift ;
2013-12-11 19:22:41 -05:00
if ( ! defined ( $ strLevel ) )
{
$ strLevel = "base" ;
}
2013-12-06 20:24:14 -05:00
2013-12-14 13:14:59 -05:00
my % oManifestHash = manifest_get ( $ strCommandManifest , $ strClusterDataPath ) ;
my $ strName ;
foreach $ strName ( sort ( keys $ oManifestHash { name } ) )
2013-12-06 20:24:14 -05:00
{
# Don't process anything in pg_xlog
if ( index ( $ strName , 'pg_xlog/' ) != 0 )
{
2013-12-14 14:03:08 -05:00
my $ cType = $ oManifestHash { name } { "${strName}" } { type } ;
my $ strLinkDestination = $ oManifestHash { name } { "${strName}" } { link_destination } ;
my $ strSection = "${strLevel}:path" ;
2013-12-11 19:22:41 -05:00
2013-12-14 14:03:08 -05:00
#&log(DEBUG, "$strClusterDataPath ${cType}: $strName");
2013-12-06 20:24:14 -05:00
2013-12-14 14:03:08 -05:00
if ( $ cType eq "f" )
{
$ strSection = "${strLevel}:file" ;
}
2013-12-06 20:24:14 -05:00
elsif ( $ cType eq "l" )
{
2013-12-14 14:03:08 -05:00
$ strSection = "${strLevel}:link" ;
}
elsif ( $ cType ne "d" )
{
die & log ( ERROR , "Unrecognized file type $cType for file $strName" ) ;
}
2013-12-09 17:26:47 -05:00
2013-12-14 14:03:08 -05:00
$ { $ oBackupManifestRef } { "${strSection}" } { "$strName" } { user } = $ oManifestHash { name } { "${strName}" } { user } ;
$ { $ oBackupManifestRef } { "${strSection}" } { "$strName" } { group } = $ oManifestHash { name } { "${strName}" } { group } ;
2013-12-15 17:18:50 -05:00
$ { $ oBackupManifestRef } { "${strSection}" } { "$strName" } { permission } = $ oManifestHash { name } { "${strName}" } { permission } ;
2013-12-17 07:47:24 -05:00
$ { $ oBackupManifestRef } { "${strSection}" } { "$strName" } { modification_time } =
( split ( "\\." , $ oManifestHash { name } { "${strName}" } { modification_time } ) ) [ 0 ] ;
# print("modification time:" . ${$oBackupManifestRef}{"${strSection}"}{"$strName"}{modification_time});
2013-12-09 17:26:47 -05:00
2013-12-14 14:03:08 -05:00
if ( $ cType eq "f" || $ cType eq "l" )
{
$ { $ oBackupManifestRef } { "${strSection}" } { "$strName" } { inode } = $ oManifestHash { name } { "${strName}" } { inode } ;
2013-12-06 20:24:14 -05:00
}
2013-12-14 14:03:08 -05:00
if ( $ cType eq "f" )
2013-12-06 20:24:14 -05:00
{
2013-12-14 14:03:08 -05:00
$ { $ oBackupManifestRef } { "${strSection}" } { "$strName" } { size } = $ oManifestHash { name } { "${strName}" } { size } ;
2013-12-06 20:24:14 -05:00
}
2013-12-14 14:03:08 -05:00
if ( $ cType eq "l" )
2013-12-06 20:24:14 -05:00
{
2013-12-14 14:03:08 -05:00
$ { $ oBackupManifestRef } { "${strSection}" } { "$strName" } { link_destination } = $ oManifestHash { name } { "${strName}" } { link_destination } ;
if ( index ( $ strName , 'pg_tblspc/' ) == 0 && $ strLevel eq "base" )
{
my $ strTablespaceOid = basename ( $ strName ) ;
my $ strTablespaceName = $ { $ oTablespaceMapRef } { oid } { "$strTablespaceOid" } { name } ;
#&log(DEBUG, "tablespace: ${strTablespace}");
2013-12-14 15:02:47 -05:00
$ { $ oBackupManifestRef } { "${strLevel}:tablespace" } { "${strTablespaceName}" } { oid } = $ strTablespaceOid ;
2013-12-15 17:18:50 -05:00
$ { $ oBackupManifestRef } { "${strLevel}:tablespace" } { "${strTablespaceName}" } { path } = $ strLinkDestination ;
2013-12-14 14:03:08 -05:00
backup_manifest_build ( $ strCommandManifest , $ strLinkDestination , $ oBackupManifestRef , $ oTablespaceMapRef , "tablespace:${strTablespaceName}" ) ;
}
2013-12-06 20:24:14 -05:00
}
}
}
}
2013-12-15 17:18:50 -05:00
####################################################################################################################################
# BACKUP - Perform the backup
####################################################################################################################################
sub backup
{
my $ strCommandChecksum = shift ;
my $ strCommandCompress = shift ;
my $ strCommandDecompress = shift ;
my $ strCommandCopy = shift ;
my $ strClusterDataPath = shift ;
my $ strBackupTmpPath = shift ;
my $ oBackupManifestRef = shift ;
2013-12-16 21:59:22 -05:00
my $ strSectionPath ;
2013-12-15 17:18:50 -05:00
my $ strCommand ;
# Create the backup file dir
2013-12-16 21:59:22 -05:00
my $ strBackupDestinationTmpFile = "${strBackupTmpPath}/file.tmp" ;
2013-12-15 17:18:50 -05:00
2013-12-16 21:59:22 -05:00
# Iterate through the path sections to backup
foreach $ strSectionPath ( sort ( keys $ oBackupManifestRef ) )
2013-12-15 17:18:50 -05:00
{
2013-12-16 21:59:22 -05:00
# Skip non-path sections
if ( $ strSectionPath !~ /\:path$/ )
2013-12-15 17:18:50 -05:00
{
next ;
}
2013-12-16 21:59:22 -05:00
# Determine the source and destination backup paths
my $ strBackupSourcePath ;
my $ strBackupDestinationPath ;
my $ strSectionFile ;
2013-12-15 17:18:50 -05:00
2013-12-16 21:59:22 -05:00
if ( $ strSectionPath =~ /^base\:/ )
2013-12-15 17:18:50 -05:00
{
2013-12-16 21:59:22 -05:00
$ strBackupSourcePath = "${strClusterDataPath}" ;
$ strBackupDestinationPath = "${strBackupTmpPath}/base" ;
$ strSectionFile = "base:file" ;
2013-12-15 17:18:50 -05:00
}
2013-12-16 21:59:22 -05:00
elsif ( $ strSectionPath =~ /^tablespace\:/ )
2013-12-15 17:18:50 -05:00
{
2013-12-16 21:59:22 -05:00
my $ strTablespaceName = ( split ( ":" , $ strSectionPath ) ) [ 1 ] ;
$ strBackupSourcePath = $ { $ oBackupManifestRef } { "base:tablespace" } { "${strTablespaceName}" } { path } ;
$ strBackupDestinationPath = "${strBackupTmpPath}/tablespace" ;
2013-12-15 17:18:50 -05:00
2013-12-16 21:59:22 -05:00
# Create the tablespace path
unless ( - e $ strBackupDestinationPath )
2013-12-15 17:18:50 -05:00
{
2013-12-16 21:59:22 -05:00
execute ( "mkdir -p -m 0750 ${strBackupDestinationPath}" ) ;
2013-12-15 17:18:50 -05:00
}
2013-12-16 21:59:22 -05:00
$ strBackupDestinationPath = "${strBackupTmpPath}/tablespace/${strTablespaceName}" ;
$ strSectionFile = "tablespace:${strTablespaceName}:file" ;
#my $strSectionLink = "tablespace:${strTablespaceName}:link";
2013-12-15 17:18:50 -05:00
2013-12-16 21:59:22 -05:00
# Create link to the tablespace
my $ strTablespaceLink = "pg_tblspc/" . $ { $ oBackupManifestRef } { "base:tablespace" } { "${strTablespaceName}" } { oid } ;
my $ strBackupTablespaceLink = "${strBackupTmpPath}/base/${strTablespaceLink}" ;
#my $strBackupLinkPath = ${$oBackupManifestRef}{"base:tablespace"}{"${strTablespaceName}"}{path};
2013-12-15 17:18:50 -05:00
2013-12-16 21:59:22 -05:00
if ( - e $ strBackupTablespaceLink )
2013-12-15 17:18:50 -05:00
{
2013-12-16 21:59:22 -05:00
unlink $ strBackupTablespaceLink or die & log ( ERROR , "Unable to remove table link '${strBackupTablespaceLink}'" ) ;
2013-12-15 17:18:50 -05:00
}
2013-12-16 21:59:22 -05:00
execute ( "ln -s ../../tablespace/${strTablespaceName} $strBackupTablespaceLink" ) ;
execute ( "chmod " . $ { $ oBackupManifestRef } { "base:link" } { $ strTablespaceLink } { permission } . " ${strBackupTablespaceLink}" ) ;
}
else
{
die "Cannot find type for section ${strSectionPath}" ;
}
2013-12-15 17:18:50 -05:00
2013-12-16 21:59:22 -05:00
# Create the base or tablespace path
unless ( - e $ strBackupDestinationPath )
{
execute ( "mkdir -p -m 0750 ${strBackupDestinationPath}" ) ;
#mkdir $strBackupDestinationPath, 0750 or die "Unable to create path ${strBackupDestinationPath}";
}
2013-12-15 17:18:50 -05:00
2013-12-16 21:59:22 -05:00
# Create all the paths required to store the files
my $ strPath ;
foreach $ strPath ( sort ( keys $ { $ oBackupManifestRef } { "${strSectionPath}" } ) )
{
2013-12-17 07:47:24 -05:00
my $ lModificationTime = $ { $ oBackupManifestRef } { "${strSectionPath}" } { "$strPath" } { modification_time } ;
2013-12-16 21:59:22 -05:00
my $ strBackupDestinationSubPath = "${strBackupDestinationPath}/${strPath}" ;
2013-12-15 17:18:50 -05:00
2013-12-16 21:59:22 -05:00
unless ( - e $ strBackupDestinationSubPath )
2013-12-15 17:18:50 -05:00
{
2013-12-16 21:59:22 -05:00
execute ( "mkdir -p ${strBackupDestinationSubPath}" ) ;
2013-12-15 17:18:50 -05:00
}
2013-12-16 21:59:22 -05:00
execute ( "chmod ${$oBackupManifestRef}{$strSectionPath}{$strPath}{permission} ${strBackupDestinationSubPath}" ) ;
2013-12-17 07:47:24 -05:00
# utime($lModificationTime, $lModificationTime, $strBackupDestinationTmpFile) or die "Unable to set time for ${strBackupDestinationTmpFile}";
2013-12-16 21:59:22 -05:00
}
2013-12-15 17:18:50 -05:00
2013-12-16 21:59:22 -05:00
#&log(DEBUG, "Backing up ${strBackupSourcePath}");
2013-12-15 17:18:50 -05:00
2013-12-16 21:59:22 -05:00
# Possible for the path section to exist with no files (i.e. empty tablespace)
if ( ! defined ( $ { $ oBackupManifestRef } { "${strSectionFile}" } ) )
{
next ;
}
2013-12-15 17:18:50 -05:00
2013-12-16 21:59:22 -05:00
# Iterate through the files for each backup source path
my $ strFile ;
foreach $ strFile ( sort ( keys $ { $ oBackupManifestRef } { "${strSectionFile}" } ) )
{
my $ strBackupSourceFile = "${strBackupSourcePath}/${strFile}" ;
my $ iSize = $ { $ oBackupManifestRef } { "${strSectionFile}" } { "$strFile" } { size } ;
2013-12-17 07:47:24 -05:00
my $ lModificationTime = $ { $ oBackupManifestRef } { "${strSectionFile}" } { "$strFile" } { modification_time } ;
2013-12-15 17:18:50 -05:00
2013-12-16 21:59:22 -05:00
#&log(DEBUG, " Backing up ${strBackupSourceFile}");
# Copy the file and calculate checksum
my $ strBackupDestinationFile = "${strBackupDestinationPath}/${strFile}" ;
my $ strHash ;
if ( $ iSize == 0 )
2013-12-15 17:18:50 -05:00
{
2013-12-16 21:59:22 -05:00
execute ( "touch ${strBackupDestinationTmpFile}" )
}
else
{
if ( $ bNoCompression || $ iSize == 0 )
2013-12-15 18:18:54 -05:00
{
2013-12-16 21:59:22 -05:00
$ strCommand = $ strCommandCopy ;
$ strCommand =~ s/\%source\%/${strBackupSourceFile}/g ;
$ strCommand =~ s/\%destination\%/${strBackupDestinationTmpFile}/g ;
execute ( $ strCommand ) ;
$ strHash = file_hash_get ( $ strCommandChecksum , $ strBackupDestinationTmpFile ) ;
2013-12-15 18:18:54 -05:00
}
else
{
2013-12-16 21:59:22 -05:00
$ strCommand = $ strCommandCompress ;
$ strCommand =~ s/\%file\%/${strBackupSourceFile}/g ;
$ strCommand . = " > ${strBackupDestinationTmpFile}" ;
execute ( $ strCommand ) ;
$ strCommand = $ strCommandDecompress ;
2013-12-15 18:18:54 -05:00
$ strCommand =~ s/\%file\%/${strBackupDestinationTmpFile}/g ;
2013-12-16 21:59:22 -05:00
$ strCommand . = " | " . $ strCommandChecksum ;
$ strCommand =~ s/\%file\%//g ;
2013-12-15 17:18:50 -05:00
2013-12-16 21:59:22 -05:00
#&log(DEBUG, " command ${strCommand}");
$ strHash = trim ( execute ( $ strCommand ) ) ;
2013-12-15 17:18:50 -05:00
}
2013-12-16 21:59:22 -05:00
#&log(DEBUG, " Hash ${strHash}");
if ( ! $ bNoCompression )
2013-12-15 17:18:50 -05:00
{
2013-12-16 21:59:22 -05:00
$ strBackupDestinationFile . = ".gz" ;
2013-12-15 17:18:50 -05:00
}
}
2013-12-16 21:59:22 -05:00
# Set permissions and move the file
execute ( "chmod ${$oBackupManifestRef}{$strSectionFile}{$strFile}{permission} ${strBackupDestinationTmpFile}" ) ;
2013-12-17 07:47:24 -05:00
utime ( $ lModificationTime , $ lModificationTime , $ strBackupDestinationTmpFile ) or die "Unable to set time for ${strBackupDestinationTmpFile}" ;
2013-12-16 21:59:22 -05:00
rename $ strBackupDestinationTmpFile , $ strBackupDestinationFile or die "Unable to move ${strBackupDestinationFile}" ;
2013-12-15 17:18:50 -05:00
# Write the hash into the backup manifest
2013-12-16 21:59:22 -05:00
if ( defined ( $ strHash ) )
{
$ { $ oBackupManifestRef } { "${strSectionFile}" } { "$strFile" } { checksum } = $ strHash ;
}
2013-12-15 17:18:50 -05:00
}
}
}
2013-12-02 21:23:10 -05:00
####################################################################################################################################
2013-11-23 19:16:09 -05:00
# START MAIN
2013-12-02 21:23:10 -05:00
####################################################################################################################################
2013-11-17 21:48:53 -05:00
# Get the command
2013-12-04 22:30:26 -05:00
my $ strOperation = $ ARGV [ 0 ] ;
2013-11-17 21:48:53 -05:00
2013-12-04 22:30:26 -05:00
####################################################################################################################################
# LOAD CONFIG FILE
####################################################################################################################################
2013-12-04 21:37:45 -05:00
if ( ! defined ( $ strConfigFile ) )
{
$ strConfigFile = "/etc/pg_backrest.conf" ;
}
my % oConfig ;
tie % oConfig , 'Config::IniFiles' , ( - file = > $ strConfigFile ) or die "Unable to find config file" ;
2013-12-11 20:39:07 -05:00
# Load commands required for archive-push
my $ strCommandChecksum = config_load ( \ % oConfig , "command" , "checksum" , ! $ bNoChecksum ) ;
my $ strCommandCompress = config_load ( \ % oConfig , "command" , "compress" , ! $ bNoCompression ) ;
2013-12-15 17:18:50 -05:00
my $ strCommandDecompress = config_load ( \ % oConfig , "command" , "decompress" , ! $ bNoCompression ) ;
2013-12-11 20:39:07 -05:00
my $ strCommandCopy = config_load ( \ % oConfig , "command" , "copy" , $ bNoCompression ) ;
2013-12-02 21:23:10 -05:00
####################################################################################################################################
2013-12-10 19:11:54 -05:00
# ARCHIVE-PUSH Command
2013-12-02 21:23:10 -05:00
####################################################################################################################################
2013-12-10 19:11:54 -05:00
if ( $ strOperation eq "archive-push" )
2013-11-17 21:48:53 -05:00
{
2013-12-10 19:11:54 -05:00
# archive-push command must have three arguments
2013-12-02 21:23:10 -05:00
if ( @ ARGV != 3 )
{
die "not enough arguments - show usage" ;
}
2013-12-02 16:21:40 -05:00
# Get the source dir/file
2013-12-02 21:23:10 -05:00
my $ strSourceFile = $ ARGV [ 1 ] ;
2013-12-02 16:21:40 -05:00
2013-12-02 21:23:10 -05:00
unless ( - e $ strSourceFile )
{
die "source file does not exist - show usage" ;
}
2013-12-02 16:21:40 -05:00
# Get the destination dir/file
2013-12-02 21:23:10 -05:00
my $ strDestinationFile = $ ARGV [ 2 ] ;
# Make sure the destination directory exists
unless ( - e dirname ( $ strDestinationFile ) )
{
die "destination dir does not exist - show usage" ;
}
# Make sure the destination file does NOT exist - ignore checksum and extension in case they (or options) have changed
if ( glob ( "$strDestinationFile*" ) )
{
die "destination file already exists" ;
}
2013-12-02 21:26:32 -05:00
# Setup the copy command
2013-12-02 21:23:10 -05:00
my $ strCommand = "" ;
2013-12-15 20:04:07 -05:00
if ( $ strSourceFile =~ /\.backup$/ )
2013-12-02 21:23:10 -05:00
{
2013-12-11 20:39:07 -05:00
$ strCommand = $ strCommandCopy ;
2013-12-02 21:23:10 -05:00
$ strCommand =~ s/\%source\%/$strSourceFile/g ;
$ strCommand =~ s/\%destination\%/$strDestinationFile/g ;
}
else
{
2013-12-15 20:04:07 -05:00
# Calculate sha1 hash for the file (unless disabled)
if ( ! $ bNoChecksum )
{
$ strDestinationFile . = "-" . file_hash_get ( $ strCommandChecksum , $ strSourceFile ) ;
}
if ( $ bNoCompression )
{
$ strCommand = $ strCommandCopy ;
$ strCommand =~ s/\%source\%/$strSourceFile/g ;
$ strCommand =~ s/\%destination\%/$strDestinationFile/g ;
}
else
{
$ strCommand = $ strCommandCompress ;
$ strCommand =~ s/\%file\%/$strSourceFile/g ;
$ strCommand . = " > $strDestinationFile.gz" ;
}
2013-12-02 21:23:10 -05:00
}
2013-11-17 13:58:21 -05:00
2013-12-02 21:26:32 -05:00
# Execute the copy
2013-11-23 19:16:09 -05:00
execute ( $ strCommand ) ;
2013-12-04 21:37:45 -05:00
2013-12-04 22:30:26 -05:00
exit 0 ;
}
####################################################################################################################################
# GET MORE CONFIG INFO
####################################################################################################################################
2013-12-11 19:57:54 -05:00
# Load and check the base backup path
2013-12-11 19:22:41 -05:00
my $ strBasePath = $ oConfig { common } { backup_path } ;
2013-12-06 20:24:14 -05:00
2013-12-11 19:22:41 -05:00
if ( ! defined ( $ strBasePath ) )
2013-12-04 22:30:26 -05:00
{
2013-12-11 19:57:54 -05:00
die & log ( ERROR , "common:backup_path undefined" ) ;
2013-12-04 21:37:45 -05:00
}
2013-12-11 19:22:41 -05:00
unless ( - e $ strBasePath )
{
2013-12-11 19:57:54 -05:00
die & log ( ERROR , "base path ${strBasePath} does not exist" ) ;
2013-12-11 19:22:41 -05:00
}
2013-12-11 19:57:54 -05:00
# Load and check the cluster
2013-12-11 19:22:41 -05:00
if ( ! defined ( $ strCluster ) )
{
2013-12-11 19:57:54 -05:00
$ strCluster = "db" ; #!!! Modify to load cluster from conf if there is only one, else error
2013-12-11 19:22:41 -05:00
}
2013-12-11 20:13:39 -05:00
my $ strBackupClusterPath = "${strBasePath}/${strCluster}" ;
2013-12-11 19:22:41 -05:00
2013-12-11 20:13:39 -05:00
unless ( - e $ strBackupClusterPath )
2013-12-11 19:22:41 -05:00
{
2013-12-11 20:13:39 -05:00
& log ( INFO , "creating cluster path ${strBackupClusterPath}" ) ;
mkdir $ strBackupClusterPath or die & log ( ERROR , "cluster backup path '${strBackupClusterPath}' create failed" ) ;
2013-12-11 19:22:41 -05:00
}
2013-12-06 20:24:14 -05:00
2013-12-11 20:39:07 -05:00
# Load commands required for backup
my $ strCommandManifest = config_load ( \ % oConfig , "command" , "manifest" ) ;
2013-12-15 20:04:07 -05:00
my $ strCommandPsql = config_load ( \ % oConfig , "command" , "psql" ) ;
2013-12-11 19:57:54 -05:00
2013-12-04 21:37:45 -05:00
####################################################################################################################################
# BACKUP
####################################################################################################################################
2013-12-04 22:30:26 -05:00
if ( $ strOperation eq "backup" )
2013-12-04 21:37:45 -05:00
{
2013-12-11 19:22:41 -05:00
# Make sure that the cluster data directory exists
my $ strClusterDataPath = $ oConfig { "cluster:$strCluster" } { data_path } ;
2013-12-04 22:30:26 -05:00
2013-12-11 19:22:41 -05:00
if ( ! defined ( $ strClusterDataPath ) )
2013-12-05 23:29:05 -05:00
{
2013-12-11 19:22:41 -05:00
die & log ( ERROR , "cluster data path is not defined" ) ;
2013-12-05 23:29:05 -05:00
}
2013-12-11 19:22:41 -05:00
unless ( - e $ strClusterDataPath )
2013-12-04 22:30:26 -05:00
{
2013-12-11 19:22:41 -05:00
die & log ( ERROR , "cluster data path '${strClusterDataPath}' does not exist" ) ;
2013-12-04 22:30:26 -05:00
}
2013-12-11 19:22:41 -05:00
# Build backup tmp and config
2013-12-11 20:13:39 -05:00
my $ strBackupTmpPath = "${strBackupClusterPath}/backup.tmp" ;
2013-12-15 17:18:50 -05:00
my $ strBackupConfFile = "${strBackupTmpPath}/backup.manifest" ;
2013-12-05 23:29:05 -05:00
2013-12-11 19:22:41 -05:00
# If the backup tmp path already exists, delete the conf file
2013-12-11 20:13:39 -05:00
if ( - e $ strBackupTmpPath )
2013-12-05 08:59:39 -05:00
{
2013-12-16 21:59:22 -05:00
& log ( WARNING , "backup path $strBackupTmpPath already exists" ) ;
2013-12-11 19:22:41 -05:00
if ( - e $ strBackupConfFile )
{
2013-12-11 19:57:54 -05:00
unlink $ strBackupConfFile or die & log ( ERROR , "backup config ${strBackupConfFile} could not be deleted" ) ;
2013-12-11 19:22:41 -05:00
}
2013-12-05 08:59:39 -05:00
}
2013-12-11 19:22:41 -05:00
# Else create the backup tmp path
2013-12-06 20:24:14 -05:00
else
{
2013-12-11 20:13:39 -05:00
& log ( INFO , "creating backup path $strBackupTmpPath" ) ;
mkdir $ strBackupTmpPath or die & log ( ERROR , "backup path ${strBackupTmpPath} could not be created" ) ;
2013-12-06 20:24:14 -05:00
}
2013-12-15 17:18:50 -05:00
# Create a new backup manifest hash
2013-12-14 13:14:59 -05:00
my % oBackupManifest ;
2013-12-15 20:56:42 -05:00
2013-12-15 20:04:07 -05:00
# Start backup
2013-12-15 20:56:42 -05:00
my $ strLabel = "test_label" ;
my $ strArchiveStart = trim ( execute ( $ strCommandPsql .
" -c \"copy (select pg_xlogfile_name(xlog) from pg_start_backup('${strLabel}') as xlog) to stdout\" postgres" ) ) ;
2013-12-16 21:59:22 -05:00
2013-12-17 07:47:24 -05:00
$ { oBackupManifest } { archive } { archive_location } { startg } = $ strArchiveStart ;
2013-12-15 20:56:42 -05:00
& log ( INFO , 'Backup archive start: ' . $ strArchiveStart ) ;
2013-12-11 19:22:41 -05:00
# Build the backup manifest
2013-12-15 20:04:07 -05:00
my % oTablespaceMap = tablespace_map_get ( $ strCommandPsql ) ;
2013-12-14 14:03:08 -05:00
backup_manifest_build ( $ strCommandManifest , $ strClusterDataPath , \ % oBackupManifest , \ % oTablespaceMap ) ;
2013-12-11 19:57:54 -05:00
# Perform the backup
2013-12-16 21:59:22 -05:00
backup ( $ strCommandChecksum , $ strCommandCompress , $ strCommandDecompress , $ strCommandCopy , $ strClusterDataPath ,
$ strBackupTmpPath , \ % oBackupManifest ) ;
2013-12-15 17:18:50 -05:00
2013-12-15 20:04:07 -05:00
# Stop backup
2013-12-15 20:56:42 -05:00
my $ strArchiveStop = trim ( execute ( $ strCommandPsql .
" -c \"copy (select pg_xlogfile_name(xlog) from pg_stop_backup() as xlog) to stdout\" postgres" ) ) ;
2013-12-15 20:04:07 -05:00
2013-12-16 21:59:22 -05:00
$ { oBackupManifest } { archive } { archive_location } { stop } = $ strArchiveStop ;
2013-12-15 20:56:42 -05:00
& log ( INFO , 'Backup archive stop: ' . $ strArchiveStop ) ;
# Fetch the archive logs and backup file
# !!! do it
2013-12-15 20:04:07 -05:00
# Delete files leftover from a partial backup
# !!! do it
2013-12-11 19:22:41 -05:00
# Save the backup conf file
2013-12-14 15:02:47 -05:00
backup_manifest_save ( $ strBackupConfFile , \ % oBackupManifest ) ;
backup_manifest_load ( $ strBackupConfFile ) ;
2013-12-11 19:57:54 -05:00
# Rename the backup tmp path to complete the backup
# !!! Still not sure about format, probably YYYYMMDDTHH24MMSS
2013-11-17 13:58:21 -05:00
}