mirror of
https://github.com/EnterpriseDB/repmgr.git
synced 2026-03-22 22:56:29 +00:00
Barman support, draft #1
TODO: we need to check what happens with configuration files placed in non-standard locations.
This commit is contained in:
70
README.md
70
README.md
@@ -504,6 +504,76 @@ standby's upstream server is the replication cluster master. While of limited
|
||||
use in a simple master/standby replication cluster, this information is required
|
||||
to effectively manage cascading replication (see below).
|
||||
|
||||
### Using Barman to clone a standby
|
||||
|
||||
`repmgr standby clone` also supports Barman, the Backup and
|
||||
Replication manager (http://www.pgbarman.org/), as a provider of both
|
||||
base backups and WAL files.
|
||||
|
||||
Barman support provides the following advantages:
|
||||
|
||||
- the primary node does not need to perform a new backup every time a
|
||||
new standby is cloned;
|
||||
- a standby node can be disconnected for longer periods without losing
|
||||
the ability to catch up, and without causing accumulation of WAL
|
||||
files on the primary node;
|
||||
- therefore, `repmgr` does not need to use replication slots, and the
|
||||
primary node does not need to set `wal_keep_segments`.
|
||||
|
||||
> *NOTE*: In view of the above, Barman support is incompatible with
|
||||
> the `use_replication_slots` setting in `repmgr.conf`.
|
||||
|
||||
In order to enable Barman support for `repmgr standby clone`, you must
|
||||
ensure that:
|
||||
|
||||
- the name of the server configured in Barman is equal to the
|
||||
`cluster_name` setting in `repmgr.conf`;
|
||||
- the `barman_server` setting in `repmgr.conf` is set to the SSH
|
||||
hostname of the Barman server;
|
||||
- the `pg_restore_command` setting in `repmgr.conf` is configured to
|
||||
use a copy of the `barman-wal-restore` script shipped with Barman
|
||||
(see below);
|
||||
- the Barman catalogue includes at least one valid backup for this
|
||||
server.
|
||||
|
||||
> *NOTE*: Barman support is automatically enabled if `barman_server`
|
||||
> is set. Normally this is a good practice; however, the command line
|
||||
> option `--without-barman` can be used to disable it.
|
||||
|
||||
> *NOTE*: if you have a non-default SSH configuration on the Barman
|
||||
> server, e.g. using a port other than 22, then you can set those
|
||||
> parameters in a dedicated Host section in `~/.ssh/config`
|
||||
> corresponding to the value of `barman_server` in `repmgr.conf`. See
|
||||
> the "Host" section in `man 5 ssh_config` for more details.
|
||||
|
||||
`barman-wal-restore` is a short shell script provided by the Barman
|
||||
development team, which must be copied in a location accessible to
|
||||
`repmgr`, and marked as executable; `pg_restore_command` must then be
|
||||
set as follows:
|
||||
|
||||
<script> <Barman hostname> <cluster_name> %f %p
|
||||
|
||||
For instance, suppose that we have installed Barman on the `barmansrv`
|
||||
host, and that we have placed a copy of `barman-wal-restore` into the
|
||||
`/usr/local/bin` directory. First, we ensure that the script is
|
||||
executable:
|
||||
|
||||
sudo chmod +x /usr/local/bin/barman-wal-restore
|
||||
|
||||
Then we check that `repmgr.conf` includes the following lines:
|
||||
|
||||
barman_server=barmansrv
|
||||
pg_restore_command=/usr/local/bin/barman-wal-restore barmansrv test %f %p
|
||||
|
||||
Now we can clone a standby using the Barman server:
|
||||
|
||||
$ repmgr -h node1 -D 9.5/main -f /etc/repmgr.conf standby clone
|
||||
[2016-06-12 20:08:35] [NOTICE] destination directory '9.5/main' provided
|
||||
[2016-06-12 20:08:35] [NOTICE] getting backup from Barman...
|
||||
[2016-06-12 20:08:36] [NOTICE] standby clone (from Barman) complete
|
||||
[2016-06-12 20:08:36] [NOTICE] you can now start your PostgreSQL server
|
||||
[2016-06-12 20:08:36] [HINT] for example : pg_ctl -D 9.5/data start
|
||||
[2016-06-12 20:08:36] [HINT] After starting the server, you need to register this standby with "repmgr standby register"
|
||||
|
||||
Advanced options for cloning a standby
|
||||
--------------------------------------
|
||||
|
||||
10
config.c
10
config.c
@@ -215,6 +215,7 @@ parse_config(t_configuration_options *options)
|
||||
options->upstream_node = NO_UPSTREAM_NODE;
|
||||
options->use_replication_slots = 0;
|
||||
memset(options->conninfo, 0, sizeof(options->conninfo));
|
||||
memset(options->barman_server, 0, sizeof(options->barman_server));
|
||||
options->failover = MANUAL_FAILOVER;
|
||||
options->priority = DEFAULT_PRIORITY;
|
||||
memset(options->node_name, 0, sizeof(options->node_name));
|
||||
@@ -310,6 +311,8 @@ parse_config(t_configuration_options *options)
|
||||
options->upstream_node = repmgr_atoi(value, "upstream_node", &config_errors, false);
|
||||
else if (strcmp(name, "conninfo") == 0)
|
||||
strncpy(options->conninfo, value, MAXLEN);
|
||||
else if (strcmp(name, "barman_server") == 0)
|
||||
strncpy(options->barman_server, value, MAXLEN);
|
||||
else if (strcmp(name, "rsync_options") == 0)
|
||||
strncpy(options->rsync_options, value, QUERY_STR_LEN);
|
||||
else if (strcmp(name, "ssh_options") == 0)
|
||||
@@ -635,6 +638,13 @@ reload_config(t_configuration_options *orig_options)
|
||||
config_changed = true;
|
||||
}
|
||||
|
||||
/* barman_server */
|
||||
if (strcmp(orig_options->barman_server, new_options.barman_server) != 0)
|
||||
{
|
||||
strcpy(orig_options->barman_server, new_options.barman_server);
|
||||
config_changed = true;
|
||||
}
|
||||
|
||||
/* node */
|
||||
if (orig_options->node != new_options.node)
|
||||
{
|
||||
|
||||
3
config.h
3
config.h
@@ -58,6 +58,7 @@ typedef struct
|
||||
int node;
|
||||
int upstream_node;
|
||||
char conninfo[MAXLEN];
|
||||
char barman_server[MAXLEN];
|
||||
int failover;
|
||||
int priority;
|
||||
char node_name[MAXLEN];
|
||||
@@ -91,7 +92,7 @@ typedef struct
|
||||
* The following will initialize the structure with a minimal set of options;
|
||||
* actual defaults are set in parse_config() before parsing the configuration file
|
||||
*/
|
||||
#define T_CONFIGURATION_OPTIONS_INITIALIZER { "", -1, NO_UPSTREAM_NODE, "", MANUAL_FAILOVER, -1, "", "", "", "", "", "", "", "", "", "", -1, -1, -1, "", "", "", "", "", 0, 0, 0, 0, "", { NULL, NULL }, {NULL, NULL} }
|
||||
#define T_CONFIGURATION_OPTIONS_INITIALIZER { "", -1, NO_UPSTREAM_NODE, "", "", MANUAL_FAILOVER, -1, "", "", "", "", "", "", "", "", "", "", -1, -1, -1, "", "", "", "", "", 0, 0, 0, 0, "", { NULL, NULL }, {NULL, NULL} }
|
||||
|
||||
typedef struct ItemListCell
|
||||
{
|
||||
|
||||
561
repmgr.c
561
repmgr.c
@@ -105,6 +105,10 @@ static bool check_upstream_config(PGconn *conn, int server_version_num, bool exi
|
||||
static bool update_node_record_set_master(PGconn *conn, int this_node_id);
|
||||
static void tablespace_data_append(TablespaceDataList *list, const char *name, const char *oid, const char *location);
|
||||
static int get_tablespace_data(PGconn *upstream_conn, TablespaceDataList *list);
|
||||
static int get_tablespace_data_barman(char *, TablespaceDataList *);
|
||||
|
||||
static char *string_skip_prefix(const char *prefix, char *string);
|
||||
static char *string_remove_trailing_newlines(char *string);
|
||||
|
||||
static char *make_pg_path(char *file);
|
||||
|
||||
@@ -211,6 +215,7 @@ main(int argc, char **argv)
|
||||
{"pwprompt", optional_argument, NULL, OPT_PWPROMPT},
|
||||
{"csv", no_argument, NULL, OPT_CSV},
|
||||
{"node", required_argument, NULL, OPT_NODE},
|
||||
{"without-barman", no_argument, NULL, OPT_WITHOUT_BARMAN},
|
||||
{"version", no_argument, NULL, 'V'},
|
||||
{NULL, 0, NULL, 0}
|
||||
};
|
||||
@@ -508,6 +513,9 @@ main(int argc, char **argv)
|
||||
case OPT_NODE:
|
||||
runtime_options.node = repmgr_atoi(optarg, "--node", &cli_errors, false);
|
||||
break;
|
||||
case OPT_WITHOUT_BARMAN:
|
||||
runtime_options.without_barman = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
unknown_option:
|
||||
@@ -832,6 +840,18 @@ main(int argc, char **argv)
|
||||
log_warning(_("-w/--wal-keep-segments has no effect when replication slots in use\n"));
|
||||
}
|
||||
|
||||
/*
|
||||
* STANDBY CLONE in Barman mode is incompatible with
|
||||
* `use_replication_slots`.
|
||||
*/
|
||||
|
||||
if (action == STANDBY_CLONE &&
|
||||
! runtime_options.without_barman
|
||||
&& strcmp(options.barman_server, "") == 0)
|
||||
{
|
||||
log_err(_("STANDBY CLONE in Barman mode is incompatible with configuration option \"use_replication_slots\""));
|
||||
}
|
||||
|
||||
/* Initialise the repmgr schema name */
|
||||
maxlen_snprintf(repmgr_schema, "%s%s", DEFAULT_REPMGR_SCHEMA_PREFIX,
|
||||
options.cluster_name);
|
||||
@@ -1562,16 +1582,107 @@ get_tablespace_data(PGconn *upstream_conn, TablespaceDataList *list)
|
||||
return retval;
|
||||
}
|
||||
|
||||
char *
|
||||
string_skip_prefix(const char *prefix, char *string)
|
||||
{
|
||||
int n;
|
||||
|
||||
n = strlen(prefix);
|
||||
|
||||
if (strncmp(prefix, string, n))
|
||||
return NULL;
|
||||
else
|
||||
return string + n;
|
||||
}
|
||||
|
||||
char *
|
||||
string_remove_trailing_newlines(char *string)
|
||||
{
|
||||
int n;
|
||||
|
||||
n = strlen(string) - 1;
|
||||
|
||||
while (n >= 0 && string[n] == '\n')
|
||||
string[n] = 0;
|
||||
|
||||
return string;
|
||||
}
|
||||
|
||||
int
|
||||
get_tablespace_data_barman
|
||||
( char *tablespace_data_barman,
|
||||
TablespaceDataList *tablespace_list)
|
||||
{
|
||||
/*
|
||||
* Example:
|
||||
* [('main', 24674, '/var/lib/postgresql/tablespaces/9.5/main'), ('alt', 24678, '/var/lib/postgresql/tablespaces/9.5/alt')]
|
||||
*/
|
||||
|
||||
char name[MAXLEN];
|
||||
char oid[MAXLEN];
|
||||
char location[MAXPGPATH];
|
||||
char *p = tablespace_data_barman;
|
||||
int i;
|
||||
|
||||
tablespace_list->head = NULL;
|
||||
tablespace_list->tail = NULL;
|
||||
|
||||
p = string_skip_prefix("[", p);
|
||||
|
||||
while (*p == '(')
|
||||
{
|
||||
p = string_skip_prefix("('", p);
|
||||
if (p == NULL) return -1;
|
||||
|
||||
i = strcspn(p, "'");
|
||||
strncpy(name, p, i);
|
||||
name[i] = 0;
|
||||
|
||||
p = string_skip_prefix("', ", p + i);
|
||||
if (p == NULL) return -1;
|
||||
|
||||
i = strcspn(p, ",");
|
||||
strncpy(oid, p, i);
|
||||
oid[i] = 0;
|
||||
|
||||
p = string_skip_prefix(", '", p + i);
|
||||
if (p == NULL) return -1;
|
||||
|
||||
i = strcspn(p, "'");
|
||||
strncpy(location, p, i);
|
||||
location[i] = 0;
|
||||
|
||||
p = string_skip_prefix("')", p + i);
|
||||
if (p == NULL) return -1;
|
||||
|
||||
tablespace_data_append (tablespace_list, name, oid, location);
|
||||
|
||||
if (*p == ']')
|
||||
break;
|
||||
|
||||
p = string_skip_prefix(", ", p);
|
||||
if (p == NULL) return -1;
|
||||
}
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
static void
|
||||
do_standby_clone(void)
|
||||
{
|
||||
PGconn *primary_conn = NULL;
|
||||
PGconn *upstream_conn;
|
||||
PGconn *upstream_conn = NULL;
|
||||
PGresult *res;
|
||||
|
||||
enum {
|
||||
barman,
|
||||
rsync,
|
||||
pg_basebackup
|
||||
} mode;
|
||||
|
||||
char sqlquery[QUERY_STR_LEN];
|
||||
|
||||
int server_version_num;
|
||||
int server_version_num = -1;
|
||||
|
||||
char cluster_size[MAXLEN];
|
||||
|
||||
@@ -1586,6 +1697,8 @@ do_standby_clone(void)
|
||||
char master_data_directory[MAXPGPATH];
|
||||
char local_data_directory[MAXPGPATH];
|
||||
|
||||
char local_repmgr_directory[MAXPGPATH];
|
||||
|
||||
char master_config_file[MAXPGPATH] = "";
|
||||
char local_config_file[MAXPGPATH] = "";
|
||||
bool config_file_outside_pgdata = false;
|
||||
@@ -1606,6 +1719,15 @@ do_standby_clone(void)
|
||||
|
||||
PQExpBufferData event_details;
|
||||
|
||||
/*
|
||||
* Detecting the appropriate mode
|
||||
*/
|
||||
if (runtime_options.rsync_only)
|
||||
mode = rsync;
|
||||
else if (strcmp(options.barman_server, "") != 0 && ! runtime_options.without_barman)
|
||||
mode = barman;
|
||||
else
|
||||
mode = pg_basebackup;
|
||||
|
||||
/*
|
||||
* If dest_dir (-D/--pgdata) was provided, this will become the new data
|
||||
@@ -1619,6 +1741,9 @@ do_standby_clone(void)
|
||||
runtime_options.dest_dir);
|
||||
}
|
||||
|
||||
if (mode != barman)
|
||||
{
|
||||
|
||||
param_set("application_name", options.node_name);
|
||||
|
||||
/* Connect to check configuration */
|
||||
@@ -1799,6 +1924,8 @@ do_standby_clone(void)
|
||||
|
||||
PQclear(res);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* target directory (-D/--pgdata) provided - use that as new data directory
|
||||
* (useful when executing backup on local machine only or creating the backup
|
||||
@@ -1811,6 +1938,12 @@ do_standby_clone(void)
|
||||
strncpy(local_hba_file, runtime_options.dest_dir, MAXPGPATH);
|
||||
strncpy(local_ident_file, runtime_options.dest_dir, MAXPGPATH);
|
||||
}
|
||||
else if (mode == barman)
|
||||
{
|
||||
log_err(_("Barman mode requires a destination directory\n"));
|
||||
log_hint(_("use -D/--data-dir to explicitly specify a data directory\n"));
|
||||
exit(ERR_BAD_CONFIG);
|
||||
}
|
||||
/*
|
||||
* Otherwise use the same data directory as on the remote host
|
||||
*/
|
||||
@@ -1826,9 +1959,9 @@ do_standby_clone(void)
|
||||
}
|
||||
|
||||
/*
|
||||
* When using rsync only, we need to check the SSH connection early
|
||||
* In rsync mode, we need to check the SSH connection early
|
||||
*/
|
||||
if (runtime_options.rsync_only)
|
||||
if (mode == rsync)
|
||||
{
|
||||
r = test_ssh_connection(runtime_options.host, runtime_options.remote_user);
|
||||
if (r != 0)
|
||||
@@ -1856,8 +1989,11 @@ do_standby_clone(void)
|
||||
* If replication slots requested, create appropriate slot on
|
||||
* the primary; this must be done before pg_start_backup() is
|
||||
* issued, either by us or by pg_basebackup.
|
||||
*
|
||||
* Replication slots are not supported (and not very useful
|
||||
* anyway) in Barman mode.
|
||||
*/
|
||||
if (options.use_replication_slots)
|
||||
if (mode != barman && options.use_replication_slots)
|
||||
{
|
||||
if (create_replication_slot(upstream_conn, repmgr_slot_name, server_version_num) == false)
|
||||
{
|
||||
@@ -1866,24 +2002,324 @@ do_standby_clone(void)
|
||||
}
|
||||
}
|
||||
|
||||
if (runtime_options.rsync_only)
|
||||
if (mode == rsync)
|
||||
{
|
||||
log_notice(_("starting backup (using rsync)...\n"));
|
||||
}
|
||||
else
|
||||
else if (mode == barman)
|
||||
{
|
||||
log_notice(_("getting backup from Barman...\n"));
|
||||
}
|
||||
else if (mode == pg_basebackup)
|
||||
{
|
||||
log_notice(_("starting backup (using pg_basebackup)...\n"));
|
||||
if (runtime_options.fast_checkpoint == false)
|
||||
log_hint(_("this may take some time; consider using the -c/--fast-checkpoint option\n"));
|
||||
}
|
||||
|
||||
if (runtime_options.rsync_only)
|
||||
if (mode == barman || mode == rsync)
|
||||
{
|
||||
PQExpBufferData tablespace_map;
|
||||
bool tablespace_map_rewrite = false;
|
||||
char command[MAXLEN];
|
||||
char filename[MAXLEN];
|
||||
char buf[MAXLEN];
|
||||
char backup_directory[MAXLEN];
|
||||
char backup_id[MAXLEN] = "";
|
||||
char datadir_list_filename[MAXLEN];
|
||||
char *p, *q;
|
||||
PQExpBufferData command_output;
|
||||
TablespaceDataList tablespace_list = { NULL, NULL };
|
||||
TablespaceDataListCell *cell_t;
|
||||
|
||||
PQExpBufferData tablespace_map;
|
||||
bool tablespace_map_rewrite = false;
|
||||
|
||||
if (mode == barman)
|
||||
{
|
||||
bool command_ok;
|
||||
/*
|
||||
* Check that there is at least one valid backup
|
||||
*/
|
||||
|
||||
maxlen_snprintf(command, "ssh %s barman show-backup %s latest > /dev/null",
|
||||
options.barman_server,
|
||||
options.cluster_name);
|
||||
command_ok = local_command(command, NULL);
|
||||
if (command_ok == false)
|
||||
{
|
||||
log_err(_("No valid backup for server %s was found in the Barman catalogue\n"),
|
||||
options.barman_server);
|
||||
log_hint(_("Refer to the Barman documentation for more information\n"));
|
||||
exit(ERR_INTERNAL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Locate Barman's backup directory
|
||||
*/
|
||||
|
||||
maxlen_snprintf(command, "ssh %s barman show-server %s | grep 'backup_directory'",
|
||||
options.barman_server,
|
||||
options.cluster_name);
|
||||
|
||||
initPQExpBuffer(&command_output);
|
||||
(void)local_command(
|
||||
command,
|
||||
&command_output);
|
||||
|
||||
p = string_skip_prefix("\tbackup_directory: ", command_output.data);
|
||||
if (p == NULL)
|
||||
{
|
||||
log_err("Unexpected output from Barman: %s\n",
|
||||
command_output.data);
|
||||
exit(ERR_INTERNAL);
|
||||
}
|
||||
|
||||
strncpy(backup_directory, p, MAXLEN);
|
||||
string_remove_trailing_newlines(backup_directory);
|
||||
|
||||
termPQExpBuffer(&command_output);
|
||||
|
||||
/*
|
||||
* Create the local repmgr subdirectory
|
||||
*/
|
||||
|
||||
maxlen_snprintf(local_repmgr_directory, "%s/repmgr", local_data_directory );
|
||||
maxlen_snprintf(datadir_list_filename, "%s/data.txt", local_repmgr_directory);
|
||||
|
||||
if (!create_pg_dir(local_repmgr_directory, runtime_options.force))
|
||||
{
|
||||
log_err(_("unable to use directory %s ...\n"),
|
||||
local_repmgr_directory);
|
||||
log_hint(_("use -F/--force option to force this directory to be overwritten\n"));
|
||||
r = ERR_BAD_CONFIG;
|
||||
retval = ERR_BAD_CONFIG;
|
||||
goto stop_backup;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read the list of backup files into a local file. In the
|
||||
* process:
|
||||
*
|
||||
* - determine the backup ID;
|
||||
* - check, and remove, the prefix;
|
||||
* - detect tablespaces;
|
||||
* - filter files in one list per tablespace;
|
||||
*/
|
||||
|
||||
{
|
||||
FILE *fi; /* input stream */
|
||||
FILE *fd; /* output for data.txt */
|
||||
char prefix[MAXLEN];
|
||||
char output[MAXLEN];
|
||||
int n;
|
||||
|
||||
maxlen_snprintf(command, "ssh %s barman list-files --target=data %s latest",
|
||||
options.barman_server,
|
||||
options.cluster_name);
|
||||
|
||||
fi = popen(command, "r");
|
||||
if (fi == NULL)
|
||||
{
|
||||
log_err("Cannot launch command: %s\n", command);
|
||||
exit(ERR_INTERNAL);
|
||||
}
|
||||
|
||||
fd = fopen(datadir_list_filename, "w");
|
||||
if (fd == NULL)
|
||||
{
|
||||
log_err("Cannot open file: %s\n", datadir_list_filename);
|
||||
exit(ERR_INTERNAL);
|
||||
}
|
||||
|
||||
maxlen_snprintf(prefix, "%s/base/", backup_directory);
|
||||
while (fgets(output, MAXLEN, fi) != NULL)
|
||||
{
|
||||
/*
|
||||
* Remove prefix
|
||||
*/
|
||||
p = string_skip_prefix(prefix, output);
|
||||
if (p == NULL)
|
||||
{
|
||||
log_err("Unexpected output from \"barman list-files\": %s\n",
|
||||
output);
|
||||
exit(ERR_INTERNAL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove and note backup ID; copy backup.info
|
||||
*/
|
||||
if (! strcmp(backup_id, ""))
|
||||
{
|
||||
FILE *fi2;
|
||||
|
||||
n = strcspn(p, "/");
|
||||
|
||||
strncpy(backup_id, p, n);
|
||||
|
||||
strncat(prefix,backup_id,MAXLEN-1);
|
||||
strncat(prefix,"/",MAXLEN-1);
|
||||
p = string_skip_prefix(backup_id, p);
|
||||
p = string_skip_prefix("/", p);
|
||||
|
||||
/*
|
||||
* Copy backup.info
|
||||
*/
|
||||
maxlen_snprintf(command,
|
||||
"rsync -a %s:%s/base/%s/backup.info %s",
|
||||
options.barman_server,
|
||||
backup_directory,
|
||||
backup_id,
|
||||
local_repmgr_directory);
|
||||
(void)local_command(
|
||||
command,
|
||||
&command_output);
|
||||
|
||||
/*
|
||||
* Get tablespace data
|
||||
*/
|
||||
maxlen_snprintf(filename, "%s/backup.info",
|
||||
local_repmgr_directory);
|
||||
fi2 = fopen(filename, "r");
|
||||
if (fi2 == NULL)
|
||||
{
|
||||
log_err("Cannot open file: %s\n", filename);
|
||||
exit(ERR_INTERNAL);
|
||||
}
|
||||
while (fgets(buf, MAXLEN, fi2) != NULL)
|
||||
{
|
||||
q = string_skip_prefix("tablespaces=", buf);
|
||||
if (q != NULL)
|
||||
{
|
||||
get_tablespace_data_barman
|
||||
(q, &tablespace_list);
|
||||
}
|
||||
q = string_skip_prefix("version=", buf);
|
||||
if (q != NULL)
|
||||
{
|
||||
server_version_num = strtol(q, NULL, 10);
|
||||
}
|
||||
}
|
||||
fclose(fi2);
|
||||
unlink(filename);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Skip backup.info
|
||||
*/
|
||||
if (string_skip_prefix("backup.info", p))
|
||||
continue;
|
||||
|
||||
/*
|
||||
* Filter data directory files
|
||||
*/
|
||||
if ((q = string_skip_prefix("data/", p)) != NULL)
|
||||
{
|
||||
fputs(q, fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Filter other files (i.e. tablespaces)
|
||||
*/
|
||||
for (cell_t = tablespace_list.head; cell_t; cell_t = cell_t->next)
|
||||
{
|
||||
if ((q = string_skip_prefix(cell_t->oid, p)) != NULL && *q == '/')
|
||||
{
|
||||
if (cell_t->f == NULL)
|
||||
{
|
||||
maxlen_snprintf(filename, "%s/%s.txt", local_repmgr_directory, cell_t->oid);
|
||||
cell_t->f = fopen(filename, "w");
|
||||
if (cell_t->f == NULL)
|
||||
{
|
||||
log_err("Cannot open file: %s\n", filename);
|
||||
exit(ERR_INTERNAL);
|
||||
}
|
||||
}
|
||||
fputs(q + 1, cell_t->f);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fclose(fd);
|
||||
|
||||
pclose(fi);
|
||||
}
|
||||
|
||||
/* For 9.5 and greater, create our own tablespace_map file */
|
||||
if (server_version_num >= 90500)
|
||||
{
|
||||
initPQExpBuffer(&tablespace_map);
|
||||
}
|
||||
|
||||
/*
|
||||
* As of Barman version 1.6.1, the file structure of a backup
|
||||
* is as follows:
|
||||
*
|
||||
* base/ - base backup
|
||||
* wals/ - WAL files associated to the backup
|
||||
*
|
||||
* base/<ID> - backup files
|
||||
*
|
||||
* here ID has the standard timestamp form yyyymmddThhmmss
|
||||
*
|
||||
* base/<ID>/backup.info - backup metadata, in text format
|
||||
* base/<ID>/data - data directory
|
||||
* base/<ID>/<OID> - tablespace with the given oid
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copy all backup files from the Barman server
|
||||
*/
|
||||
|
||||
maxlen_snprintf(command,
|
||||
"rsync --progress -a --files-from=%s %s:%s/base/%s/data %s",
|
||||
datadir_list_filename,
|
||||
options.barman_server,
|
||||
backup_directory,
|
||||
backup_id,
|
||||
local_data_directory);
|
||||
(void)local_command(
|
||||
command,
|
||||
&command_output);
|
||||
unlink(datadir_list_filename);
|
||||
|
||||
/*
|
||||
* We must create some PGDATA subdirectories because they are
|
||||
* not included in the Barman backup.
|
||||
*/
|
||||
{
|
||||
const char* const dirs[] = {
|
||||
/* Only from 9.5 */
|
||||
"pg_commit_ts",
|
||||
/* Only from 9.4 */
|
||||
"pg_dynshmem", "pg_logical",
|
||||
/* Already in 9.3 */
|
||||
"pg_serial", "pg_snapshots", "pg_stat", "pg_stat_tmp", "pg_tblspc",
|
||||
"pg_twophase", "pg_xlog", 0
|
||||
};
|
||||
const int vers[] = {
|
||||
90500,
|
||||
90400, 90400,
|
||||
0, 0, 0, 0, 0,
|
||||
0, 0, 0
|
||||
};
|
||||
for (i = 0; dirs[i]; i++)
|
||||
{
|
||||
if (vers[i] > 0 && server_version_num < vers[i])
|
||||
continue;
|
||||
maxlen_snprintf(filename, "%s/%s", local_data_directory, dirs[i]);
|
||||
if(mkdir(filename, S_IRWXU) != 0 && errno != EEXIST)
|
||||
{
|
||||
log_err(_("unable to create the %s directory\n"), dirs[i]);
|
||||
exit(ERR_INTERNAL);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (mode == rsync)
|
||||
{
|
||||
/* For 9.5 and greater, create our own tablespace_map file */
|
||||
if (server_version_num >= 90500)
|
||||
{
|
||||
@@ -1952,6 +2388,7 @@ do_standby_clone(void)
|
||||
/* Copy tablespaces and, if required, remap to a new location */
|
||||
retval = get_tablespace_data(upstream_conn, &tablespace_list);
|
||||
if(retval != SUCCESS) goto stop_backup;
|
||||
}
|
||||
|
||||
for (cell_t = tablespace_list.head; cell_t; cell_t = cell_t->next)
|
||||
{
|
||||
@@ -1983,7 +2420,38 @@ do_standby_clone(void)
|
||||
tblspc_dir_dest = cell_t->location;
|
||||
}
|
||||
|
||||
/*
|
||||
* Tablespace file copy
|
||||
*/
|
||||
|
||||
if (mode == barman)
|
||||
{
|
||||
create_pg_dir(cell_t->location, false);
|
||||
|
||||
if (cell_t->f != NULL) /* cell_t->f == NULL iff the tablespace is empty */
|
||||
{
|
||||
maxlen_snprintf(command,
|
||||
"rsync --progress -a --files-from=%s/%s.txt %s:%s/base/%s/%s %s",
|
||||
local_repmgr_directory,
|
||||
cell_t->oid,
|
||||
options.barman_server,
|
||||
backup_directory,
|
||||
backup_id,
|
||||
cell_t->oid,
|
||||
tblspc_dir_dest);
|
||||
(void)local_command(
|
||||
command,
|
||||
&command_output);
|
||||
fclose(cell_t->f);
|
||||
maxlen_snprintf(filename,
|
||||
"%s/%s.txt",
|
||||
local_repmgr_directory,
|
||||
cell_t->oid);
|
||||
unlink(filename);
|
||||
}
|
||||
}
|
||||
else if (mode == rsync)
|
||||
{
|
||||
/* Copy tablespace directory */
|
||||
r = copy_remote_files(runtime_options.host, runtime_options.remote_user,
|
||||
cell_t->location, tblspc_dir_dest,
|
||||
@@ -2001,6 +2469,7 @@ do_standby_clone(void)
|
||||
cell_t->location);
|
||||
goto stop_backup;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If a valid mapping was provide for this tablespace, arrange for it to
|
||||
@@ -2008,7 +2477,7 @@ do_standby_clone(void)
|
||||
* (if no tablespace mappings was provided, the link will be copied as-is
|
||||
* by pg_basebackup or rsync and no action is required)
|
||||
*/
|
||||
if (mapping_found == true)
|
||||
if (mapping_found == true || mode == barman)
|
||||
{
|
||||
/* 9.5 and later - append to the tablespace_map file */
|
||||
if (server_version_num >= 90500)
|
||||
@@ -2116,7 +2585,9 @@ do_standby_clone(void)
|
||||
* standby server as on the primary?
|
||||
*/
|
||||
|
||||
if (external_config_file_copy_required && !runtime_options.ignore_external_config_files)
|
||||
if (mode != barman &&
|
||||
external_config_file_copy_required &&
|
||||
!runtime_options.ignore_external_config_files)
|
||||
{
|
||||
log_notice(_("copying configuration files from master\n"));
|
||||
r = test_ssh_connection(runtime_options.host, runtime_options.remote_user);
|
||||
@@ -2175,7 +2646,7 @@ do_standby_clone(void)
|
||||
* When using rsync, copy pg_control file last, emulating the base backup
|
||||
* protocol.
|
||||
*/
|
||||
if (runtime_options.rsync_only)
|
||||
if (mode == rsync)
|
||||
{
|
||||
maxlen_snprintf(local_control_file, "%s/global", local_data_directory);
|
||||
|
||||
@@ -2207,7 +2678,7 @@ do_standby_clone(void)
|
||||
|
||||
stop_backup:
|
||||
|
||||
if (runtime_options.rsync_only && pg_start_backup_executed)
|
||||
if (mode == rsync && pg_start_backup_executed)
|
||||
{
|
||||
log_notice(_("notifying master about backup completion...\n"));
|
||||
if (stop_backup(upstream_conn, last_wal_segment) == false)
|
||||
@@ -2239,7 +2710,7 @@ stop_backup:
|
||||
* files which won't be removed by rsync and which could
|
||||
* be stale or are otherwise not required
|
||||
*/
|
||||
if (runtime_options.rsync_only)
|
||||
if (mode == rsync)
|
||||
{
|
||||
char label_path[MAXPGPATH];
|
||||
char dirpath[MAXLEN] = "";
|
||||
@@ -2303,13 +2774,18 @@ stop_backup:
|
||||
/* Finally, write the recovery.conf file */
|
||||
create_recovery_file(local_data_directory, upstream_conn);
|
||||
|
||||
if (runtime_options.rsync_only)
|
||||
/* In Barman mode, remove local_repmgr_directory */
|
||||
if (mode == barman)
|
||||
rmdir(local_repmgr_directory);
|
||||
|
||||
switch(mode)
|
||||
{
|
||||
case rsync:
|
||||
log_notice(_("standby clone (using rsync) complete\n"));
|
||||
}
|
||||
else
|
||||
{
|
||||
case pg_basebackup:
|
||||
log_notice(_("standby clone (using pg_basebackup) complete\n"));
|
||||
case barman:
|
||||
log_notice(_("standby clone (from Barman) complete\n"));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -4544,6 +5020,7 @@ do_help(void)
|
||||
printf(_("Command-specific configuration options:\n"));
|
||||
printf(_(" -c, --fast-checkpoint (standby clone) force fast checkpoint\n"));
|
||||
printf(_(" -r, --rsync-only (standby clone) use only rsync, not pg_basebackup\n"));
|
||||
printf(_(" --without-barman (standby clone) do not use Barman even if configured\n"));
|
||||
printf(_(" --recovery-min-apply-delay=VALUE (standby clone, follow) set recovery_min_apply_delay\n" \
|
||||
" in recovery.conf (PostgreSQL 9.4 and later)\n"));
|
||||
printf(_(" --ignore-external-config-files (standby clone) don't copy configuration files located\n" \
|
||||
@@ -5707,10 +6184,10 @@ check_upstream_config(PGconn *conn, int server_version_num, bool exit_on_error)
|
||||
|
||||
}
|
||||
/*
|
||||
* physical replication slots not available or not requested -
|
||||
* physical replication slots not available or not requested, and Barman mode not used -
|
||||
* ensure some reasonably high value set for `wal_keep_segments`
|
||||
*/
|
||||
else
|
||||
else if (! runtime_options.without_barman && strcmp(options.barman_server, "") == 0)
|
||||
{
|
||||
i = guc_set_typed(conn, "wal_keep_segments", ">=",
|
||||
runtime_options.wal_keep_segments, "integer");
|
||||
@@ -6005,33 +6482,43 @@ remote_command(const char *host, const char *user, const char *command, PQExpBuf
|
||||
|
||||
|
||||
/*
|
||||
* Execute a command locally.
|
||||
* Execute a command locally. If outputbuf == NULL, discard the
|
||||
* output.
|
||||
*/
|
||||
static bool
|
||||
local_command(const char *command, PQExpBufferData *outputbuf)
|
||||
{
|
||||
FILE *fp;
|
||||
char output[MAXLEN];
|
||||
int retval;
|
||||
|
||||
fp = popen(command, "r");
|
||||
|
||||
if (fp == NULL)
|
||||
if (outputbuf == NULL)
|
||||
{
|
||||
log_err(_("unable to execute local command:\n%s\n"), command);
|
||||
return false;
|
||||
retval = system(command);
|
||||
return (retval == 0) ? true : false;
|
||||
}
|
||||
|
||||
/* TODO: better error handling */
|
||||
while (fgets(output, MAXLEN, fp) != NULL)
|
||||
else
|
||||
{
|
||||
appendPQExpBuffer(outputbuf, "%s", output);
|
||||
fp = popen(command, "r");
|
||||
|
||||
if (fp == NULL)
|
||||
{
|
||||
log_err(_("unable to execute local command:\n%s\n"), command);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* TODO: better error handling */
|
||||
while (fgets(output, MAXLEN, fp) != NULL)
|
||||
{
|
||||
appendPQExpBuffer(outputbuf, "%s", output);
|
||||
}
|
||||
|
||||
pclose(fp);
|
||||
|
||||
log_verbose(LOG_DEBUG, "local_command(): output returned was:\n%s", outputbuf->data);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
pclose(fp);
|
||||
|
||||
log_verbose(LOG_DEBUG, "local_command(): output returned was:\n%s", outputbuf->data);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
4
repmgr.h
4
repmgr.h
@@ -56,6 +56,7 @@
|
||||
#define OPT_PWPROMPT 7
|
||||
#define OPT_CSV 8
|
||||
#define OPT_NODE 9
|
||||
#define OPT_WITHOUT_BARMAN 10
|
||||
|
||||
|
||||
/* Run time options type */
|
||||
@@ -79,6 +80,7 @@ typedef struct
|
||||
bool fast_checkpoint;
|
||||
bool ignore_external_config_files;
|
||||
bool csv_mode;
|
||||
bool without_barman;
|
||||
char masterport[MAXLEN];
|
||||
/*
|
||||
* configuration file parameters which can be overridden on the
|
||||
@@ -102,7 +104,7 @@ typedef struct
|
||||
char recovery_min_apply_delay[MAXLEN];
|
||||
} t_runtime_options;
|
||||
|
||||
#define T_RUNTIME_OPTIONS_INITIALIZER { "", "", "", "", "", "", "", DEFAULT_WAL_KEEP_SEGMENTS, false, false, false, false, false, false, false, false, false, false, "", "", "", "", "fast", "", 0, 0, "", ""}
|
||||
#define T_RUNTIME_OPTIONS_INITIALIZER { "", "", "", "", "", "", "", DEFAULT_WAL_KEEP_SEGMENTS, false, false, false, false, false, false, false, false, false, false, false, "", "", "", "", "fast", "", 0, 0, "", ""}
|
||||
|
||||
struct BackupLabel
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user