1
0
mirror of https://github.com/pgbackrest/pgbackrest.git synced 2025-07-13 01:00:23 +02:00

Add info command set option for detailed text output.

The additional details include databases that can be used for selective restore and a list of tablespaces and symlinks with their default destinations.

This information is not included in the JSON output because it requires reading the manifest which is too IO intensive to do for all manifests.  We plan to include this information for JSON in a future release.
This commit is contained in:
Cynthia Shang
2019-09-30 12:39:38 -04:00
committed by David Steele
parent 33ec5a3aac
commit f96c54c4ba
8 changed files with 538 additions and 16 deletions

View File

@ -823,7 +823,15 @@ my %hConfigDefine =
&CFGCMD_RESTORE =>
{
&CFGDEF_DEFAULT => 'latest',
}
},
&CFGCMD_INFO =>
{
&CFGDEF_REQUIRED => false,
&CFGDEF_DEPEND =>
{
&CFGDEF_DEPEND_OPTION => CFGOPT_STANZA,
},
},
}
},

View File

@ -1135,6 +1135,13 @@
<example>json</example>
</option>
<option id="set" name="Set">
<summary>Backup set to detail.</summary>
<text>Details include a list of databases (with OIDs) in the backup set (excluding template databases), tablespaces (with OIDs) with the destination where they will be restored by default, and symlinks with the destination where they will be restored when <setting>--link-all</setting> is specified.</text>
<example>20150131-153358F_20150131-153401I</example>
</option>
</option-list>
<command-example-list>

View File

@ -15,6 +15,16 @@
<release date="XXXX-XX-XX" version="2.18dev" title="UNDER DEVELOPMENT">
<release-core-list>
<release-feature-list>
<release-item>
<release-item-contributor-list>
<release-item-contributor id="cynthia.shang"/>
</release-item-contributor-list>
<p>Add <cmd>info</cmd> command <br-option>set</br-option> option for detailed text output.</p>
<p>The additional details include databases that can be used for selective restore and a list of tablespaces and symlinks with their default destinations.</p>
</release-item>
<release-item>
<release-item-contributor-list>
<release-item-reviewer id="cynthia.shang"/>

View File

@ -1752,6 +1752,22 @@
</execute>
</execute-list>
<p>If the database to restore is not known, use the <cmd>info</cmd> command <br-option>set</br-option> option to discover databases that are part of the backup set.</p>
<execute-list host="{[host-pg1]}">
<title>Show database list for backup</title>
<execute user="postgres" show="n" variable-key="backup-last-incr">
<exe-cmd>{[cmd-backup-last]}</exe-cmd>
</execute>
<execute user="postgres">
<exe-cmd>{[project-exe]} {[dash]}-stanza={[postgres-cluster-demo]}
{[dash]}-set={[backup-last-incr]} info</exe-cmd>
<exe-highlight>database list</exe-highlight>
</execute>
</execute-list>
<p>Stop the cluster and restore only the test2 database. Built-in databases (<id>template0</id>, <id>template1</id>, and <id>postgres</id>) are always restored.</p>
<execute-list host="{[host-pg1]}">

View File

@ -269,7 +269,7 @@ command/expire/expire.o: command/expire/expire.c build.auto.h command/archive/co
command/help/help.o: command/help/help.c build.auto.h common/assert.h common/debug.h common/error.auto.h common/error.h common/io/filter/filter.h common/io/filter/group.h common/io/handleWrite.h common/io/write.h common/lock.h common/log.h common/logLevel.h common/memContext.h common/stackTrace.h common/time.h common/type/buffer.h common/type/convert.h common/type/keyValue.h common/type/list.h common/type/string.h common/type/stringList.h common/type/variant.h common/type/variantList.h config/config.auto.h config/config.h config/define.auto.h config/define.h version.h
$(CC) $(CPPFLAGS) $(CFLAGS) $(CMAKE) -c command/help/help.c -o command/help/help.o
command/info/info.o: command/info/info.c build.auto.h command/archive/common.h command/info/info.h common/assert.h common/crypto/common.h common/debug.h common/error.auto.h common/error.h common/ini.h common/io/filter/filter.h common/io/filter/group.h common/io/handleWrite.h common/io/read.h common/io/write.h common/lock.h common/log.h common/logLevel.h common/memContext.h common/stackTrace.h common/time.h common/type/buffer.h common/type/convert.h common/type/json.h common/type/keyValue.h common/type/list.h common/type/string.h common/type/stringList.h common/type/variant.h common/type/variantList.h config/config.auto.h config/config.h config/define.auto.h config/define.h info/info.h info/infoArchive.h info/infoBackup.h info/infoPg.h perl/exec.h postgres/interface.h storage/helper.h storage/info.h storage/read.h storage/storage.h storage/write.h
command/info/info.o: command/info/info.c build.auto.h command/archive/common.h command/backup/common.h command/info/info.h common/assert.h common/crypto/common.h common/crypto/hash.h common/debug.h common/error.auto.h common/error.h common/ini.h common/io/filter/filter.h common/io/filter/group.h common/io/handleWrite.h common/io/read.h common/io/write.h common/lock.h common/log.h common/logLevel.h common/memContext.h common/stackTrace.h common/time.h common/type/buffer.h common/type/convert.h common/type/json.h common/type/keyValue.h common/type/list.h common/type/string.h common/type/stringList.h common/type/variant.h common/type/variantList.h config/config.auto.h config/config.h config/define.auto.h config/define.h info/info.h info/infoArchive.h info/infoBackup.h info/infoPg.h info/manifest.h perl/exec.h postgres/interface.h storage/helper.h storage/info.h storage/read.h storage/storage.h storage/write.h
$(CC) $(CPPFLAGS) $(CFLAGS) $(CMAKE) -c command/info/info.c -o command/info/info.o
command/local/local.o: command/local/local.c build.auto.h command/archive/get/protocol.h command/archive/push/protocol.h command/backup/protocol.h command/restore/protocol.h common/assert.h common/debug.h common/error.auto.h common/error.h common/io/filter/filter.h common/io/filter/group.h common/io/handleRead.h common/io/handleWrite.h common/io/read.h common/io/write.h common/lock.h common/log.h common/logLevel.h common/memContext.h common/stackTrace.h common/time.h common/type/buffer.h common/type/convert.h common/type/keyValue.h common/type/list.h common/type/string.h common/type/stringList.h common/type/variant.h common/type/variantList.h config/config.auto.h config/config.h config/define.auto.h config/define.h config/protocol.h protocol/client.h protocol/command.h protocol/helper.h protocol/server.h

View File

@ -21,6 +21,7 @@ Info Command
#include "info/infoArchive.h"
#include "info/infoBackup.h"
#include "info/infoPg.h"
#include "info/manifest.h"
#include "perl/exec.h"
#include "postgres/interface.h"
#include "storage/helper.h"
@ -36,10 +37,13 @@ VARIANT_STRDEF_STATIC(ARCHIVE_KEY_MAX_VAR, "max");
VARIANT_STRDEF_STATIC(BACKREST_KEY_FORMAT_VAR, "format");
VARIANT_STRDEF_STATIC(BACKREST_KEY_VERSION_VAR, "version");
VARIANT_STRDEF_STATIC(BACKUP_KEY_BACKREST_VAR, "backrest");
VARIANT_STRDEF_STATIC(BACKUP_KEY_DATABASE_REF_VAR, "database-ref");
VARIANT_STRDEF_STATIC(BACKUP_KEY_INFO_VAR, "info");
VARIANT_STRDEF_STATIC(BACKUP_KEY_LABEL_VAR, "label");
VARIANT_STRDEF_STATIC(BACKUP_KEY_LINK_VAR, "link");
VARIANT_STRDEF_STATIC(BACKUP_KEY_PRIOR_VAR, "prior");
VARIANT_STRDEF_STATIC(BACKUP_KEY_REFERENCE_VAR, "reference");
VARIANT_STRDEF_STATIC(BACKUP_KEY_TABLESPACE_VAR, "tablespace");
VARIANT_STRDEF_STATIC(BACKUP_KEY_TIMESTAMP_VAR, "timestamp");
VARIANT_STRDEF_STATIC(BACKUP_KEY_TYPE_VAR, "type");
VARIANT_STRDEF_STATIC(DB_KEY_ID_VAR, "id");
@ -49,12 +53,14 @@ VARIANT_STRDEF_STATIC(INFO_KEY_REPOSITORY_VAR, "repository"
VARIANT_STRDEF_STATIC(KEY_ARCHIVE_VAR, "archive");
VARIANT_STRDEF_STATIC(KEY_DATABASE_VAR, "database");
VARIANT_STRDEF_STATIC(KEY_DELTA_VAR, "delta");
VARIANT_STRDEF_STATIC(KEY_DESTINATION_VAR, "destination");
VARIANT_STRDEF_STATIC(KEY_NAME_VAR, "name");
VARIANT_STRDEF_STATIC(KEY_OID_VAR, "oid");
VARIANT_STRDEF_STATIC(KEY_SIZE_VAR, "size");
VARIANT_STRDEF_STATIC(KEY_START_VAR, "start");
VARIANT_STRDEF_STATIC(KEY_STOP_VAR, "stop");
VARIANT_STRDEF_STATIC(STANZA_KEY_BACKUP_VAR, "backup");
VARIANT_STRDEF_STATIC(STANZA_KEY_CIPHER_VAR, "cipher");
VARIANT_STRDEF_STATIC(STANZA_KEY_NAME_VAR, "name");
VARIANT_STRDEF_STATIC(STANZA_KEY_STATUS_VAR, "status");
VARIANT_STRDEF_STATIC(STANZA_KEY_DB_VAR, "db");
VARIANT_STRDEF_STATIC(STATUS_KEY_CODE_VAR, "code");
@ -188,11 +194,12 @@ archiveDbList(const String *stanza, const InfoPgData *pgData, VariantList *archi
For each current backup in the backup.info file of the stanza, set the data for the backup section.
***********************************************************************************************************************************/
static void
backupList(VariantList *backupSection, InfoBackup *info)
backupList(VariantList *backupSection, InfoBackup *info, const String *backupLabel)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(VARIANT, backupSection);
FUNCTION_TEST_PARAM(INFO_BACKUP, info);
FUNCTION_TEST_PARAM(STRING, backupLabel);
FUNCTION_TEST_END();
ASSERT(backupSection != NULL);
@ -255,9 +262,78 @@ backupList(VariantList *backupSection, InfoBackup *info)
kvAdd(timeInfo, KEY_START_VAR, VARUINT64(backupData.backupTimestampStart));
kvAdd(timeInfo, KEY_STOP_VAR, VARUINT64(backupData.backupTimestampStop));
varLstAdd(backupSection, backupInfo);
// If a backup label was specified and this is that label, then get the manifest
if (backupLabel != NULL && strEq(backupData.backupLabel, backupLabel))
{
// Load the manifest file
const Manifest *manifest = manifestLoadFile(
storageRepo(), strNewFmt(STORAGE_REPO_BACKUP "/%s/" BACKUP_MANIFEST_FILE, strPtr(backupLabel)),
cipherType(cfgOptionStr(cfgOptRepoCipherType)), infoPgCipherPass(infoBackupPg(info)));
// Get the list of databases in this backup
VariantList *databaseSection = varLstNew();
for (unsigned int dbIdx = 0; dbIdx < manifestDbTotal(manifest); dbIdx++)
{
const ManifestDb *db = manifestDb(manifest, dbIdx);
// Do not display template databases
if (db->id > db->lastSystemId)
{
Variant *database = varNewKv(kvNew());
kvPut(varKv(database), KEY_NAME_VAR, VARSTR(db->name));
kvPut(varKv(database), KEY_OID_VAR, VARUINT64(db->id));
varLstAdd(databaseSection, database);
}
}
// Add the database section even if none found
kvPut(varKv(backupInfo), BACKUP_KEY_DATABASE_REF_VAR, varNewVarLst(databaseSection));
// Get symlinks and tablespaces
VariantList *linkSection = varLstNew();
VariantList *tablespaceSection = varLstNew();
for (unsigned int targetIdx = 0; targetIdx < manifestTargetTotal(manifest); targetIdx++)
{
const ManifestTarget *target = manifestTarget(manifest, targetIdx);
Variant *link = varNewKv(kvNew());
Variant *tablespace = varNewKv(kvNew());
if (target->type == manifestTargetTypeLink)
{
if (target->tablespaceName != NULL)
{
kvPut(varKv(tablespace), KEY_NAME_VAR, VARSTR(target->tablespaceName));
kvPut(varKv(tablespace), KEY_DESTINATION_VAR, VARSTR(target->path));
kvPut(varKv(tablespace), KEY_OID_VAR, VARUINT64(target->tablespaceId));
varLstAdd(tablespaceSection, tablespace);
}
else if (target->file != NULL)
{
kvPut(varKv(link), KEY_NAME_VAR, varNewStr(target->file));
kvPut(
varKv(link), KEY_DESTINATION_VAR, varNewStr(strNewFmt("%s/%s", strPtr(target->path),
strPtr(target->file))));
varLstAdd(linkSection, link);
}
else
{
kvPut(varKv(link), KEY_NAME_VAR, VARSTR(manifestPgPath(target->name)));
kvPut(varKv(link), KEY_DESTINATION_VAR, VARSTR(target->path));
varLstAdd(linkSection, link);
}
}
}
kvPut(varKv(backupInfo), BACKUP_KEY_LINK_VAR, (varLstSize(linkSection) > 0 ? varNewVarLst(linkSection) : NULL));
kvPut(
varKv(backupInfo), BACKUP_KEY_TABLESPACE_VAR,
(varLstSize(tablespaceSection) > 0 ? varNewVarLst(tablespaceSection) : NULL));
}
varLstAdd(backupSection, backupInfo);
}
FUNCTION_TEST_RETURN_VOID();
}
@ -266,11 +342,12 @@ backupList(VariantList *backupSection, InfoBackup *info)
Set the stanza data for each stanza found in the repo.
***********************************************************************************************************************************/
static VariantList *
stanzaInfoList(const String *stanza, StringList *stanzaList)
stanzaInfoList(const String *stanza, StringList *stanzaList, const String *backupLabel)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(STRING, stanza);
FUNCTION_TEST_PARAM(STRING_LIST, stanzaList);
FUNCTION_TEST_PARAM(STRING, backupLabel);
FUNCTION_TEST_END();
ASSERT(stanzaList != NULL);
@ -326,8 +403,8 @@ stanzaInfoList(const String *stanza, StringList *stanzaList)
}
TRY_END();
// Set the stanza name and cipher
kvPut(varKv(stanzaInfo), STANZA_KEY_NAME_VAR, VARSTR(stanzaListName));
// Set the stanza name and cipher. Since we may not be going through the config parsing system, default the cipher to NONE.
kvPut(varKv(stanzaInfo), KEY_NAME_VAR, VARSTR(stanzaListName));
kvPut(varKv(stanzaInfo), STANZA_KEY_CIPHER_VAR, VARSTR(CIPHER_TYPE_NONE_STR));
// If the backup.info file exists, get the database history information (newest to oldest) and corresponding archive
@ -359,7 +436,7 @@ stanzaInfoList(const String *stanza, StringList *stanzaList)
}
// Get data for all existing backups for this stanza
backupList(backupSection, info);
backupList(backupSection, info, backupLabel);
}
// Add the database history, backup and archive sections to the stanza info
@ -386,7 +463,7 @@ stanzaInfoList(const String *stanza, StringList *stanzaList)
{
Variant *stanzaInfo = varNewKv(kvNew());
kvPut(varKv(stanzaInfo), STANZA_KEY_NAME_VAR, VARSTR(stanza));
kvPut(varKv(stanzaInfo), KEY_NAME_VAR, VARSTR(stanza));
kvPut(varKv(stanzaInfo), STANZA_KEY_DB_VAR, varNewVarLst(varLstNew()));
kvPut(varKv(stanzaInfo), STANZA_KEY_BACKUP_VAR, varNewVarLst(varLstNew()));
@ -402,11 +479,12 @@ stanzaInfoList(const String *stanza, StringList *stanzaList)
Format the text output for each database of the stanza.
***********************************************************************************************************************************/
static void
formatTextDb(const KeyValue *stanzaInfo, String *resultStr)
formatTextDb(const KeyValue *stanzaInfo, String *resultStr, const String *backupLabel)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(KEY_VALUE, stanzaInfo);
FUNCTION_TEST_PARAM(STRING, resultStr);
FUNCTION_TEST_PARAM(STRING, backupLabel);
FUNCTION_TEST_END();
ASSERT(stanzaInfo != NULL);
@ -420,6 +498,29 @@ formatTextDb(const KeyValue *stanzaInfo, String *resultStr)
{
KeyValue *pgInfo = varKv(varLstGet(dbSection, dbIdx));
unsigned int dbId = varUInt(kvGet(pgInfo, DB_KEY_ID_VAR));
bool backupInDb = false;
// If a backup label was specified then see if it exists for this database
if (backupLabel != NULL)
{
for (unsigned int backupIdx = 0; backupIdx < varLstSize(backupSection); backupIdx++)
{
KeyValue *backupInfo = varKv(varLstGet(backupSection, backupIdx));
KeyValue *backupDbInfo = varKv(kvGet(backupInfo, KEY_DATABASE_VAR));
unsigned int backupDbId = varUInt(kvGet(backupDbInfo, DB_KEY_ID_VAR));
// If the backup requested is in this database then break from the loop
if (backupDbId == dbId)
{
backupInDb = true;
break;
}
}
}
// If backup label was requested but was not found in this database then continue to next database
if (backupLabel != NULL && !backupInDb)
continue;
// List is ordered so 0 is always the current DB index
if (dbIdx == varLstSize(dbSection) - 1)
@ -461,6 +562,10 @@ formatTextDb(const KeyValue *stanzaInfo, String *resultStr)
KeyValue *backupDbInfo = varKv(kvGet(backupInfo, KEY_DATABASE_VAR));
unsigned int backupDbId = varUInt(kvGet(backupDbInfo, DB_KEY_ID_VAR));
// If a backup label was specified but this is not it then continue
if (backupLabel != NULL && !strEq(varStr(kvGet(backupInfo, BACKUP_KEY_LABEL_VAR)), backupLabel))
continue;
if (backupDbId == dbId)
{
strCatFmt(
@ -512,6 +617,70 @@ formatTextDb(const KeyValue *stanzaInfo, String *resultStr)
StringList *referenceList = strLstNewVarLst(varVarLst(kvGet(backupInfo, BACKUP_KEY_REFERENCE_VAR)));
strCatFmt(backupResult, " backup reference list: %s\n", strPtr(strLstJoin(referenceList, ", ")));
}
if (kvGet(backupInfo, BACKUP_KEY_DATABASE_REF_VAR) != NULL)
{
VariantList *dbSection = kvGetList(backupInfo, BACKUP_KEY_DATABASE_REF_VAR);
strCat(backupResult, " database list:");
if (varLstSize(dbSection) == 0)
strCat(backupResult, " none\n");
else
{
for (unsigned int dbIdx = 0; dbIdx < varLstSize(dbSection); dbIdx++)
{
KeyValue *db = varKv(varLstGet(dbSection, dbIdx));
strCatFmt(
backupResult, " %s (%s)", strPtr(varStr(kvGet(db, KEY_NAME_VAR))),
strPtr(varStrForce(kvGet(db, KEY_OID_VAR))));
if (dbIdx != varLstSize(dbSection) - 1)
strCat(backupResult, ",");
}
strCat(backupResult, "\n");
}
}
if (kvGet(backupInfo, BACKUP_KEY_LINK_VAR) != NULL)
{
VariantList *linkSection = kvGetList(backupInfo, BACKUP_KEY_LINK_VAR);
strCat(backupResult, " symlinks:\n");
for (unsigned int linkIdx = 0; linkIdx < varLstSize(linkSection); linkIdx++)
{
KeyValue *link = varKv(varLstGet(linkSection, linkIdx));
strCatFmt(
backupResult, " %s => %s", strPtr(varStr(kvGet(link, KEY_NAME_VAR))),
strPtr(varStr(kvGet(link, KEY_DESTINATION_VAR))));
if (linkIdx != varLstSize(linkSection) - 1)
strCat(backupResult, "\n");
}
strCat(backupResult, "\n");
}
if (kvGet(backupInfo, BACKUP_KEY_TABLESPACE_VAR) != NULL)
{
VariantList *tablespaceSection = kvGetList(backupInfo, BACKUP_KEY_TABLESPACE_VAR);
strCat(backupResult, " tablespaces:\n");
for (unsigned int tblIdx = 0; tblIdx < varLstSize(tablespaceSection); tblIdx++)
{
KeyValue *tablespace = varKv(varLstGet(tablespaceSection, tblIdx));
strCatFmt(
backupResult, " %s (%s) => %s", strPtr(varStr(kvGet(tablespace, KEY_NAME_VAR))),
strPtr(varStrForce(kvGet(tablespace, KEY_OID_VAR))),
strPtr(varStr(kvGet(tablespace, KEY_DESTINATION_VAR))));
if (tblIdx != varLstSize(tablespaceSection) - 1)
strCat(backupResult, "\n");
}
strCat(backupResult, "\n");
}
}
}
@ -546,6 +715,26 @@ infoRender(void)
// Get stanza if specified
const String *stanza = cfgOptionTest(cfgOptStanza) ? cfgOptionStr(cfgOptStanza) : NULL;
// Get the backup label if specified
const String *backupLabel = cfgOptionTest(cfgOptSet) ? cfgOptionStr(cfgOptSet) : NULL;
// Get the repo storage in case it is remote and encryption settings need to be pulled down
storageRepo();
// If a backup set was specified, see if the manifest exists
if (backupLabel != NULL)
{
if (!strEq(cfgOptionStr(cfgOptOutput), CFGOPTVAL_INFO_OUTPUT_TEXT_STR))
THROW(ConfigError, "option 'set' is currently only valid for text output");
if (!storageExistsNP(storageRepo(), strNewFmt(STORAGE_REPO_BACKUP "/%s/" BACKUP_MANIFEST_FILE, strPtr(backupLabel))))
{
THROW_FMT(
FileMissingError, "manifest does not exist for backup '%s'\n"
"HINT: is the backup listed when running the info command with --stanza option only?", strPtr(backupLabel));
}
}
// Get a list of stanzas in the backup directory.
StringList *stanzaList = storageListNP(storageRepo(), STORAGE_PATH_BACKUP_STR);
VariantList *infoList = varLstNew();
@ -553,7 +742,7 @@ infoRender(void)
// If the backup storage exists, then search for and process any stanzas
if (strLstSize(stanzaList) > 0)
infoList = stanzaInfoList(stanza, stanzaList);
infoList = stanzaInfoList(stanza, stanzaList, backupLabel);
// Format text output
if (strEq(cfgOptionStr(cfgOptOutput), CFGOPTVAL_INFO_OUTPUT_TEXT_STR))
@ -571,7 +760,7 @@ infoRender(void)
// Stanza name and status
strCatFmt(
resultStr, "stanza: %s\n status: ", strPtr(varStr(kvGet(stanzaInfo, STANZA_KEY_NAME_VAR))));
resultStr, "stanza: %s\n status: ", strPtr(varStr(kvGet(stanzaInfo, KEY_NAME_VAR))));
// If an error has occurred, provide the information that is available and move onto next stanza
KeyValue *stanzaStatus = varKv(kvGet(stanzaInfo, STANZA_KEY_STATUS_VAR));
@ -591,7 +780,7 @@ infoRender(void)
// If there is a backup.info file but no backups, then process the archive info
if (statusCode == INFO_STANZA_STATUS_CODE_NO_BACKUP)
formatTextDb(stanzaInfo, resultStr);
formatTextDb(stanzaInfo, resultStr, NULL);
}
continue;
@ -603,7 +792,7 @@ infoRender(void)
strCatFmt(resultStr, " cipher: %s\n",
strPtr(varStr(kvGet(stanzaInfo, STANZA_KEY_CIPHER_VAR))));
formatTextDb(stanzaInfo, resultStr);
formatTextDb(stanzaInfo, resultStr, backupLabel);
}
}
else

View File

@ -4096,11 +4096,28 @@ static ConfigDefineOptionData configDefineOptionData[] = CFGDEFDATA_OPTION_LIST
CFGDEFDATA_OPTION_COMMAND_LIST
(
CFGDEFDATA_OPTION_COMMAND(cfgDefCmdInfo)
CFGDEFDATA_OPTION_COMMAND(cfgDefCmdRestore)
)
CFGDEFDATA_OPTION_OPTIONAL_LIST
(
CFGDEFDATA_OPTION_OPTIONAL_COMMAND_OVERRIDE
(
CFGDEFDATA_OPTION_OPTIONAL_COMMAND(cfgDefCmdInfo)
CFGDEFDATA_OPTION_OPTIONAL_DEPEND(cfgDefOptStanza)
CFGDEFDATA_OPTION_OPTIONAL_REQUIRED(false)
CFGDEFDATA_OPTION_OPTIONAL_HELP_SUMMARY("Backup set to detail.")
CFGDEFDATA_OPTION_OPTIONAL_HELP_DESCRIPTION
(
"Details include a list of databases (with OIDs) in the backup set (excluding template databases), tablespaces "
"(with OIDs) with the destination where they will be restored by default, and symlinks with the "
"destination where they will be restored when --link-all is specified."
)
)
CFGDEFDATA_OPTION_OPTIONAL_COMMAND_OVERRIDE
(
CFGDEFDATA_OPTION_OPTIONAL_COMMAND(cfgDefCmdRestore)

View File

@ -410,6 +410,130 @@ testRun(void)
storagePutNP(storageNewWriteNP(storageLocalWrite(), strNewFmt("%s/backup.info", strPtr(backupStanza1Path))),
harnessInfoChecksum(content)), "put backup info to file - stanza1");
// Manifest with all features
// -------------------------------------------------------------------------------------------------------------------------
#define TEST_MANIFEST_HEADER \
"[backup]\n" \
"backup-archive-start=\"000000030000028500000089\"\n" \
"backup-archive-stop=\"000000030000028500000089\"\n" \
"backup-label=\"20190818-084502F_20190820-084502D\"\n" \
"backup-lsn-start=\"285/89000028\"\n" \
"backup-lsn-stop=\"285/89001F88\"\n" \
"backup-prior=\"20190818-084502F\"\n" \
"backup-timestamp-copy-start=1565282141\n" \
"backup-timestamp-start=1565282140\n" \
"backup-timestamp-stop=1565282142\n" \
"backup-type=\"full\"\n" \
"\n" \
"[backup:db]\n" \
"db-catalog-version=201409291\n" \
"db-control-version=942\n" \
"db-id=1\n" \
"db-system-id=1000000000000000094\n" \
"db-version=\"9.4\"\n" \
"\n" \
"[backup:option]\n" \
"option-archive-check=true\n" \
"option-archive-copy=true\n" \
"option-backup-standby=false\n" \
"option-buffer-size=16384\n" \
"option-checksum-page=true\n" \
"option-compress=false\n" \
"option-compress-level=3\n" \
"option-compress-level-network=3\n" \
"option-delta=false\n" \
"option-hardlink=false\n" \
"option-online=false\n" \
"option-process-max=32\n"
#define TEST_MANIFEST_TARGET \
"\n" \
"[backup:target]\n" \
"pg_data={\"path\":\"/pg/base\",\"type\":\"path\"}\n" \
"pg_data/pg_hba.conf={\"file\":\"pg_hba.conf\",\"path\":\"../pg_config\",\"type\":\"link\"}\n" \
"pg_data/pg_stat={\"path\":\"../pg_stat\",\"type\":\"link\"}\n" \
"pg_tblspc/1={\"path\":\"/tblspc/ts1\",\"tablespace-id\":\"1\",\"tablespace-name\":\"ts1\",\"type\":\"link\"}\n" \
"pg_tblspc/12={\"path\":\"/tblspc/ts12\",\"tablespace-id\":\"12\",\"tablespace-name\":\"ts12\",\"type\":\"link\"}\n"
#define TEST_MANIFEST_DB \
"\n" \
"[db]\n" \
"mail={\"db-id\":16456,\"db-last-system-id\":12168}\n" \
"postgres={\"db-id\":12173,\"db-last-system-id\":12168}\n" \
"template0={\"db-id\":12168,\"db-last-system-id\":12168}\n" \
"template1={\"db-id\":1,\"db-last-system-id\":12168}\n" \
#define TEST_MANIFEST_FILE \
"\n" \
"[target:file]\n" \
"pg_data/PG_VERSION={\"checksum\":\"184473f470864e067ee3a22e64b47b0a1c356f29\",\"master\":true" \
",\"reference\":\"20190818-084502F_20190819-084506D\",\"size\":4,\"timestamp\":1565282114}\n" \
"pg_data/base/16384/17000={\"checksum\":\"e0101dd8ffb910c9c202ca35b5f828bcb9697bed\",\"checksum-page\":false" \
",\"checksum-page-error\":[1],\"repo-size\":4096,\"size\":8192,\"timestamp\":1565282114}\n" \
"pg_data/base/16384/PG_VERSION={\"checksum\":\"184473f470864e067ee3a22e64b47b0a1c356f29\",\"group\":false,\"size\":4" \
",\"timestamp\":1565282115}\n" \
"pg_data/base/32768/33000={\"checksum\":\"7a16d165e4775f7c92e8cdf60c0af57313f0bf90\",\"checksum-page\":true" \
",\"reference\":\"20190818-084502F\",\"size\":1073741824,\"timestamp\":1565282116}\n" \
"pg_data/base/32768/33000.32767={\"checksum\":\"6e99b589e550e68e934fd235ccba59fe5b592a9e\",\"checksum-page\":true" \
",\"reference\":\"20190818-084502F\",\"size\":32768,\"timestamp\":1565282114}\n" \
"pg_data/postgresql.conf={\"checksum\":\"6721d92c9fcdf4248acff1f9a1377127d9064807\",\"master\":true,\"size\":4457" \
",\"timestamp\":1565282114}\n" \
"pg_data/special={\"master\":true,\"mode\":\"0640\",\"size\":0,\"timestamp\":1565282120,\"user\":false}\n"
#define TEST_MANIFEST_FILE_DEFAULT \
"\n" \
"[target:file:default]\n" \
"group=\"group1\"\n" \
"master=false\n" \
"mode=\"0600\"\n" \
"user=\"user1\"\n"
#define TEST_MANIFEST_LINK \
"\n" \
"[target:link]\n" \
"pg_data/pg_stat={\"destination\":\"../pg_stat\"}\n" \
"pg_data/postgresql.conf={\"destination\":\"../pg_config/postgresql.conf\",\"group\":false,\"user\":\"user1\"}\n"
#define TEST_MANIFEST_LINK_DEFAULT \
"\n" \
"[target:link:default]\n" \
"group=\"group1\"\n" \
"user=false\n"
#define TEST_MANIFEST_PATH \
"\n" \
"[target:path]\n" \
"pg_data={\"user\":\"user2\"}\n" \
"pg_data/base={\"group\":\"group2\"}\n" \
"pg_data/base/16384={\"mode\":\"0750\"}\n" \
"pg_data/base/32768={}\n" \
"pg_data/base/65536={\"user\":false}\n"
#define TEST_MANIFEST_PATH_DEFAULT \
"\n" \
"[target:path:default]\n" \
"group=false\n" \
"mode=\"0700\"\n" \
"user=\"user1\"\n"
const Buffer *contentLoad = harnessInfoChecksumZ
(
TEST_MANIFEST_HEADER
TEST_MANIFEST_TARGET
TEST_MANIFEST_DB
TEST_MANIFEST_FILE
TEST_MANIFEST_FILE_DEFAULT
TEST_MANIFEST_LINK
TEST_MANIFEST_LINK_DEFAULT
TEST_MANIFEST_PATH
TEST_MANIFEST_PATH_DEFAULT
);
TEST_RESULT_VOID(
storagePutNP(storageNewWriteNP(storageLocalWrite(),
strNewFmt("%s/20181119-152138F_20181119-152152I/" BACKUP_MANIFEST_FILE, strPtr(backupStanza1Path))), contentLoad),
"write manifest - stanza1");
String *archiveStanza2Path = strNewFmt("%s/stanza2", strPtr(archivePath));
String *backupStanza2Path = strNewFmt("%s/stanza2", strPtr(backupPath));
TEST_RESULT_VOID(storagePathCreateNP(storageLocalWrite(), backupStanza1Path), "backup stanza2 directory");
@ -651,6 +775,142 @@ testRun(void)
" wal archive min/max (9.4-1): none present\n"
, "text - multiple stanzas, one with valid backups, archives in latest DB");
// Backup set requested
//--------------------------------------------------------------------------------------------------------------------------
argList2 = strLstDup(argListText);
strLstAddZ(argList2, "--stanza=stanza1");
strLstAddZ(argList2, "--set=20181119-152138F_20181119-152152I");
harnessCfgLoad(strLstSize(argList2), strLstPtr(argList2));
TEST_RESULT_STR(strPtr(infoRender()),
"stanza: stanza1\n"
" status: ok\n"
" cipher: none\n"
"\n"
" db (prior)\n"
" wal archive min/max (9.4-1): 000000010000000000000002/000000020000000000000003\n"
"\n"
" incr backup: 20181119-152138F_20181119-152152I\n"
" timestamp start/stop: 2018-11-19 15:21:52 / 2018-11-19 15:21:55\n"
" wal start/stop: n/a\n"
" database size: 19.2MB, backup size: 8.2KB\n"
" repository size: 2.3MB, repository backup size: 346B\n"
" backup reference list: 20181119-152138F, 20181119-152138F_20181119-152152D\n"
" database list: mail (16456), postgres (12173)\n"
" symlinks:\n"
" pg_hba.conf => ../pg_config/pg_hba.conf\n"
" pg_stat => ../pg_stat\n"
" tablespaces:\n"
" ts1 (1) => /tblspc/ts1\n"
" ts12 (12) => /tblspc/ts12\n"
, "text - backup set requested");
strLstAddZ(argList2, "--output=json");
harnessCfgLoad(strLstSize(argList2), strLstPtr(argList2));
TEST_ERROR(strPtr(infoRender()), ConfigError, "option 'set' is currently only valid for text output");
// Backup set requested but no links
//--------------------------------------------------------------------------------------------------------------------------
argList2 = strLstDup(argListText);
strLstAddZ(argList2, "--stanza=stanza1");
strLstAddZ(argList2, "--set=20181119-152138F_20181119-152152I");
harnessCfgLoad(strLstSize(argList2), strLstPtr(argList2));
#define TEST_MANIFEST_TARGET_NO_LINK \
"\n" \
"[backup:target]\n" \
"pg_data={\"path\":\"/pg/base\",\"type\":\"path\"}\n" \
contentLoad = harnessInfoChecksumZ
(
TEST_MANIFEST_HEADER
TEST_MANIFEST_TARGET_NO_LINK
TEST_MANIFEST_DB
TEST_MANIFEST_FILE
TEST_MANIFEST_FILE_DEFAULT
TEST_MANIFEST_LINK
TEST_MANIFEST_LINK_DEFAULT
TEST_MANIFEST_PATH
TEST_MANIFEST_PATH_DEFAULT
);
TEST_RESULT_VOID(
storagePutNP(
storageNewWriteNP(
storageRepoWrite(), strNew(STORAGE_REPO_BACKUP "/20181119-152138F_20181119-152152I/" BACKUP_MANIFEST_FILE)),
contentLoad),
"write manifest");
TEST_RESULT_STR(strPtr(infoRender()),
"stanza: stanza1\n"
" status: ok\n"
" cipher: none\n"
"\n"
" db (prior)\n"
" wal archive min/max (9.4-1): 000000010000000000000002/000000020000000000000003\n"
"\n"
" incr backup: 20181119-152138F_20181119-152152I\n"
" timestamp start/stop: 2018-11-19 15:21:52 / 2018-11-19 15:21:55\n"
" wal start/stop: n/a\n"
" database size: 19.2MB, backup size: 8.2KB\n"
" repository size: 2.3MB, repository backup size: 346B\n"
" backup reference list: 20181119-152138F, 20181119-152138F_20181119-152152D\n"
" database list: mail (16456), postgres (12173)\n"
, "text - backup set requested, no links");
// Backup set requested but no databases
//--------------------------------------------------------------------------------------------------------------------------
argList2 = strLstDup(argListText);
strLstAddZ(argList2, "--stanza=stanza1");
strLstAddZ(argList2, "--set=20181119-152138F_20181119-152152I");
harnessCfgLoad(strLstSize(argList2), strLstPtr(argList2));
#define TEST_MANIFEST_NO_DB \
"\n" \
"[db]\n" \
"template0={\"db-id\":12168,\"db-last-system-id\":12168}\n" \
"template1={\"db-id\":1,\"db-last-system-id\":12168}\n" \
contentLoad = harnessInfoChecksumZ
(
TEST_MANIFEST_HEADER
TEST_MANIFEST_TARGET_NO_LINK
TEST_MANIFEST_NO_DB
TEST_MANIFEST_FILE
TEST_MANIFEST_FILE_DEFAULT
TEST_MANIFEST_LINK
TEST_MANIFEST_LINK_DEFAULT
TEST_MANIFEST_PATH
TEST_MANIFEST_PATH_DEFAULT
);
TEST_RESULT_VOID(
storagePutNP(
storageNewWriteNP(
storageRepoWrite(), strNew(STORAGE_REPO_BACKUP "/20181119-152138F_20181119-152152I/" BACKUP_MANIFEST_FILE)),
contentLoad),
"write manifest");
TEST_RESULT_STR(strPtr(infoRender()),
"stanza: stanza1\n"
" status: ok\n"
" cipher: none\n"
"\n"
" db (prior)\n"
" wal archive min/max (9.4-1): 000000010000000000000002/000000020000000000000003\n"
"\n"
" incr backup: 20181119-152138F_20181119-152152I\n"
" timestamp start/stop: 2018-11-19 15:21:52 / 2018-11-19 15:21:55\n"
" wal start/stop: n/a\n"
" database size: 19.2MB, backup size: 8.2KB\n"
" repository size: 2.3MB, repository backup size: 346B\n"
" backup reference list: 20181119-152138F, 20181119-152138F_20181119-152152D\n"
" database list: none\n"
, "text - backup set requested, no db");
// Stanza not found
//--------------------------------------------------------------------------------------------------------------------------
argList2 = strLstDup(argList);
@ -790,7 +1050,7 @@ testRun(void)
kvPut(stanzaInfo, KEY_ARCHIVE_VAR, varNewVarLst(varLstNew()));
String *result = strNew("");
formatTextDb(stanzaInfo, result);
formatTextDb(stanzaInfo, result, NULL);
TEST_RESULT_STR(strPtr(result),
"\n"
@ -832,6 +1092,21 @@ testRun(void)
TEST_RESULT_STR(
strPtr(strNewBuf(storageGetNP(storageNewReadNP(storage, stdoutFile)))), "No stanzas exist in the repository.\n",
" check text");
//--------------------------------------------------------------------------------------------------------------------------
strLstAddZ(argList, "--set=bogus");
TEST_ERROR_FMT(
harnessCfgLoad(strLstSize(argList), strLstPtr(argList)), OptionInvalidError,
"option 'set' not valid without option 'stanza'");
//--------------------------------------------------------------------------------------------------------------------------
strLstAddZ(argList, "--stanza=stanza1");
harnessCfgLoad(strLstSize(argList), strLstPtr(argList));
TEST_ERROR_FMT(
cmdInfo(), FileMissingError, "manifest does not exist for backup 'bogus'\n"
"HINT: is the backup listed when running the info command with --stanza option only?");
}
FUNCTION_HARNESS_RESULT_VOID();