diff --git a/configfile.c b/configfile.c index 556021e3..d364cdd1 100644 --- a/configfile.c +++ b/configfile.c @@ -362,6 +362,7 @@ _parse_config(t_configuration_options *options, ItemList *error_list, ItemList * options->sibling_nodes_disconnect_timeout = DEFAULT_SIBLING_NODES_DISCONNECT_TIMEOUT; options->connection_check_type = CHECK_PING; options->primary_visibility_consensus = false; + memset(options->failover_validation_command, 0, sizeof(options->failover_validation_command)); /*------------- * witness settings @@ -644,6 +645,8 @@ _parse_config(t_configuration_options *options, ItemList *error_list, ItemList * } else if (strcmp(name, "primary_visibility_consensus") == 0) options->primary_visibility_consensus = parse_bool(value, name, error_list); + else if (strcmp(name, "failover_validation_command") == 0) + strncpy(options->failover_validation_command, value, sizeof(options->failover_validation_command)); /* witness settings */ else if (strcmp(name, "witness_sync_interval") == 0) diff --git a/configfile.h b/configfile.h index b4a20b82..9bb9515f 100644 --- a/configfile.h +++ b/configfile.h @@ -145,6 +145,7 @@ typedef struct int sibling_nodes_disconnect_timeout; ConnectionCheckType connection_check_type; bool primary_visibility_consensus; + char failover_validation_command[MAXPGPATH]; /* BDR settings */ bool bdr_local_monitoring_only; @@ -189,7 +190,7 @@ typedef struct /* node information */ \ UNKNOWN_NODE_ID, "", "", "", "", "", "", "", REPLICATION_TYPE_PHYSICAL, \ /* log settings */ \ - "", "", "", DEFAULT_LOG_STATUS_INTERVAL, \ + "", "", "", DEFAULT_LOG_STATUS_INTERVAL, \ /* standby clone settings */ \ false, "", "", { NULL, NULL }, "", false, "", false, "", \ /* standby promote settings */ \ @@ -216,7 +217,7 @@ typedef struct false, -1, \ DEFAULT_ASYNC_QUERY_TIMEOUT, \ DEFAULT_PRIMARY_NOTIFICATION_TIMEOUT, \ - -1, "", false, DEFAULT_SIBLING_NODES_DISCONNECT_TIMEOUT, CHECK_PING, true, \ + -1, "", false, DEFAULT_SIBLING_NODES_DISCONNECT_TIMEOUT, CHECK_PING, true, "", \ /* BDR settings */ \ false, DEFAULT_BDR_RECOVERY_TIMEOUT, \ /* service settings */ \ diff --git a/repmgrd-physical.c b/repmgrd-physical.c index b372b083..47fb9df6 100644 --- a/repmgrd-physical.c +++ b/repmgrd-physical.c @@ -23,7 +23,6 @@ #include "repmgrd.h" #include "repmgrd-physical.h" - typedef enum { FAILOVER_STATE_UNKNOWN = -1, @@ -88,7 +87,8 @@ static void update_monitoring_history(void); static void handle_sighup(PGconn **conn, t_server_type server_type); static const char *format_failover_state(FailoverState failover_state); - +static const char * format_failover_state(FailoverState failover_state); +static void parse_failover_validation_command(const char *template, t_node_info *node_info, PQExpBufferData *out); void handle_sigint_physical(SIGNAL_ARGS) @@ -3571,7 +3571,58 @@ do_election(void) candidate_node->node_id); if (candidate_node->node_id == local_node_info.node_id) + { + /* + * If "failover_validation_command" is set, execute that command. + */ + + if (config_file_options.failover_validation_command[0] != '\0') + { + PQExpBufferData failover_validation_command; + PQExpBufferData command_output; + int return_value = -1; + + initPQExpBuffer(&failover_validation_command); + initPQExpBuffer(&command_output); + + parse_failover_validation_command(config_file_options.failover_validation_command, + candidate_node, + &failover_validation_command); + + log_notice(_("executing \"failover_validation_command\"")); + log_detail("%s", failover_validation_command.data); + + /* we determine success of the command by the value placed into return_value */ + (void) local_command_return_value(failover_validation_command.data, + &command_output, + &return_value); + + termPQExpBuffer(&failover_validation_command); + + if (return_value != 0) + log_warning(_("failover validation command returned %i"), return_value); + + if (command_output.data[0] != '\0') + { + log_info("output returned by failover validation command:\n%s", command_output.data); + } + else + { + log_info(_("no output returned from command")); + } + + termPQExpBuffer(&command_output); + + if (return_value != 0) + { + /* create event here? */ + log_notice(_("failover validation command returned a non-zero value (%i)"), return_value); + return ELECTION_LOST; + } + } + return ELECTION_WON; + } return ELECTION_LOST; } @@ -3782,3 +3833,47 @@ handle_sighup(PGconn **conn, t_server_type server_type) got_SIGHUP = false; } + + +static void +parse_failover_validation_command(const char *template, t_node_info *node_info, PQExpBufferData *out) +{ + const char *src_ptr; + + for (src_ptr = template; *src_ptr; src_ptr++) + { + if (*src_ptr == '%') + { + switch (src_ptr[1]) + { + case '%': + /* %%: replace with % */ + src_ptr++; + appendPQExpBufferChar(out, *src_ptr); + break; + case 'n': + /* %n: node id */ + src_ptr++; + appendPQExpBuffer(out, "%i", node_info->node_id); + break; + case 'a': + /* %a: node name */ + src_ptr++; + appendPQExpBufferStr(out, node_info->node_name); + break; + + default: + /* otherwise treat the % as not special */ + appendPQExpBufferChar(out, *src_ptr); + + break; + } + } + else + { + appendPQExpBufferChar(out, *src_ptr); + } + } + + return; +}