"daemon start": initial implementation

This commit is contained in:
Ian Barwick
2019-01-24 18:42:08 +09:00
committed by Ian Barwick
parent cbfef17a1d
commit 32b81e7d49
11 changed files with 270 additions and 30 deletions

View File

@@ -371,17 +371,24 @@ _parse_config(t_configuration_options *options, ItemList *error_list, ItemList *
options->bdr_local_monitoring_only = false;
options->bdr_recovery_timeout = DEFAULT_BDR_RECOVERY_TIMEOUT;
/*-----------------
* service settings
*-----------------
/*-------------------------
* service command settings
*-------------------------
*/
memset(options->pg_ctl_options, 0, sizeof(options->pg_ctl_options));
memset(options->service_stop_command, 0, sizeof(options->service_stop_command));
memset(options->service_start_command, 0, sizeof(options->service_start_command));
memset(options->service_stop_command, 0, sizeof(options->service_stop_command));
memset(options->service_restart_command, 0, sizeof(options->service_restart_command));
memset(options->service_reload_command, 0, sizeof(options->service_reload_command));
memset(options->service_promote_command, 0, sizeof(options->service_promote_command));
/*---------------------------------
* repmgrd service command settings
*---------------------------------
*/
memset(options->repmgrd_service_start_command, 0, sizeof(options->repmgrd_service_start_command));
memset(options->repmgrd_service_stop_command, 0, sizeof(options->repmgrd_service_stop_command));
/*----------------------------
* event notification settings
*----------------------------
@@ -585,11 +592,11 @@ _parse_config(t_configuration_options *options, ItemList *error_list, ItemList *
else if (strcmp(name, "priority") == 0)
options->priority = repmgr_atoi(value, name, error_list, 0);
else if (strcmp(name, "location") == 0)
strncpy(options->location, value, MAXLEN);
strncpy(options->location, value, sizeof(options->location));
else if (strcmp(name, "promote_command") == 0)
strncpy(options->promote_command, value, MAXLEN);
strncpy(options->promote_command, value, sizeof(options->promote_command));
else if (strcmp(name, "follow_command") == 0)
strncpy(options->follow_command, value, MAXLEN);
strncpy(options->follow_command, value, sizeof(options->follow_command));
else if (strcmp(name, "reconnect_attempts") == 0)
options->reconnect_attempts = repmgr_atoi(value, name, error_list, 0);
else if (strcmp(name, "reconnect_interval") == 0)
@@ -621,41 +628,48 @@ _parse_config(t_configuration_options *options, ItemList *error_list, ItemList *
/* service settings */
else if (strcmp(name, "pg_ctl_options") == 0)
strncpy(options->pg_ctl_options, value, MAXLEN);
else if (strcmp(name, "service_stop_command") == 0)
strncpy(options->service_stop_command, value, MAXLEN);
strncpy(options->pg_ctl_options, value, sizeof(options->pg_ctl_options));
else if (strcmp(name, "service_start_command") == 0)
strncpy(options->service_start_command, value, MAXLEN);
strncpy(options->service_start_command, value, sizeof(options->service_start_command));
else if (strcmp(name, "service_stop_command") == 0)
strncpy(options->service_stop_command, value, sizeof(options->service_stop_command));
else if (strcmp(name, "service_restart_command") == 0)
strncpy(options->service_restart_command, value, MAXLEN);
strncpy(options->service_restart_command, value, sizeof(options->service_restart_command));
else if (strcmp(name, "service_reload_command") == 0)
strncpy(options->service_reload_command, value, MAXLEN);
strncpy(options->service_reload_command, value, sizeof(options->service_reload_command));
else if (strcmp(name, "service_promote_command") == 0)
strncpy(options->service_promote_command, value, MAXLEN);
strncpy(options->service_promote_command, value, sizeof(options->service_promote_command));
/* repmgrd service settings */
else if (strcmp(name, "repmgrd_service_start_command") == 0)
strncpy(options->repmgrd_service_start_command, value, sizeof(options->repmgrd_service_start_command));
else if (strcmp(name, "repmgrd_service_stop_command") == 0)
strncpy(options->repmgrd_service_stop_command, value, sizeof(options->repmgrd_service_stop_command));
/* event notification settings */
else if (strcmp(name, "event_notification_command") == 0)
strncpy(options->event_notification_command, value, MAXLEN);
strncpy(options->event_notification_command, value, sizeof(options->event_notification_command));
else if (strcmp(name, "event_notifications") == 0)
{
/* store unparsed value for comparison when reloading config */
strncpy(options->event_notifications_orig, value, MAXLEN);
strncpy(options->event_notifications_orig, value, sizeof(options->event_notifications_orig));
parse_event_notifications_list(options, value);
}
/* barman settings */
else if (strcmp(name, "barman_host") == 0)
strncpy(options->barman_host, value, MAXLEN);
strncpy(options->barman_host, value, sizeof(options->barman_host));
else if (strcmp(name, "barman_server") == 0)
strncpy(options->barman_server, value, MAXLEN);
strncpy(options->barman_server, value, sizeof(options->barman_server));
else if (strcmp(name, "barman_config") == 0)
strncpy(options->barman_config, value, MAXLEN);
strncpy(options->barman_config, value, sizeof(options->barman_config));
/* rsync/ssh settings */
else if (strcmp(name, "rsync_options") == 0)
strncpy(options->rsync_options, value, MAXLEN);
strncpy(options->rsync_options, value, sizeof(options->rsync_options));
else if (strcmp(name, "ssh_options") == 0)
strncpy(options->ssh_options, value, MAXLEN);
strncpy(options->ssh_options, value, sizeof(options->ssh_options));
/* undocumented settings for testing */
else if (strcmp(name, "promote_delay") == 0)

View File

@@ -141,14 +141,18 @@ typedef struct
/* service settings */
char pg_ctl_options[MAXLEN];
char service_stop_command[MAXLEN];
char service_start_command[MAXLEN];
char service_restart_command[MAXLEN];
char service_reload_command[MAXLEN];
char service_promote_command[MAXLEN];
char service_start_command[MAXPGPATH];
char service_stop_command[MAXPGPATH];
char service_restart_command[MAXPGPATH];
char service_reload_command[MAXPGPATH];
char service_promote_command[MAXPGPATH];
/* repmgrd service settings */
char repmgrd_service_start_command[MAXPGPATH];
char repmgrd_service_stop_command[MAXPGPATH];
/* event notification settings */
char event_notification_command[MAXLEN];
char event_notification_command[MAXPGPATH];
char event_notifications_orig[MAXLEN];
EventNotificationList event_notifications;
@@ -205,6 +209,8 @@ typedef struct
false, DEFAULT_BDR_RECOVERY_TIMEOUT, \
/* service settings */ \
"", "", "", "", "", "", \
/* repmgrd service settings */ \
"", "", \
/* event notification settings */ \
"", "", { NULL, NULL }, \
/* barman settings */ \

View File

@@ -1746,6 +1746,31 @@ repmgrd_get_local_node_id(PGconn *conn)
}
bool
repmgrd_check_local_node_id(PGconn *conn)
{
PGresult *res = NULL;
bool node_id_settable = true;
const char *sqlquery = "SELECT repmgr.get_local_node_id()";
res = PQexec(conn, sqlquery);
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
log_db_error(conn, sqlquery, _("repmgrd_get_local_node_id(): unable to execute query"));
}
if (PQgetisnull(res, 0, 0))
{
node_id_settable = false;
}
PQclear(res);
return node_id_settable;
}
/*
* Function that checks if the primary is in exclusive backup mode.
* We'll use this when executing an action can conflict with an exclusive

View File

@@ -431,6 +431,7 @@ TimeLineHistoryEntry *get_timeline_history(PGconn *repl_conn, TimeLineID tli);
/* repmgrd shared memory functions */
bool repmgrd_set_local_node_id(PGconn *conn, int local_node_id);
int repmgrd_get_local_node_id(PGconn *conn);
bool repmgrd_check_local_node_id(PGconn *conn);
BackupState server_in_exclusive_backup_mode(PGconn *conn);
void repmgrd_set_pid(PGconn *conn, pid_t repmgrd_pid, const char *pidfile);
pid_t repmgrd_get_pid(PGconn *conn);

View File

@@ -48,5 +48,6 @@
#define ERR_REJOIN_FAIL 24
#define ERR_NODE_STATUS 25
#define ERR_REPMGRD_PAUSE 26
#define ERR_REPMGRD_SERVICE 27
#endif /* _ERRCODE_H_ */

View File

@@ -384,6 +384,84 @@ fetch_node_records(PGconn *conn, NodeInfoList *node_list)
}
void
do_daemon_start(void)
{
PGconn *conn = NULL;
PQExpBufferData repmgrd_command;
PQExpBufferData output_buf;
bool success;
/*
* if local connection available, check if repmgr.so is installed, and
* whether repmgrd is running
*/
log_verbose(LOG_INFO, _("connecting to local node"));
if (strlen(config_file_options.conninfo))
conn = establish_db_connection(config_file_options.conninfo, false);
else
conn = establish_db_connection_by_params(&source_conninfo, false);
if (PQstatus(conn) != CONNECTION_OK)
{
log_warning(_("unable to connect to local node"));
}
else
{
check_shared_library(conn);
if (is_repmgrd_running(conn) == true)
{
log_error(_("repmgrd appears to be running already"));
PQfinish(conn);
exit(ERR_REPMGRD_SERVICE);
}
}
initPQExpBuffer(&repmgrd_command);
if (config_file_options.repmgrd_service_start_command[0] != '\0')
{
appendPQExpBufferStr(&repmgrd_command,
config_file_options.repmgrd_service_start_command);
}
else
{
make_repmgrd_path(&repmgrd_command);
}
if (runtime_options.dry_run == true)
{
log_info(_("prerequisites for starting repmgrd met"));
log_detail("%s", repmgrd_command.data);
exit(SUCCESS);
}
log_debug("repmgrd start command: '%s'", repmgrd_command.data);
initPQExpBuffer(&output_buf);
success = local_command(repmgrd_command.data, &output_buf);
termPQExpBuffer(&repmgrd_command);
if (success == false)
{
log_error(_("unable to start repmgrd"));
if (output_buf.data[0] != '\0')
log_detail("%s", output_buf.data);
termPQExpBuffer(&output_buf);
exit(ERR_REPMGRD_SERVICE);
}
termPQExpBuffer(&output_buf);
}
void do_daemon_stop(void)
{
}
void do_daemon_help(void)
{
print_help_header();
@@ -392,6 +470,8 @@ void do_daemon_help(void)
printf(_(" %s [OPTIONS] daemon status\n"), progname());
printf(_(" %s [OPTIONS] daemon pause\n"), progname());
printf(_(" %s [OPTIONS] daemon unpause\n"), progname());
printf(_(" %s [OPTIONS] daemon start\n"), progname());
printf(_(" %s [OPTIONS] daemon stop\n"), progname());
puts("");
printf(_("DAEMON STATUS\n"));
@@ -416,6 +496,13 @@ void do_daemon_help(void)
printf(_(" --dry-run check if nodes are reachable but don't unpause repmgrd\n"));
puts("");
printf(_("DAEMON START\n"));
puts("");
puts("XXX");
printf(_("DAEMON STOP\n"));
puts("");
puts("XXX");
puts("");
}

View File

@@ -23,6 +23,8 @@
extern void do_daemon_status(void);
extern void do_daemon_pause(void);
extern void do_daemon_unpause(void);
extern void do_daemon_start(void);
extern void do_daemon_stop(void);
extern void do_daemon_help(void);
#endif

View File

@@ -239,6 +239,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);
extern void make_remote_repmgr_path(PQExpBufferData *outputbuf, t_node_info *remote_node_record);
extern void make_repmgrd_path(PQExpBufferData *output_buf);
/* display functions */
extern void print_help_header(void);
@@ -254,5 +256,7 @@ extern bool can_use_pg_rewind(PGconn *conn, const char *data_directory, PQExpBuf
extern void drop_replication_slot_if_exists(PGconn *conn, int node_id, char *slot_name);
extern bool check_node_can_attach(TimeLineID local_tli, XLogRecPtr local_xlogpos, PGconn *follow_target_conn, t_node_info *follow_target_node_record, bool is_rejoin);
extern void check_shared_library(PGconn *conn);
extern bool is_repmgrd_running(PGconn *conn);
#endif /* _REPMGR_CLIENT_GLOBAL_H_ */

View File

@@ -35,7 +35,8 @@
* DAEMON STATUS
* DAEMON PAUSE
* DAEMON UNPAUSE
*
* DAEMON START
* DAEMON STOP
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -935,6 +936,10 @@ main(int argc, char **argv)
action = DAEMON_PAUSE;
else if (strcasecmp(repmgr_action, "UNPAUSE") == 0)
action = DAEMON_UNPAUSE;
else if (strcasecmp(repmgr_action, "START") == 0)
action = DAEMON_START;
else if (strcasecmp(repmgr_action, "STOP") == 0)
action = DAEMON_STOP;
}
else
{
@@ -1344,6 +1349,11 @@ main(int argc, char **argv)
case DAEMON_UNPAUSE:
do_daemon_unpause();
break;
case DAEMON_START:
do_daemon_start();
case DAEMON_STOP:
do_daemon_stop();
break;
default:
/* An action will have been determined by this point */
@@ -1834,6 +1844,8 @@ check_cli_parameters(const int action)
case NODE_SERVICE:
case DAEMON_PAUSE:
case DAEMON_UNPAUSE:
case DAEMON_START:
case DAEMON_STOP:
break;
default:
item_list_append_format(&cli_warnings,
@@ -1920,7 +1932,10 @@ action_name(const int action)
return "DAEMON PAUSE";
case DAEMON_UNPAUSE:
return "DAEMON UNPAUSE";
case DAEMON_START:
return "DAEMON START";
case DAEMON_STOP:
return "DAEMON STOP";
}
return "UNKNOWN ACTION";
@@ -2731,6 +2746,34 @@ make_remote_repmgr_path(PQExpBufferData *output_buf, t_node_info *remote_node_re
}
void
make_repmgrd_path(PQExpBufferData *output_buf)
{
if (config_file_options.repmgr_bindir[0] != '\0')
{
int len = strlen(config_file_options.repmgr_bindir);
appendPQExpBufferStr(output_buf,
config_file_options.repmgr_bindir);
/* Add trailing slash */
if (config_file_options.repmgr_bindir[len - 1] != '/')
{
appendPQExpBufferChar(output_buf, '/');
}
}
else if (pg_bindir[0] != '\0')
{
appendPQExpBufferStr(output_buf,
pg_bindir);
}
appendPQExpBuffer(output_buf,
"repmgrd -f %s ",
config_file_path);
}
/* ======================== */
/* server control functions */
/* ======================== */
@@ -3358,3 +3401,47 @@ check_node_can_attach(TimeLineID local_tli, XLogRecPtr local_xlogpos, PGconn *fo
PQfinish(follow_target_repl_conn);
return success;
}
/*
* Simple check to see if "shared_preload_libraries" includes "repmgr".
* Parsing "shared_preload_libraries" is non-trivial, as it's potentially
* a comma-separated list, and worse may not be readable by the repmgr
* user.
*
* Instead, we check if a function which should return a value returns
* NULL; this indicates the shared library is not installed.
*/
void
check_shared_library(PGconn *conn)
{
bool ok = repmgrd_check_local_node_id(conn);
if (ok == true)
return;
log_error(_("repmgrd not configured for this node"));
log_hint(_("ensure \"shared_preload_libraries\" includes \"repmgr\" and restart PostgreSQL"));
PQfinish(conn);
exit(ERR_BAD_CONFIG);
}
bool
is_repmgrd_running(PGconn *conn)
{
pid_t pid;
bool is_running = false;
pid = repmgrd_get_pid(conn);
if (pid != UNKNOWN_PID)
{
if (kill(pid, 0) != -1)
{
is_running = true;
}
}
return is_running;
}

View File

@@ -48,6 +48,8 @@
#define DAEMON_STATUS 22
#define DAEMON_PAUSE 23
#define DAEMON_UNPAUSE 24
#define DAEMON_START 25
#define DAEMON_STOP 26
/* command line options without short versions */
#define OPT_HELP 1001

View File

@@ -322,6 +322,12 @@ ssh_options='-q -o ConnectTimeout=10' # Options to append to "ssh"
# repmgr provides options to override the default pg_ctl commands
# used to stop, start, restart, reload and promote the PostgreSQL cluster
#
# These options are useful when PostgreSQL has been installed from a package
# which provides OS-level service commands. In environments using an init system
# such as systemd, which keeps track of the state of various services, it is
# essential that the service commands are correctly configured and pg_ctl is
# not executed directly.
#
# NOTE: These commands must be runnable on remote nodes as well for switchover
# to function correctly.
#
@@ -343,7 +349,7 @@ ssh_options='-q -o ConnectTimeout=10' # Options to append to "ssh"
#
# Debian/Ubuntu users: use "sudo pg_ctlcluster" to execute service control commands.
#
# For more details, see: https://repmgr.org/docs/4.1/configuration-service-commands.html
# For more details, see: https://repmgr.org/docs/current/configuration-service-commands.html
#service_start_command = ''
#service_stop_command = ''
@@ -355,6 +361,11 @@ ssh_options='-q -o ConnectTimeout=10' # Options to append to "ssh"
# for "promote_command"; do not use "repmgr standby promote"
# (or a script which executes "repmgr standby promote") here.
# Used by "repmgr daemon (start|stop)" to control repmgrd
#
#repmgrd_service_start_command = ''
#repmgrd_service_stop_command = ''
#------------------------------------------------------------------------------
# Status check thresholds
#------------------------------------------------------------------------------