diff --git a/dbutils.c b/dbutils.c index 3d7654e2..0993496b 100644 --- a/dbutils.c +++ b/dbutils.c @@ -3795,3 +3795,4 @@ unset_bdr_failover_handler(PGconn *conn) PQclear(res); return; } + diff --git a/dbutils.h b/dbutils.h index 3dfd4e82..fe914cd3 100644 --- a/dbutils.h +++ b/dbutils.h @@ -254,6 +254,9 @@ typedef struct #define T_CONFIGFILE_LIST_INITIALIZER { 0, 0, NULL } + +/* global variables */ + extern int server_version_num; /* macros */ @@ -412,5 +415,6 @@ void get_bdr_other_node_name(PGconn *conn, int node_id, char *name_buf); bool am_bdr_failover_handler(PGconn *conn, int node_id); void unset_bdr_failover_handler(PGconn *conn); -#endif /* dbutils.h */ + +#endif /* _REPMGR_DBUTILS_H_ */ diff --git a/repmgr-action-node.c b/repmgr-action-node.c index 0a2a77f2..06040140 100644 --- a/repmgr-action-node.c +++ b/repmgr-action-node.c @@ -16,6 +16,10 @@ static bool copy_file(const char *src_file, const char *dest_file); static void format_archive_dir(char *archive_dir); +static t_server_action parse_server_action(const char *action); + +static void _do_node_service_check(void); +static void _do_node_service_list(void); void do_node_status(void) @@ -282,6 +286,103 @@ do_node_check(void) } +// --action=... +// --check +// --list -> list what would be executed for each action, filter to --action +void +do_node_service(void) +{ + t_server_action action = parse_server_action(runtime_options.action); + + if (action == ACTION_UNKNOWN) + { + log_error(_("unknown value \"%s\" provided for parameter --action"), + runtime_options.action); + log_hint(_("valid values are \"start\", \"stop\" and \"restart\"")); + exit(ERR_BAD_CONFIG); + } + + if (runtime_options.check == true) + { + if (action != ACTION_NONE) + log_warning(_("--action not required for --check")); + + return _do_node_service_check(); + } + + if (runtime_options.list == true) + { + return _do_node_service_list(); + } + + + // perform action... + // --dry-run: print only +} + + +static void +_do_node_service_check(void) +{ +} + + +static void +_do_node_service_list(void) +{ + char command[MAXLEN] = ""; + + char *data_dir = runtime_options.data_dir; + + + puts(_("Following commands would be executed for each action:")); + puts(""); + + get_server_action(ACTION_START, command, data_dir); + printf(" start: \"%s\"\n", command); + + get_server_action(ACTION_STOP, command, data_dir); + printf(" stop: \"%s\"\n", command); + + get_server_action(ACTION_RESTART, command, data_dir); + printf(" restart: \"%s\"\n", command); + + get_server_action(ACTION_RELOAD, command, data_dir); + printf(" reload: \"%s\"\n", command); + + get_server_action(ACTION_PROMOTE, command, data_dir); + printf(" promote: \"%s\"\n", command); + + puts(""); + +} + + +static t_server_action +parse_server_action(const char *action_name) +{ + if (action_name[0] == '\0') + return ACTION_NONE; + + if (strcasecmp(action_name, "start") == 0) + return ACTION_START; + + if (strcasecmp(action_name, "stop") == 0) + return ACTION_STOP; + + if (strcasecmp(action_name, "restart") == 0) + return ACTION_RESTART; + + if (strcasecmp(action_name, "reload") == 0) + return ACTION_RELOAD; + + if (strcasecmp(action_name, "promote") == 0) + return ACTION_PROMOTE; + + return ACTION_UNKNOWN; +} + + /* * Intended mainly for "internal" use by `node switchover`, which * calls this on the target server to archive any configuration files diff --git a/repmgr-action-node.h b/repmgr-action-node.h index 06e75285..22878b1e 100644 --- a/repmgr-action-node.h +++ b/repmgr-action-node.h @@ -10,5 +10,8 @@ extern void do_node_status(void); extern void do_node_check(void); extern void do_node_archive_config(void); extern void do_node_restore_config(void); +extern void do_node_service(void); + + #endif /* _REPMGR_ACTION_NODE_H_ */ diff --git a/repmgr-action-standby.c b/repmgr-action-standby.c index f314b679..42525b2b 100644 --- a/repmgr-action-standby.c +++ b/repmgr-action-standby.c @@ -369,7 +369,7 @@ do_standby_clone(void) log_notice(_("you can now start your PostgreSQL server")); - if (*config_file_options.service_start_command) + if (config_file_options.service_start_command[0] != '\n') { log_hint(_("for example : %s"), config_file_options.service_start_command); @@ -498,7 +498,7 @@ check_barman_config(void) log_error(_("unable to use directory %s"), local_data_directory); log_hint(_("use -F/--force option to force this directory to be overwritten")); - exit(ERR_BAD_CONFIG); + exit(ERR_BAD_CONFIG); } @@ -1127,15 +1127,7 @@ do_standby_promote(void) * For now we'll poll the server until the default timeout (60 seconds) */ - if (*config_file_options.service_promote_command) - { - maxlen_snprintf(script, "%s", config_file_options.service_promote_command); - } - else - { - maxlen_snprintf(script, "%s -D %s promote", - make_pg_path("pg_ctl"), data_dir); - } + get_server_action(ACTION_PROMOTE, script, data_dir); log_notice(_("promoting server using '%s'"), script); @@ -1154,8 +1146,8 @@ do_standby_promote(void) for (i = 0; i < promote_check_timeout; i += promote_check_interval) { - recovery_type = get_recovery_type(conn); + if (recovery_type == RECTYPE_PRIMARY) { promote_success = true; @@ -1446,19 +1438,8 @@ do_standby_follow(void) // XXX here check if service is running!! if not, start // ensure that problem with pg_ctl output is caught here - if (*config_file_options.service_restart_command) - { - maxlen_snprintf(restart_command, "%s", config_file_options.service_restart_command); - } - else - { - maxlen_snprintf(restart_command, - "%s %s -w -D %s -m fast restart", - make_pg_path("pg_ctl"), - config_file_options.pg_ctl_options, - data_dir); - } + get_server_action(ACTION_RESTART, restart_command, data_dir); log_notice(_("restarting server using '%s'"), restart_command); diff --git a/repmgr-client-global.h b/repmgr-client-global.h index aa3bd8cd..026b2ffc 100644 --- a/repmgr-client-global.h +++ b/repmgr-client-global.h @@ -50,12 +50,12 @@ typedef struct char remote_user[MAXLEN]; char superuser[MAXLEN]; - /* node options */ + /* general node options */ int node_id; char node_name[MAXLEN]; char data_dir[MAXPGPATH]; - /* standby clone options */ + /* "standby clone" options */ bool copy_external_config_files; int copy_external_config_files_destination; bool fast_checkpoint; @@ -69,11 +69,16 @@ typedef struct char wal_keep_segments[MAXLEN]; bool without_barman; - /* standby register options */ + /* "standby register" options */ bool wait_register_sync; int wait_register_sync_seconds; - /* event options */ + /* "node service" options */ + char action[MAXLEN]; + bool check; + bool list; + + /* "cluster event" options */ bool all; char event[MAXLEN]; int limit; @@ -97,11 +102,13 @@ typedef struct "", "", \ /* node options */ \ UNKNOWN_NODE_ID, "", "", \ - /* standby clone options */ \ + /* "standby clone" options */ \ false, CONFIG_FILE_SAMEPATH, false, false, false, "", "", "", NO_UPSTREAM_NODE, false, "", false, \ - /* standby register options */ \ + /* "standby register" options */ \ false, 0, \ - /* event options */ \ + /* "node service" options */ \ + "", false, false, \ + /* "cluster event" options */ \ false, "", CLUSTER_EVENT_LIMIT, \ "/tmp" \ } @@ -112,6 +119,17 @@ typedef enum { pg_basebackup } standy_clone_mode; +typedef enum { + ACTION_UNKNOWN = -1, + ACTION_NONE, + ACTION_START, + ACTION_STOP, + ACTION_RESTART, + ACTION_RELOAD, + ACTION_PROMOTE +} t_server_action; + + /* global configuration structures */ extern t_runtime_options runtime_options; @@ -149,4 +167,8 @@ extern void get_superuser_connection(PGconn **conn, PGconn **superuser_conn, PGc extern bool remote_command(const char *host, const char *user, const char *command, PQExpBufferData *outputbuf); +/* server control functions */ +extern void get_server_action(t_server_action action, char *script, char *data_dir); + + #endif diff --git a/repmgr-client.c b/repmgr-client.c index ca860fdf..fb9b8b71 100644 --- a/repmgr-client.c +++ b/repmgr-client.c @@ -29,6 +29,7 @@ * For internal use: * NODE ARCHIVE-CONFIG * NODE RESTORE-CONFIG + * NODE SERVICE */ #include @@ -302,8 +303,8 @@ main(int argc, char **argv) runtime_options.upstream_node_id = repmgr_atoi(optarg, "--upstream-node-id", &cli_errors, false); break; - /* standby clone options * - * --------------------- */ + /* "standby clone" options * + * ----------------------- */ /* -c/--fast-checkpoint */ case 'c': @@ -383,8 +384,8 @@ main(int argc, char **argv) runtime_options.without_barman = true; break; - /* standby register options * - * --------------------- */ + /* "standby register" options * + * -------------------------- */ case OPT_REGISTER_WAIT: runtime_options.wait_register_sync = true; @@ -394,9 +395,24 @@ main(int argc, char **argv) } break; + /* "node service" options * + * ---------------------- */ - /* event options * - * ------------- */ + /* --action (repmgr node service --action) */ + case OPT_ACTION: + strncpy(runtime_options.action, optarg, MAXLEN); + break; + + case OPT_LIST: + runtime_options.list = true; + break; + + case OPT_CHECK: + runtime_options.check = true; + break; + + /* "cluster event" options * + * ----------------------- */ case OPT_EVENT: strncpy(runtime_options.event, optarg, MAXLEN); @@ -411,6 +427,8 @@ main(int argc, char **argv) runtime_options.all = true; break; + + /* logging options * * --------------- */ @@ -578,7 +596,7 @@ main(int argc, char **argv) * { PRIMARY | MASTER } REGISTER | * STANDBY {REGISTER | UNREGISTER | CLONE [node] | PROMOTE | FOLLOW [node] | SWITCHOVER | REWIND} | * BDR { REGISTER | UNREGISTER } | - * NODE { STATUS } | + * NODE { STATUS | ARCHIVE-CONFIG | RESTORE-CONFIG | SERVICE } | * CLUSTER { CROSSCHECK | MATRIX | SHOW | CLEANUP | EVENT } * * [node] is an optional hostname, provided instead of the -h/--host optipn @@ -656,6 +674,8 @@ main(int argc, char **argv) action = NODE_ARCHIVE_CONFIG; else if (strcasecmp(repmgr_action, "RESTORE-CONFIG") == 0) action = NODE_RESTORE_CONFIG; + else if (strcasecmp(repmgr_action, "SERVICE") == 0) + action = NODE_SERVICE; } else if (strcasecmp(repmgr_node_type, "CLUSTER") == 0) @@ -972,11 +992,18 @@ main(int argc, char **argv) case NODE_STATUS: do_node_status(); break; + case NODE_CHECK: + do_node_check(); + break; case NODE_ARCHIVE_CONFIG: do_node_archive_config(); break; case NODE_RESTORE_CONFIG: do_node_restore_config(); + break; + case NODE_SERVICE: + do_node_service(); + break; /* CLUSTER */ case CLUSTER_SHOW: @@ -1293,6 +1320,20 @@ check_cli_parameters(const int action) action_name(action)); } } + + /* repmgr node service --action */ + if (runtime_options.action) + { + switch (action) + { + case NODE_SERVICE: + break; + default: + item_list_append_format(&cli_warnings, + _("--action not required when executing %s"), + action_name(action)); + } + } } @@ -1330,6 +1371,8 @@ action_name(const int action) return "NODE ARCHIVE-CONFIG"; case NODE_RESTORE_CONFIG: return "NODE RESTORE-CONFIG"; + case NODE_SERVICE: + return "NODE_SERVICE"; case CLUSTER_SHOW: return "CLUSTER SHOW"; @@ -2513,3 +2556,184 @@ remote_command(const char *host, const char *user, const char *command, PQExpBuf } +/* ======================== */ +/* server control functions */ +/* ======================== */ + +void +get_server_action(t_server_action action, char *script, char *data_dir) +{ + PQExpBufferData command; + + if (data_dir == NULL) + data_dir = "(none provided)"; + + switch(action) + { + case ACTION_NONE: + script[0] = '\0'; + return; + + case ACTION_START: + { + if (config_file_options.service_start_command[0] != '\0') + { + maxlen_snprintf(script, "%s", + config_file_options.service_start_command); + } + else + { + initPQExpBuffer(&command); + + appendPQExpBuffer( + &command, + "%s %s -w -D ", + make_pg_path("pg_ctl"), + config_file_options.pg_ctl_options); + + appendShellString( + &command, + data_dir); + + appendPQExpBuffer( + &command, + " start"); + + strncpy(script, command.data, MAXLEN); + + termPQExpBuffer(&command); + } + + return; + } + + case ACTION_STOP: + { + if (config_file_options.service_stop_command[0] != '\0') + { + maxlen_snprintf(script, "%s", + config_file_options.service_stop_command); + } + else + { + initPQExpBuffer(&command); + appendPQExpBuffer( + &command, + "%s %s -D ", + make_pg_path("pg_ctl"), + config_file_options.pg_ctl_options); + + appendShellString( + &command, + data_dir); + + appendPQExpBuffer( + &command, + " -m fast -W stop"); + + strncpy(script, command.data, MAXLEN); + + termPQExpBuffer(&command); + } + return; + } + + case ACTION_RESTART: + { + if (config_file_options.service_restart_command[0] != '\0') + { + maxlen_snprintf(script, "%s", + config_file_options.service_restart_command); + } + else + { + initPQExpBuffer(&command); + appendPQExpBuffer( + &command, + "%s %s -w -D ", + make_pg_path("pg_ctl"), + config_file_options.pg_ctl_options); + + appendShellString( + &command, + data_dir); + + appendPQExpBuffer( + &command, + " restart"); + + strncpy(script, command.data, MAXLEN); + + termPQExpBuffer(&command); + } + return; + } + + case ACTION_RELOAD: + { + if (config_file_options.service_reload_command[0] != '\0') + { + maxlen_snprintf(script, "%s", config_file_options.service_reload_command); + } + else + { + initPQExpBuffer(&command); + appendPQExpBuffer( + &command, + "%s %s -w -D ", + make_pg_path("pg_ctl"), + config_file_options.pg_ctl_options); + + appendShellString( + &command, + data_dir); + + appendPQExpBuffer( + &command, + " reload"); + + strncpy(script, command.data, MAXLEN); + + termPQExpBuffer(&command); + + } + return; + } + + case ACTION_PROMOTE: + { + if (config_file_options.service_promote_command[0] != '\0') + { + maxlen_snprintf(script, "%s", config_file_options.service_promote_command); + } + else + { + initPQExpBuffer(&command); + appendPQExpBuffer( + &command, + "%s %s -w -D ", + make_pg_path("pg_ctl"), + config_file_options.pg_ctl_options); + + appendShellString( + &command, + data_dir); + + appendPQExpBuffer( + &command, + " promote"); + + strncpy(script, command.data, MAXLEN); + + termPQExpBuffer(&command); + } + return; + } + + default: + return; + } + + return; +} + diff --git a/repmgr-client.h b/repmgr-client.h index 6c4b0744..22588a08 100644 --- a/repmgr-client.h +++ b/repmgr-client.h @@ -24,13 +24,14 @@ #define BDR_UNREGISTER 10 #define NODE_STATUS 11 #define NODE_CHECK 12 -#define NODE_ARCHIVE_CONFIG 13 -#define NODE_RESTORE_CONFIG 14 -#define CLUSTER_SHOW 15 -#define CLUSTER_CLEANUP 16 -#define CLUSTER_MATRIX 17 -#define CLUSTER_CROSSCHECK 18 -#define CLUSTER_EVENT 19 +#define NODE_SERVICE 13 +#define NODE_ARCHIVE_CONFIG 14 +#define NODE_RESTORE_CONFIG 15 +#define CLUSTER_SHOW 16 +#define CLUSTER_CLEANUP 17 +#define CLUSTER_MATRIX 18 +#define CLUSTER_CROSSCHECK 19 +#define CLUSTER_EVENT 20 /* command line options without short versions */ #define OPT_HELP 1 @@ -58,6 +59,10 @@ #define OPT_ALL 22 #define OPT_DRY_RUN 23 #define OPT_UPSTREAM_NODE_ID 24 +#define OPT_ACTION 25 +#define OPT_CHECK 26 +#define OPT_LIST 27 + /* deprecated since 3.3 */ #define OPT_DATA_DIR 998 #define OPT_NO_CONNINFO_PASSWORD 999 @@ -84,7 +89,7 @@ static struct option long_options[] = {"superuser", required_argument, NULL, 'S'}, {"username", required_argument, NULL, 'U'}, -/* node options */ +/* general node options */ {"pgdata", required_argument, NULL, 'D'}, {"node-id", required_argument, NULL, OPT_NODE_ID}, {"node-name", required_argument, NULL, OPT_NODE_NAME}, @@ -98,7 +103,7 @@ static struct option long_options[] = /* output options */ {"csv", no_argument, NULL, OPT_CSV}, -/* standby clone options */ +/* "standby clone" options */ {"copy-external-config-files", optional_argument, NULL, OPT_COPY_EXTERNAL_CONFIG_FILES}, {"fast-checkpoint", no_argument, NULL, 'c'}, {"wal-keep-segments", required_argument, NULL, 'w'}, @@ -110,10 +115,15 @@ static struct option long_options[] = {"use-recovery-conninfo-password", no_argument, NULL, OPT_USE_RECOVERY_CONNINFO_PASSWORD}, {"without-barman", no_argument, NULL, OPT_WITHOUT_BARMAN}, -/* standby register options */ +/* "standby register" options */ {"wait-sync", optional_argument, NULL, OPT_REGISTER_WAIT}, -/* event options */ +/* "node service" options */ + {"action", required_argument, NULL, OPT_ACTION}, + {"check", no_argument, NULL, OPT_CHECK}, + {"list", no_argument, NULL, OPT_LIST}, + +/* "cluster event" options */ {"all", no_argument, NULL, OPT_ALL }, {"event", required_argument, NULL, OPT_EVENT }, {"limit", required_argument, NULL, OPT_LIMIT }, @@ -155,4 +165,4 @@ static void check_cli_parameters(const int action); static void write_primary_conninfo(char *line, t_conninfo_param_list *param_list); static bool write_recovery_file_line(FILE *recovery_file, char *recovery_file_path, char *line); -#endif +#endif /* _REPMGR_CLIENT_H_ */