Add "repmgr node archive-config"

This commit is contained in:
Ian Barwick
2017-08-01 17:38:54 +09:00
parent 3683d096f1
commit f023b9c90c
9 changed files with 281 additions and 39 deletions

View File

@@ -2078,6 +2078,7 @@ delete_node_record(PGconn *conn, int node)
return true; return true;
} }
void void
get_node_replication_stats(PGconn *conn, t_node_info *node_info) get_node_replication_stats(PGconn *conn, t_node_info *node_info)
{ {
@@ -2099,8 +2100,8 @@ get_node_replication_stats(PGconn *conn, t_node_info *node_info)
if (PQresultStatus(res) != PGRES_TUPLES_OK) if (PQresultStatus(res) != PGRES_TUPLES_OK)
{ {
log_warning(_("unable to retrieve node replication statistics:\n %s"), log_warning(_("unable to retrieve node replication statistics"));
PQerrorMessage(conn)); log_detail("%s", PQerrorMessage(conn));
PQclear(res); PQclear(res);
return; return;
} }
@@ -2153,6 +2154,65 @@ clear_node_info_list(NodeInfoList *nodes)
} }
/* ================================================ */
/* PostgreSQL configuration file location functions */
/* ================================================ */
bool
get_datadir_configuration_files(PGconn *conn, KeyValueList *list)
{
PQExpBufferData query;
PGresult *res;
int i;
initPQExpBuffer(&query);
appendPQExpBuffer(
&query,
"WITH files AS ( "
" WITH dd AS ( "
" SELECT setting "
" FROM pg_catalog.pg_settings "
" WHERE name = 'data_directory') "
" SELECT distinct(sourcefile) AS config_file"
" FROM dd, pg_catalog.pg_settings ps "
" WHERE ps.sourcefile IS NOT NULL "
" AND ps.sourcefile ~ ('^' || dd.setting) "
" UNION "
" SELECT ps.setting AS config_file"
" FROM dd, pg_catalog.pg_settings ps "
" WHERE ps.name IN ( 'config_file', 'hba_file', 'ident_file') "
" AND ps.setting ~ ('^' || dd.setting) "
") "
" SELECT config_file, "
" regexp_replace(config_file, '^.*\\/','') AS filename "
" FROM files "
"ORDER BY config_file");
res = PQexec(conn, query.data);
termPQExpBuffer(&query);
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
log_error(_("unable to retrieve configuration file information"));
log_detail("%s", PQerrorMessage(conn));
PQclear(res);
return false;
}
for (i = 0; i < PQntuples(res); i++)
{
key_value_list_set(
list,
PQgetvalue(res, i, 1),
PQgetvalue(res, i, 0));
}
PQclear(res);
return true;
}
/* ====================== */ /* ====================== */
/* event record functions */ /* event record functions */
/* ====================== */ /* ====================== */
@@ -2174,6 +2234,8 @@ create_event_record(PGconn *conn, t_configuration_options *options, int node_id,
return _create_event(conn, options, node_id, event, successful, details, &event_info, false); return _create_event(conn, options, node_id, event, successful, details, &event_info, false);
} }
/* /*
* create_event_notification() * create_event_notification()
* *

View File

@@ -338,6 +338,10 @@ void clear_node_info_list(NodeInfoList *nodes);
void get_node_replication_stats(PGconn *conn, t_node_info *node_info); void get_node_replication_stats(PGconn *conn, t_node_info *node_info);
/* PostgreSQL configuration file location functions */
bool get_datadir_configuration_files(PGconn *conn, KeyValueList *list);
/* event functions */ /* event functions */
bool create_event_record(PGconn *conn, t_configuration_options *options, int node_id, char *event, bool successful, char *details); bool create_event_record(PGconn *conn, t_configuration_options *options, int node_id, char *event, bool successful, char *details);
bool create_event_notification(PGconn *conn, t_configuration_options *options, int node_id, char *event, bool successful, char *details); bool create_event_notification(PGconn *conn, t_configuration_options *options, int node_id, char *event, bool successful, char *details);

View File

@@ -6,12 +6,16 @@
* Copyright (c) 2ndQuadrant, 2010-2017 * Copyright (c) 2ndQuadrant, 2010-2017
*/ */
#include <sys/stat.h>
#include <dirent.h>
#include "repmgr.h" #include "repmgr.h"
#include "repmgr-client-global.h" #include "repmgr-client-global.h"
#include "repmgr-action-node.h" #include "repmgr-action-node.h"
static bool copy_file(const char *src_file, const char *dest_file);
void void
do_node_status(void) do_node_status(void)
{ {
@@ -275,3 +279,175 @@ void
do_node_check(void) do_node_check(void)
{ {
} }
/*
* Intended mainly for "internal" use by `standby switchover`, which
* calls this on the target server to archive any configuration files
* in the data directory, which may be overwritten by an operation
* like pg_rewind
*
* Requires configuration file.
*/
void
do_node_archive_config(void)
{
char archive_dir[MAXPGPATH];
struct stat statbuf;
struct dirent *arcdir_ent;
DIR *arcdir;
PGconn *local_conn = NULL;
KeyValueList config_files = { NULL, NULL };
KeyValueListCell *cell;
int copied_count = 0;
snprintf(archive_dir,
MAXPGPATH,
"%s/repmgr-config-archive-%s",
runtime_options.config_archive_dir,
config_file_options.node_name);
log_verbose(LOG_DEBUG, "using archive directory \"%s\"", archive_dir);
/* sanity-check directory path */
if (stat(archive_dir, &statbuf) == -1)
{
if (errno != ENOENT)
{
log_error(_("error encountered when checking archive directory \"%s\""),
archive_dir);
log_detail("%s",strerror(errno));
exit(ERR_BAD_CONFIG);
}
/* attempt to create and open the directory */
if (mkdir(archive_dir, S_IRWXU) != 0 && errno != EEXIST)
{
log_error(_("unable to create temporary archive directory \"%s\""),
archive_dir);
log_detail("%s", strerror(errno));
exit(ERR_BAD_CONFIG);
}
}
else if(!S_ISDIR(statbuf.st_mode))
{
log_error(_("\"%s\" exists but is not a directory"),
archive_dir);
exit(ERR_BAD_CONFIG);
}
arcdir = opendir(archive_dir);
if (arcdir == NULL)
{
log_error(_("unable to open archive directory \"%s\""),
archive_dir);
log_detail("%s", strerror(errno));
exit(ERR_BAD_CONFIG);
}
/*
* attempt to remove any existing files in the directory
* TODO: collate problem files into list
*/
while ((arcdir_ent = readdir(arcdir)) != NULL)
{
char arcdir_ent_path[MAXPGPATH];
snprintf(arcdir_ent_path, MAXPGPATH,
"%s/%s",
archive_dir,
arcdir_ent->d_name);
if (stat(arcdir_ent_path, &statbuf) == 0 && !S_ISREG(statbuf.st_mode))
{
continue;
}
if (unlink(arcdir_ent_path) == -1)
{
log_error(_("unable to create temporary archive directory \"%s\""),
archive_dir);
log_detail("%s", strerror(errno));
closedir(arcdir);
exit(ERR_BAD_CONFIG);
}
}
closedir(arcdir);
local_conn = establish_db_connection(config_file_options.conninfo, true);
get_datadir_configuration_files(local_conn, &config_files);
for (cell = config_files.head; cell; cell = cell->next)
{
char dest_file[MAXPGPATH] = "";
snprintf(dest_file, MAXPGPATH,
"%s/%s",
archive_dir,
cell->key);
copy_file(cell->value, dest_file);
copied_count++;
}
PQfinish(local_conn);
log_verbose(LOG_INFO, _("%i files copied to %s"),
copied_count, archive_dir);
}
void
do_node_restore_config(void)
{
return;
}
static bool
copy_file(const char *src_file, const char *dest_file)
{
FILE *ptr_old, *ptr_new;
int a;
ptr_old = fopen(src_file, "r");
ptr_new = fopen(dest_file, "w");
if (ptr_old == NULL)
return false;
if (ptr_new == NULL)
{
fclose(ptr_old);
return false;
}
chmod(dest_file, S_IRUSR | S_IWUSR);
while(1)
{
a = fgetc(ptr_old);
if (!feof(ptr_old))
{
fputc(a, ptr_new);
}
else
{
break;
}
}
fclose(ptr_new);
fclose(ptr_old);
return true;
}

View File

@@ -8,5 +8,7 @@
extern void do_node_status(void); extern void do_node_status(void);
extern void do_node_check(void); extern void do_node_check(void);
extern void do_node_archive_config(void);
extern void do_node_restore_config(void);
#endif /* _REPMGR_ACTION_NODE_H_ */ #endif /* _REPMGR_ACTION_NODE_H_ */

View File

@@ -1569,19 +1569,6 @@ do_standby_switchover(void)
} }
void
do_standby_archive_config(void)
{
return;
}
void
do_standby_restore_config(void)
{
return;
}
static void static void
check_source_server() check_source_server()
{ {

View File

@@ -12,8 +12,6 @@ extern void do_standby_unregister(void);
extern void do_standby_promote(void); extern void do_standby_promote(void);
extern void do_standby_follow(void); extern void do_standby_follow(void);
extern void do_standby_switchover(void); extern void do_standby_switchover(void);
extern void do_standby_archive_config(void);
extern void do_standby_restore_config(void);
typedef struct typedef struct
{ {

View File

@@ -78,6 +78,8 @@ typedef struct
char event[MAXLEN]; char event[MAXLEN];
int limit; int limit;
/* following options for internal use */
char config_archive_dir[MAXPGPATH];
} t_runtime_options; } t_runtime_options;
#define T_RUNTIME_OPTIONS_INITIALIZER { \ #define T_RUNTIME_OPTIONS_INITIALIZER { \
@@ -100,7 +102,9 @@ typedef struct
/* standby register options */ \ /* standby register options */ \
false, 0, \ false, 0, \
/* event options */ \ /* event options */ \
false, "", CLUSTER_EVENT_LIMIT } false, "", CLUSTER_EVENT_LIMIT, \
"/tmp" \
}
typedef enum { typedef enum {

View File

@@ -23,6 +23,12 @@
* CLUSTER EVENT * CLUSTER EVENT
* CLUSTER CROSSCHECK * CLUSTER CROSSCHECK
* CLUSTER MATRIX * CLUSTER MATRIX
*
* NODE STATUS
*
* For internal use:
* NODE ARCHIVE-CONFIG
* NODE RESTORE-CONFIG
*/ */
#include <unistd.h> #include <unistd.h>
@@ -450,6 +456,11 @@ main(int argc, char **argv)
runtime_options.csv = true; runtime_options.csv = true;
break; break;
/* internal options */
case OPT_CONFIG_ARCHIVE_DIR:
/* TODO: check this is an absolute path */
strncpy(runtime_options.config_archive_dir, optarg, MAXPGPATH);
break;
/* options deprecated since 3.3 * /* options deprecated since 3.3 *
* ---------------------------- */ * ---------------------------- */
@@ -615,10 +626,6 @@ main(int argc, char **argv)
action = STANDBY_FOLLOW; action = STANDBY_FOLLOW;
else if (strcasecmp(repmgr_action, "SWITCHOVER") == 0) else if (strcasecmp(repmgr_action, "SWITCHOVER") == 0)
action = STANDBY_SWITCHOVER; action = STANDBY_SWITCHOVER;
else if (strcasecmp(repmgr_action, "ARCHIVE-CONFIG") == 0)
action = STANDBY_ARCHIVE_CONFIG;
else if (strcasecmp(repmgr_action, "RESTORE-CONFIG") == 0)
action = STANDBY_RESTORE_CONFIG;
else if (strcasecmp(repmgr_action, "CHECK") == 0) else if (strcasecmp(repmgr_action, "CHECK") == 0)
action = NODE_CHECK; action = NODE_CHECK;
else if (strcasecmp(repmgr_action, "STATUS") == 0) else if (strcasecmp(repmgr_action, "STATUS") == 0)
@@ -645,6 +652,10 @@ main(int argc, char **argv)
action = NODE_CHECK; action = NODE_CHECK;
else if (strcasecmp(repmgr_action, "STATUS") == 0) else if (strcasecmp(repmgr_action, "STATUS") == 0)
action = NODE_STATUS; action = NODE_STATUS;
else if (strcasecmp(repmgr_action, "ARCHIVE-CONFIG") == 0)
action = NODE_ARCHIVE_CONFIG;
else if (strcasecmp(repmgr_action, "RESTORE-CONFIG") == 0)
action = NODE_RESTORE_CONFIG;
} }
else if (strcasecmp(repmgr_node_type, "CLUSTER") == 0) else if (strcasecmp(repmgr_node_type, "CLUSTER") == 0)
@@ -934,11 +945,7 @@ main(int argc, char **argv)
case STANDBY_SWITCHOVER: case STANDBY_SWITCHOVER:
do_standby_switchover(); do_standby_switchover();
break; break;
case STANDBY_ARCHIVE_CONFIG:
do_standby_archive_config();
break;
case STANDBY_RESTORE_CONFIG:
do_standby_restore_config();
break; break;
#else #else
/* we won't ever reach here, but stop the compiler complaining */ /* we won't ever reach here, but stop the compiler complaining */
@@ -950,8 +957,6 @@ main(int argc, char **argv)
case STANDBY_PROMOTE: case STANDBY_PROMOTE:
case STANDBY_FOLLOW: case STANDBY_FOLLOW:
case STANDBY_SWITCHOVER: case STANDBY_SWITCHOVER:
case STANDBY_ARCHIVE_CONFIG:
case STANDBY_RESTORE_CONFIG:
break; break;
#endif #endif
@@ -967,6 +972,11 @@ main(int argc, char **argv)
case NODE_STATUS: case NODE_STATUS:
do_node_status(); do_node_status();
break; break;
case NODE_ARCHIVE_CONFIG:
do_node_archive_config();
break;
case NODE_RESTORE_CONFIG:
do_node_restore_config();
/* CLUSTER */ /* CLUSTER */
case CLUSTER_SHOW: case CLUSTER_SHOW:
@@ -1135,7 +1145,6 @@ check_cli_parameters(const int action)
{ {
case STANDBY_CLONE: case STANDBY_CLONE:
case STANDBY_FOLLOW: case STANDBY_FOLLOW:
case STANDBY_RESTORE_CONFIG:
break; break;
default: default:
item_list_append_format(&cli_warnings, item_list_append_format(&cli_warnings,

View File

@@ -20,12 +20,12 @@
#define STANDBY_PROMOTE 6 #define STANDBY_PROMOTE 6
#define STANDBY_FOLLOW 7 #define STANDBY_FOLLOW 7
#define STANDBY_SWITCHOVER 8 #define STANDBY_SWITCHOVER 8
#define STANDBY_ARCHIVE_CONFIG 9 #define BDR_REGISTER 9
#define STANDBY_RESTORE_CONFIG 10 #define BDR_UNREGISTER 10
#define BDR_REGISTER 11 #define NODE_STATUS 11
#define BDR_UNREGISTER 12 #define NODE_CHECK 12
#define NODE_STATUS 13 #define NODE_ARCHIVE_CONFIG 13
#define NODE_CHECK 14 #define NODE_RESTORE_CONFIG 14
#define CLUSTER_SHOW 15 #define CLUSTER_SHOW 15
#define CLUSTER_CLEANUP 16 #define CLUSTER_CLEANUP 16
#define CLUSTER_MATRIX 17 #define CLUSTER_MATRIX 17
@@ -118,6 +118,9 @@ static struct option long_options[] =
{"event", required_argument, NULL, OPT_EVENT }, {"event", required_argument, NULL, OPT_EVENT },
{"limit", required_argument, NULL, OPT_LIMIT }, {"limit", required_argument, NULL, OPT_LIMIT },
/* Following options for internal use */
{"config-archive-dir", required_argument, NULL, OPT_CONFIG_ARCHIVE_DIR},
/* deprecated */ /* deprecated */
{"no-conninfo-password", no_argument, NULL, OPT_NO_CONNINFO_PASSWORD}, {"no-conninfo-password", no_argument, NULL, OPT_NO_CONNINFO_PASSWORD},
/* legacy alias for -D/--pgdata*/ /* legacy alias for -D/--pgdata*/
@@ -135,9 +138,6 @@ static struct option long_options[] =
{"without-barman", no_argument, NULL, OPT_WITHOUT_BARMAN}, {"without-barman", no_argument, NULL, OPT_WITHOUT_BARMAN},
{"copy-external-config-files", optional_argument, NULL, OPT_COPY_EXTERNAL_CONFIG_FILES}, {"copy-external-config-files", optional_argument, NULL, OPT_COPY_EXTERNAL_CONFIG_FILES},
{"wait-sync", optional_argument, NULL, OPT_REGISTER_WAIT}, {"wait-sync", optional_argument, NULL, OPT_REGISTER_WAIT},
/* Following options for internal use */
{"cluster", required_argument, NULL, OPT_CLUSTER},
{"config-archive-dir", required_argument, NULL, OPT_CONFIG_ARCHIVE_DIR},
{NULL, 0, NULL, 0} {NULL, 0, NULL, 0}
}; };