From 99e7bb0ea391829d1ac6ffd18ff4808a55f7de58 Mon Sep 17 00:00:00 2001 From: Ian Barwick Date: Thu, 27 Apr 2017 09:22:09 +0900 Subject: [PATCH] Initial code for standby clone --- Makefile.in | 2 +- repmgr-action-standby.c | 75 +++++++++++++++++++++++++++++++++++++++++ repmgr-action-standby.h | 12 +++++++ repmgr-client-global.h | 30 ++++++++++++++++- repmgr-client.c | 75 +++++++++++++++++++++++++++++++++++------ repmgr-client.h | 24 ++++--------- 6 files changed, 188 insertions(+), 30 deletions(-) create mode 100644 repmgr-action-standby.c create mode 100644 repmgr-action-standby.h diff --git a/Makefile.in b/Makefile.in index 04178428..27c1e507 100644 --- a/Makefile.in +++ b/Makefile.in @@ -26,7 +26,7 @@ include Makefile.global $(info Building against PostgreSQL $(MAJORVERSION)) -REPMGR_CLIENT_OBJS = repmgr-client.o repmgr-action-master.o repmgr-action-cluster.o \ +REPMGR_CLIENT_OBJS = repmgr-client.o repmgr-action-master.o repmgr-action-standby.o repmgr-action-cluster.o \ config.o log.o strutil.o dbutils.o REPMGRD_OBJS = repmgrd.o diff --git a/repmgr-action-standby.c b/repmgr-action-standby.c new file mode 100644 index 00000000..d4738415 --- /dev/null +++ b/repmgr-action-standby.c @@ -0,0 +1,75 @@ +/* + * repmgr-action-standby.c + * + * Implements standby actions for the repmgr command line utility + * + * Copyright (c) 2ndQuadrant, 2010-2017 + */ + +#include "repmgr.h" + +#include "repmgr-client-global.h" +#include "repmgr-action-standby.h" + + + +void +do_standby_clone(void) +{ + PGconn *primary_conn = NULL; + PGconn *source_conn = NULL; + PGresult *res; + + int server_version_num = -1; + char cluster_size[MAXLEN]; + + /* + * conninfo params for the actual upstream node (which might be different + * to the node we're cloning from) to write to recovery.conf + */ + t_conninfo_param_list recovery_conninfo; + char recovery_conninfo_str[MAXLEN]; + bool upstream_record_found = false; + int upstream_node_id = UNKNOWN_NODE_ID; + + + enum { + barman, + rsync, + pg_basebackup + } mode; + + /* used by barman mode */ + char datadir_list_filename[MAXLEN]; + char local_repmgr_tmp_directory[MAXPGPATH]; + + puts("standby clone"); + + + /* + * detecting the cloning mode + */ + if (runtime_options.rsync_only) + mode = rsync; + else if (strcmp(config_file_options.barman_server, "") != 0 && ! runtime_options.without_barman) + mode = barman; + else + mode = pg_basebackup; + + /* + * In rsync mode, we need to check the SSH connection early + */ + if (mode == rsync) + { + int r; + + r = test_ssh_connection(runtime_options.host, runtime_options.remote_user); + if (r != 0) + { + log_error(_("remote host %s is not reachable via SSH"), + runtime_options.host); + exit(ERR_BAD_SSH); + } + } + +} diff --git a/repmgr-action-standby.h b/repmgr-action-standby.h new file mode 100644 index 00000000..a1617234 --- /dev/null +++ b/repmgr-action-standby.h @@ -0,0 +1,12 @@ +/* + * repmgr-action-standby.h + * Copyright (c) 2ndQuadrant, 2010-2017 + */ + +#ifndef _REPMGR_ACTION_STANDBY_H_ +#define _REPMGR_ACTION_STANDBY_H_ + +extern void do_standby_clone(void); + + +#endif diff --git a/repmgr-client-global.h b/repmgr-client-global.h index b0dd0d48..95338279 100644 --- a/repmgr-client-global.h +++ b/repmgr-client-global.h @@ -28,6 +28,8 @@ typedef struct bool verbose; /* connection options */ + char host[MAXLEN]; + char remote_user[MAXLEN]; char superuser[MAXLEN]; /* node options */ @@ -35,6 +37,10 @@ typedef struct char node_name[MAXLEN]; char data_dir[MAXPGPATH]; + /* standby clone options */ + bool rsync_only; + bool without_barman; + /* event options */ char event[MAXLEN]; int limit; @@ -42,12 +48,32 @@ typedef struct } t_runtime_options; +#define T_RUNTIME_OPTIONS_INITIALIZER { \ + /* configuration metadata */ \ + false, false, false, false, \ + /* general configuration options */ \ + "", false, "", \ + /* logging options */ \ + "", false, false, false, \ + /* connection options */ \ + "", "", "", \ + /* node options */ \ + UNKNOWN_NODE_ID, "", "", \ + /* standby clone options */ \ + false, false, \ + /* event options */ \ + "", 20, false} + + + /* global configuration structures */ extern t_runtime_options runtime_options; extern t_configuration_options config_file_options; +t_conninfo_param_list source_conninfo; -extern bool config_file_required; + +extern bool config_file_required; extern char pg_bindir[MAXLEN]; extern char repmgr_slot_name[MAXLEN]; @@ -58,5 +84,7 @@ extern t_node_info target_node_info; extern int check_server_version(PGconn *conn, char *server_type, bool exit_on_error, char *server_version_string); extern bool create_repmgr_extension(PGconn *conn); +extern int test_ssh_connection(char *host, char *remote_user); + #endif diff --git a/repmgr-client.c b/repmgr-client.c index a7b865a1..67965d99 100644 --- a/repmgr-client.c +++ b/repmgr-client.c @@ -20,20 +20,30 @@ #include "repmgr.h" #include "repmgr-client.h" #include "repmgr-client-global.h" -#include "repmgr-action-cluster.h" #include "repmgr-action-master.h" +#include "repmgr-action-standby.h" +#include "repmgr-action-cluster.h" + + +/* globally available variables * + * ============================ */ t_runtime_options runtime_options = T_RUNTIME_OPTIONS_INITIALIZER; t_configuration_options config_file_options = T_CONFIGURATION_OPTIONS_INITIALIZER; +/* conninfo params for the node we're cloning from */ +t_conninfo_param_list source_conninfo; - -bool config_file_required = true; +bool config_file_required = true; char pg_bindir[MAXLEN] = ""; char repmgr_slot_name[MAXLEN] = ""; -char *repmgr_slot_name_ptr = NULL; +char *repmgr_slot_name_ptr = NULL; +/* + * if --node-id/--node-name provided, place that node's record here + * for later use + */ t_node_info target_node_info = T_NODE_INFO_INITIALIZER; @@ -115,6 +125,19 @@ main(int argc, char **argv) /* connection options */ /* ------------------ */ + /* -h/--host */ + case 'h': + strncpy(runtime_options.host, optarg, MAXLEN); + param_set(&source_conninfo, "host", optarg); + runtime_options.connection_param_provided = true; + runtime_options.host_param_provided = true; + break; + + /* -R/--remote_user */ + case 'R': + strncpy(runtime_options.remote_user, optarg, MAXLEN); + break; + /* -S/--superuser */ case 'S': strncpy(runtime_options.superuser, optarg, MAXLEN); @@ -748,12 +771,6 @@ do_help(void) -static void -do_standby_clone(void) -{ - puts("standby clone"); -} - @@ -992,3 +1009,41 @@ check_server_version(PGconn *conn, char *server_type, bool exit_on_error, char * return server_version_num; } + + +int +test_ssh_connection(char *host, char *remote_user) +{ + char script[MAXLEN]; + int r = 1, i; + + /* On some OS, true is located in a different place than in Linux + * we have to try them all until all alternatives are gone or we + * found `true' because the target OS may differ from the source + * OS + */ + const char *bin_true_paths[] = { + "/bin/true", + "/usr/bin/true", + NULL + }; + + for (i = 0; bin_true_paths[i] && r != 0; ++i) + { + if (!remote_user[0]) + maxlen_snprintf(script, "ssh -o Batchmode=yes %s %s %s 2>/dev/null", + config_file_options.ssh_options, host, bin_true_paths[i]); + else + maxlen_snprintf(script, "ssh -o Batchmode=yes %s %s -l %s %s 2>/dev/null", + config_file_options.ssh_options, host, remote_user, + bin_true_paths[i]); + + log_verbose(LOG_DEBUG, _("test_ssh_connection(): executing %s"), script); + r = system(script); + } + + if (r != 0) + log_warning(_("unable to connect to remote host '%s' via SSH"), host); + + return r; +} diff --git a/repmgr-client.h b/repmgr-client.h index da0293f2..1fe0f00d 100644 --- a/repmgr-client.h +++ b/repmgr-client.h @@ -70,9 +70,10 @@ static struct option long_options[] = {"pg_bindir", required_argument, NULL, 'b'}, /* connection options */ + {"host", required_argument, NULL, 'h'}, + {"remote-user", required_argument, NULL, 'R'}, {"superuser", required_argument, NULL, 'S'}, - /* node options */ {"pgdata", required_argument, NULL, 'D'}, /* legacy alias for -D/--pgdata*/ @@ -86,6 +87,10 @@ static struct option long_options[] = {"terse", required_argument, NULL, 't'}, {"verbose", no_argument, NULL, 'v'}, +/* standby clone options */ + {"rsync-only", no_argument, NULL, 'r'}, + {"without-barman", no_argument, NULL, OPT_WITHOUT_BARMAN}, + /* event options */ {"event", required_argument, NULL, OPT_EVENT }, {"limit", required_argument, NULL, OPT_LIMIT }, @@ -93,10 +98,8 @@ static struct option long_options[] = /* not yet handled */ {"dbname", required_argument, NULL, 'd'}, - {"host", required_argument, NULL, 'h'}, {"port", required_argument, NULL, 'p'}, {"username", required_argument, NULL, 'U'}, - {"remote-user", required_argument, NULL, 'R'}, {"wal-keep-segments", required_argument, NULL, 'w'}, {"keep-history", required_argument, NULL, 'k'}, {"wait", no_argument, NULL, 'W'}, @@ -125,21 +128,6 @@ static struct option long_options[] = -#define T_RUNTIME_OPTIONS_INITIALIZER { \ - /* configuration metadata */ \ - false, false, false, false, \ - /* general configuration options */ \ - "", false, "", \ - /* logging options */ \ - "", false, false, false, \ - /* connection options */ \ - "", \ - /* node options */ \ - UNKNOWN_NODE_ID, "", "", \ - /* event options */ \ - "", 20, false} - - static void do_help(void); static void do_standby_clone(void);