diff --git a/Makefile.in b/Makefile.in index 416c45b7..459fa9a9 100644 --- a/Makefile.in +++ b/Makefile.in @@ -19,6 +19,8 @@ SCRIPTS_built = repmgr4 repmgrd4 PG_CPPFLAGS = -std=gnu89 -I$(libpq_srcdir) -Wall -Wmissing-prototypes -Wmissing-declarations $(EXTRA_CFLAGS) SHLIB_LINK = $(libpq) +HEADERS = $(wildcard *.h) + OBJS = \ repmgr.o @@ -27,7 +29,7 @@ include Makefile.global $(info Building against PostgreSQL $(MAJORVERSION)) REPMGR_CLIENT_OBJS = repmgr-client.o \ - repmgr-action-primary.o repmgr-action-standby.o repmgr-action-bdr.o repmgr-action-cluster.o \ + repmgr-action-primary.o repmgr-action-standby.o repmgr-action-bdr.o repmgr-action-cluster.o repmgr-action-node.o \ configfile.o log.o strutil.o dbutils.o dirutil.o compat.o controldata.o REPMGRD_OBJS = repmgrd.o repmgrd-physical.o repmgrd-bdr.o configfile.o log.o dbutils.o strutil.o @@ -39,6 +41,8 @@ repmgr4: $(REPMGR_CLIENT_OBJS) repmgrd4: $(REPMGRD_OBJS) $(CC) $(CFLAGS) $(REPMGRD_OBJS) $(libpq_pgport) $(LDFLAGS) $(LDFLAGS_EX) $(LIBS) -o $@$(X) +$(REPMGR_CLIENT_OBJS): $(HEADERS) +$(REPMGRD_OBJS): $(HEADERS) # Ensure Makefiles are up-to-date (should we move this to Makefile.global?) Makefile: Makefile.in config.status configure @@ -56,6 +60,7 @@ additional-clean: rm -f repmgr-action-primary.o rm -f repmgr-action-standby.o rm -f repmgr-action-bdr.o + rm -f repmgr-action-node.o rm -f repmgr-action-cluster.o rm -f repmgrd.o rm -f repmgrd-physical.o diff --git a/dbutils.c b/dbutils.c index a2689b86..16471545 100644 --- a/dbutils.c +++ b/dbutils.c @@ -2005,6 +2005,7 @@ delete_node_record(PGconn *conn, int node) log_verbose(LOG_DEBUG, "delete_node_record():\n %s", query.data); res = PQexec(conn, query.data); + termPQExpBuffer(&query); if (!res || PQresultStatus(res) != PGRES_COMMAND_OK) { @@ -2019,6 +2020,43 @@ delete_node_record(PGconn *conn, int node) return true; } +void +get_node_replication_stats(PGconn *conn, t_node_info *node_info) +{ + PQExpBufferData query; + PGresult *res; + + initPQExpBuffer(&query); + + appendPQExpBuffer( + &query, + " SELECT current_setting('max_wal_senders')::INT AS max_wal_senders, " + " (SELECT COUNT(*) FROM pg_catalog.pg_stat_replication) AS attached_wal_receivers, " + " current_setting('max_replication_slots')::INT AS max_replication_slots, " + " (SELECT COUNT(*) FROM pg_catalog.pg_replication_slots WHERE active = TRUE) AS active_replication_slots, " + " (SELECT COUNT(*) FROM pg_catalog.pg_replication_slots WHERE active = FALSE) AS inactive_replication_slots "); + + res = PQexec(conn, query.data); + termPQExpBuffer(&query); + + if (PQresultStatus(res) != PGRES_TUPLES_OK) + { + log_warning(_("unable to retrieve node replication statistics:\n %s"), + PQerrorMessage(conn)); + PQclear(res); + return; + } + + node_info->max_wal_senders = atoi(PQgetvalue(res, 0, 0)); + node_info->attached_wal_receivers = atoi(PQgetvalue(res, 0, 1)); + node_info->max_replication_slots = atoi(PQgetvalue(res, 0, 2)); + node_info->active_replication_slots = atoi(PQgetvalue(res, 0, 3)); + node_info->inactive_replication_slots = atoi(PQgetvalue(res, 0, 4)); + + return; +} + + void clear_node_info_list(NodeInfoList *nodes) diff --git a/dbutils.h b/dbutils.h index 07a98d34..bb0686e4 100644 --- a/dbutils.h +++ b/dbutils.h @@ -84,6 +84,12 @@ typedef struct s_node_info PGconn *conn; /* for ad-hoc use e.g. when working with a list of nodes */ char details[MAXLEN]; + /* various statistics */ + int max_wal_senders; + int attached_wal_receivers; + int max_replication_slots; + int active_replication_slots; + int inactive_replication_slots; } t_node_info; @@ -104,7 +110,8 @@ typedef struct s_node_info RECTYPE_UNKNOWN, \ MS_NORMAL, \ NULL, \ - "" \ + "", \ + -1, -1, -1, -1, -1 \ } @@ -306,6 +313,8 @@ bool update_node_record_conn_priority(PGconn *conn, t_configuration_options *op void clear_node_info_list(NodeInfoList *nodes); +void get_node_replication_stats(PGconn *conn, t_node_info *node_info); + /* event functions */ 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); diff --git a/repmgr-action-node.c b/repmgr-action-node.c new file mode 100644 index 00000000..3e2b323b --- /dev/null +++ b/repmgr-action-node.c @@ -0,0 +1,184 @@ +/* + * repmgr-action-node.c + * + * Implements actions available for any kind of node + * + * Copyright (c) 2ndQuadrant, 2010-2017 + */ + + +#include "repmgr.h" + +#include "repmgr-client-global.h" +#include "repmgr-action-node.h" + +void +do_node_status(void) +{ + PGconn *conn; + + int target_node_id = UNKNOWN_NODE_ID; + t_node_info node_info = T_NODE_INFO_INITIALIZER; + char server_version[MAXLEN]; + char cluster_size[MAXLEN]; + PQExpBufferData output; + + ItemList warnings = { NULL, NULL }; + RecoveryType recovery_type; + + if (strlen(config_file_options.conninfo)) + conn = establish_db_connection(config_file_options.conninfo, true); + else + conn = establish_db_connection_by_params(&source_conninfo, true); + + if (runtime_options.node_id != UNKNOWN_NODE_ID) + target_node_id = runtime_options.node_id; + else + target_node_id = config_file_options.node_id; + + /* Check node exists and is really a standby */ + + if (get_node_record(conn, target_node_id, &node_info) != RECORD_FOUND) + { + log_error(_("no record found for node %i"), target_node_id); + PQfinish(conn); + exit(ERR_BAD_CONFIG); + } + (void) get_server_version(conn, server_version); + + if (get_cluster_size(conn, cluster_size) == false) + strncpy(cluster_size, _("unknown"), MAXLEN); + + recovery_type = get_recovery_type(conn); + + get_node_replication_stats(conn, &node_info); + + initPQExpBuffer(&output); + + appendPQExpBuffer( + &output, + "Node \"%s\":\n", + node_info.node_name); + + appendPQExpBuffer( + &output, + "%cPostgreSQL version: %s\n", + '\t', server_version); + + appendPQExpBuffer( + &output, + "%cTotal data size: %s\n", + '\t', cluster_size); + + appendPQExpBuffer( + &output, + "%cConninfo: \"%s\"\n", + '\t', node_info.conninfo); + + appendPQExpBuffer( + &output, + "%cRole: \"%s\"\n", + '\t', get_node_type_string(node_info.type)); + + switch (node_info.type) + { + case PRIMARY: + if (recovery_type == RECTYPE_STANDBY) + { + item_list_append( + &warnings, + _("- node is registered as primary but running as standby")); + } + break; + case STANDBY: + if (recovery_type == RECTYPE_PRIMARY) + { + item_list_append( + &warnings, + _("- node is registered as standby but running as primary")); + } + break; + case BDR: + default: + break; + } + + + if (node_info.max_wal_senders >= 0) + { + appendPQExpBuffer( + &output, + "%cReplication connections: %i (of maximal %i)\n", + '\t', + node_info.attached_wal_receivers, + node_info.max_wal_senders); + + } + else if (node_info.max_wal_senders == 0) + { + appendPQExpBuffer( + &output, + "%cReplication connections: disabled\n", + '\t'); + } + + if (node_info.max_replication_slots > 0) + { + appendPQExpBuffer( + &output, + "%cReplication slots: %i (of maximal %i)", + '\t', + node_info.active_replication_slots + node_info.inactive_replication_slots, + node_info.max_replication_slots); + + if (node_info.inactive_replication_slots > 0) + { + appendPQExpBuffer( + &output, + "; %i inactive", + node_info.inactive_replication_slots); + + item_list_append_format( + &warnings, + _("- node has %i inactive replication slots"), + node_info.inactive_replication_slots); + } + } + else if (node_info.max_replication_slots == 0) + { + appendPQExpBuffer( + &output, + "%cReplication slots: disabled\n", + '\t'); + } + +/* + appendPQExpBuffer( + &output, + "%c: \"%s\"\n", + '\t', ); + appendPQExpBuffer( + &output, + "%c: \"%s\"\n", + '\t', node_info.); +*/ + + + puts(output.data); + + termPQExpBuffer(&output); + + if (warnings.head != NULL && runtime_options.terse == false) + { + log_warning(_("following issue(s) were detected:")); + print_item_list(&warnings); + log_hint(_("execute \"repmgr node check\" for more details")); + } + +} + + +void +do_node_check(void) +{ +} diff --git a/repmgr-action-node.h b/repmgr-action-node.h new file mode 100644 index 00000000..da4ccc6b --- /dev/null +++ b/repmgr-action-node.h @@ -0,0 +1,12 @@ +/* + * repmgr-action-node.h + * Copyright (c) 2ndQuadrant, 2010-2017 + */ + +#ifndef _REPMGR_ACTION_NODE_H_ +#define _REPMGR_ACTION_NODE_H_ + +extern void do_node_status(void); +extern void do_node_check(void); + +#endif /* _REPMGR_ACTION_NODE_H_ */ diff --git a/repmgr-action-standby.c b/repmgr-action-standby.c index 0df3001d..e993c602 100644 --- a/repmgr-action-standby.c +++ b/repmgr-action-standby.c @@ -1389,8 +1389,8 @@ do_standby_follow(void) * replication slots are in use and we want to delete the old slot. */ record_status = get_node_record(primary_conn, - config_file_options.node_id, - &local_node_record); + config_file_options.node_id, + &local_node_record); if (record_status != RECORD_FOUND) { diff --git a/repmgr-client.c b/repmgr-client.c index 0c1fe0fe..a5b5f21d 100644 --- a/repmgr-client.c +++ b/repmgr-client.c @@ -16,6 +16,9 @@ * STANDBY UNREGISTER * STANDBY PROMOTE * + * BDR REGISTER + * BDR UNREGISTER + * * CLUSTER SHOW * CLUSTER EVENT */ @@ -30,6 +33,7 @@ #include "repmgr-action-primary.h" #include "repmgr-action-standby.h" #include "repmgr-action-bdr.h" +#include "repmgr-action-node.h" #include "repmgr-action-cluster.h" @@ -588,6 +592,10 @@ main(int argc, char **argv) action = PRIMARY_REGISTER; else if (strcasecmp(repmgr_action, "UNREGISTER") == 0) action = PRIMARY_UNREGISTER; + else if (strcasecmp(repmgr_action, "CHECK") == 0) + action = NODE_CHECK; + else if (strcasecmp(repmgr_action, "STATUS") == 0) + action = NODE_STATUS; } else if (strcasecmp(repmgr_node_type, "STANDBY") == 0) @@ -608,6 +616,10 @@ main(int argc, char **argv) action = STANDBY_ARCHIVE_CONFIG; else if (strcasecmp(repmgr_action, "RESTORE-CONFIG") == 0) action = STANDBY_RESTORE_CONFIG; + else if (strcasecmp(repmgr_action, "CHECK") == 0) + action = NODE_CHECK; + else if (strcasecmp(repmgr_action, "STATUS") == 0) + action = NODE_STATUS; } else if (strcasecmp(repmgr_node_type, "BDR") == 0) #else @@ -618,6 +630,18 @@ main(int argc, char **argv) action = BDR_REGISTER; else if (strcasecmp(repmgr_action, "UNREGISTER") == 0) action = BDR_UNREGISTER; + else if (strcasecmp(repmgr_action, "CHECK") == 0) + action = NODE_CHECK; + else if (strcasecmp(repmgr_action, "STATUS") == 0) + action = NODE_STATUS; + } + + else if (strcasecmp(repmgr_node_type, "NODE") == 0) + { + if (strcasecmp(repmgr_action, "CHECK") == 0) + action = NODE_CHECK; + else if (strcasecmp(repmgr_action, "STATUS") == 0) + action = NODE_STATUS; } else if (strcasecmp(repmgr_node_type, "CLUSTER") == 0) @@ -929,6 +953,11 @@ main(int argc, char **argv) do_bdr_unregister(); break; + /* NODE */ + case NODE_STATUS: + do_node_status(); + break; + /* CLUSTER */ case CLUSTER_SHOW: do_cluster_show(); diff --git a/repmgr-client.h b/repmgr-client.h index 0c984bbb..efcda6dc 100644 --- a/repmgr-client.h +++ b/repmgr-client.h @@ -22,13 +22,15 @@ #define STANDBY_SWITCHOVER 8 #define STANDBY_ARCHIVE_CONFIG 9 #define STANDBY_RESTORE_CONFIG 10 -#define CLUSTER_SHOW 11 -#define CLUSTER_CLEANUP 12 -#define CLUSTER_MATRIX 13 -#define CLUSTER_CROSSCHECK 14 -#define CLUSTER_EVENT 15 -#define BDR_REGISTER 16 -#define BDR_UNREGISTER 17 +#define BDR_REGISTER 11 +#define BDR_UNREGISTER 12 +#define NODE_STATUS 13 +#define NODE_CHECK 14 +#define CLUSTER_SHOW 15 +#define CLUSTER_CLEANUP 16 +#define CLUSTER_MATRIX 17 +#define CLUSTER_CROSSCHECK 18 +#define CLUSTER_EVENT 19 /* command line options without short versions */ #define OPT_HELP 1