From 49ac9cf9cabb02286f002e641ee4336163774bce Mon Sep 17 00:00:00 2001 From: Ian Barwick Date: Wed, 19 Jul 2017 17:36:21 +0900 Subject: [PATCH] Add "repmgr cluster show" --- dbutils.c | 79 +++++++++++++++++------- dbutils.h | 10 ++- repmgr--4.0.sql | 2 + repmgr-action-cluster.c | 131 ++++++++++++++++++++++++++++++++++++++++ repmgr-action-cluster.h | 1 + repmgr-client-global.h | 5 ++ repmgr-client.c | 17 +++++- repmgr-client.h | 5 +- 8 files changed, 225 insertions(+), 25 deletions(-) diff --git a/dbutils.c b/dbutils.c index a076fe4d..c78202a4 100644 --- a/dbutils.c +++ b/dbutils.c @@ -1298,6 +1298,9 @@ _populate_node_record(PGresult *res, t_node_info *node_info, int row) node_info->priority = atoi(PQgetvalue(res, row, 8)); node_info->active = atobool(PQgetvalue(res, row, 9)); + /* This won't normally be set */ + strncpy(node_info->upstream_node_name, PQgetvalue(res, row, 10), MAXLEN); + /* Set remaining struct fields with default values */ node_info->node_status = NODE_STATUS_UNKNOWN; node_info->last_wal_receive_lsn = InvalidXLogRecPtr; @@ -1354,7 +1357,7 @@ get_node_record(PGconn *conn, int node_id, t_node_info *node_info) initPQExpBuffer(&query); appendPQExpBuffer(&query, - "SELECT node_id, type, upstream_node_id, node_name, conninfo, repluser, slot_name, location, priority, active" + "SELECT " REPMGR_NODES_COLUMNS " FROM repmgr.nodes " " WHERE node_id = %i", node_id); @@ -1382,7 +1385,7 @@ get_node_record_by_name(PGconn *conn, const char *node_name, t_node_info *node_i initPQExpBuffer(&query); appendPQExpBuffer(&query, - "SELECT node_id, type, upstream_node_id, node_name, conninfo, repluser, slot_name, location, priority, active" + "SELECT " REPMGR_NODES_COLUMNS " FROM repmgr.nodes " " WHERE node_name = '%s' ", node_name); @@ -1507,10 +1510,11 @@ get_all_node_records(PGconn *conn, NodeInfoList *node_list) initPQExpBuffer(&query); - appendPQExpBuffer(&query, - " SELECT node_id, type, upstream_node_id, node_name, conninfo, repluser, slot_name, location, priority, active" - " FROM repmgr.nodes " - "ORDER BY node_id "); + appendPQExpBuffer( + &query, + " SELECT " REPMGR_NODES_COLUMNS + " FROM repmgr.nodes " + "ORDER BY node_id "); log_verbose(LOG_DEBUG, "get_all_node_records():\n%s", query.data); @@ -1533,12 +1537,13 @@ get_downstream_node_records(PGconn *conn, int node_id, NodeInfoList *node_list) initPQExpBuffer(&query); - appendPQExpBuffer(&query, - " SELECT node_id, type, upstream_node_id, node_name, conninfo, repluser, slot_name, location, priority, active" - " FROM repmgr.nodes " - " WHERE upstream_node_id = %i " - "ORDER BY node_id ", - node_id); + appendPQExpBuffer( + &query, + " SELECT " REPMGR_NODES_COLUMNS + " FROM repmgr.nodes " + " WHERE upstream_node_id = %i " + "ORDER BY node_id ", + node_id); log_verbose(LOG_DEBUG, "get_downstream_node_records():\n%s", query.data); @@ -1562,15 +1567,16 @@ get_active_sibling_node_records(PGconn *conn, int node_id, int upstream_node_id, initPQExpBuffer(&query); - appendPQExpBuffer(&query, - " SELECT node_id, type, upstream_node_id, node_name, conninfo, repluser, slot_name, location, priority, active" - " FROM repmgr.nodes " - " WHERE upstream_node_id = %i " - " AND node_id != %i " - " AND active IS TRUE " - "ORDER BY node_id ", - upstream_node_id, - node_id); + appendPQExpBuffer( + &query, + " SELECT " REPMGR_NODES_COLUMNS + " FROM repmgr.nodes " + " WHERE upstream_node_id = %i " + " AND node_id != %i " + " AND active IS TRUE " + "ORDER BY node_id ", + upstream_node_id, + node_id); log_verbose(LOG_DEBUG, "get_active_sibling_node_records():\n%s", query.data); @@ -1596,7 +1602,7 @@ get_node_records_by_priority(PGconn *conn, NodeInfoList *node_list) appendPQExpBuffer( &query, - " SELECT node_id, type, upstream_node_id, node_name, conninfo, repluser, slot_name, location, priority, active" + " SELECT " REPMGR_NODES_COLUMNS " FROM repmgr.nodes " "ORDER BY priority DESC, node_name "); @@ -1613,6 +1619,35 @@ get_node_records_by_priority(PGconn *conn, NodeInfoList *node_list) return; } +void +get_all_node_records_with_upstream(PGconn *conn, NodeInfoList *node_list) +{ + PQExpBufferData query; + PGresult *res; + + initPQExpBuffer(&query); + + appendPQExpBuffer( + &query, + " SELECT n.node_id, n.type, n.upstream_node_id, n.node_name, n.conninfo, n.repluser, " + " n.slot_name, n.location, n.priority, n.active, un.node_name AS upstream_node_name " + " FROM repmgr.nodes n " + " LEFT JOIN nodes un " + " ON un.node_id = n.upstream_node_id" + " ORDER BY n.node_id "); + + log_verbose(LOG_DEBUG, "get_all_node_records_with_upstream():\n%s", query.data); + + res = PQexec(conn, query.data); + + termPQExpBuffer(&query); + + _populate_node_records(res, node_list); + + return; +} + + bool create_node_record(PGconn *conn, char *repmgr_action, t_node_info *node_info) diff --git a/dbutils.h b/dbutils.h index 20574e96..7695d53b 100644 --- a/dbutils.h +++ b/dbutils.h @@ -15,6 +15,8 @@ #include "strutil.h" #include "voting.h" +#define REPMGR_NODES_COLUMNS "node_id, type, upstream_node_id, node_name, conninfo, repluser, slot_name, location, priority, active, '' AS upstream_node_name " + typedef enum { UNKNOWN = 0, PRIMARY, @@ -67,6 +69,7 @@ typedef struct s_node_info int upstream_node_id; t_server_type type; char node_name[MAXLEN]; + char upstream_node_name[MAXLEN]; char conninfo[MAXLEN]; char repluser[NAMEDATALEN]; char location[MAXLEN]; @@ -78,6 +81,8 @@ typedef struct s_node_info NodeStatus node_status; MonitoringState monitoring_state; PGconn *conn; + /* for ad-hoc use e.g. when working with a list of nodes */ + char details[MAXLEN]; } t_node_info; @@ -88,6 +93,7 @@ typedef struct s_node_info "", \ "", \ "", \ + "", \ DEFAULT_LOCATION, \ DEFAULT_PRIORITY, \ true, \ @@ -95,7 +101,8 @@ typedef struct s_node_info InvalidXLogRecPtr, \ NODE_STATUS_UNKNOWN, \ MS_NORMAL, \ - NULL \ + NULL, \ + "" \ } @@ -283,6 +290,7 @@ void get_all_node_records(PGconn *conn, NodeInfoList *node_list); void get_downstream_node_records(PGconn *conn, int node_id, NodeInfoList *nodes); void get_active_sibling_node_records(PGconn *conn, int node_id, int upstream_node_id, NodeInfoList *node_list); void get_node_records_by_priority(PGconn *conn, NodeInfoList *node_list); +void get_all_node_records_with_upstream(PGconn *conn, NodeInfoList *node_list); bool create_node_record(PGconn *conn, char *repmgr_action, t_node_info *node_info); bool update_node_record(PGconn *conn, char *repmgr_action, t_node_info *node_info); diff --git a/repmgr--4.0.sql b/repmgr--4.0.sql index 37c1ac26..050f94aa 100644 --- a/repmgr--4.0.sql +++ b/repmgr--4.0.sql @@ -26,6 +26,8 @@ CREATE TABLE events ( CREATE VIEW show_nodes AS SELECT n.node_id, n.node_name, + n.active, + n.upstream_node_id, un.node_name AS upstream_node_name, n.type, n.priority, diff --git a/repmgr-action-cluster.c b/repmgr-action-cluster.c index 7a961509..674e6f49 100644 --- a/repmgr-action-cluster.c +++ b/repmgr-action-cluster.c @@ -11,6 +11,137 @@ #include "repmgr-client-global.h" #include "repmgr-action-cluster.h" + +void +do_cluster_show(void) +{ + PGconn *conn; + NodeInfoList nodes = T_NODE_INFO_LIST_INITIALIZER; + NodeInfoListCell *cell; + + char name_header[MAXLEN]; + char upstream_header[MAXLEN]; + int name_length, + upstream_length, + conninfo_length = 0; + + + /* Connect to local database to obtain cluster connection data */ + log_verbose(LOG_INFO, _("connecting to database\n")); + + 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); + + get_all_node_records_with_upstream(conn, &nodes); + + if (nodes.node_count == 0) + { + log_error(_("unable to retrieve any node records")); + PQfinish(conn); + exit(ERR_BAD_CONFIG); + } + + strncpy(name_header, _("Name"), MAXLEN); + strncpy(upstream_header, _("Upstream"), MAXLEN); + + /* + * XXX if repmgr is ever localized into non-ASCII locales, + * use pg_wcssize() or similar to establish printed column length + */ + name_length = strlen(name_header); + upstream_length = strlen(upstream_header); + + for (cell = nodes.head; cell; cell = cell->next) + { + int conninfo_length_cur, + name_length_cur, + upstream_length_cur; + + conninfo_length_cur = strlen(cell->node_info->conninfo); + if (conninfo_length_cur > conninfo_length) + conninfo_length = conninfo_length_cur; + + name_length_cur = strlen(cell->node_info->node_name); + if (name_length_cur > name_length) + name_length = name_length_cur; + + upstream_length_cur = strlen(cell->node_info->upstream_node_name); + if (upstream_length_cur > upstream_length) + upstream_length = upstream_length_cur; + + cell->node_info->conn = establish_db_connection_quiet(cell->node_info->conninfo); + + if (PQstatus(conn) != CONNECTION_OK) + { + strcpy(cell->node_info->details, " FAILED"); + } + else if (cell->node_info->type == BDR) + { + strcpy(cell->node_info->details, " BDR"); + } + else + { + RecoveryType rec_type = get_recovery_type(cell->node_info->conn); + switch (rec_type) + { + case RECTYPE_PRIMARY: + strcpy(cell->node_info->details, "* primary"); + break; + case RECTYPE_STANDBY: + strcpy(cell->node_info->details, " standby"); + break; + case RECTYPE_UNKNOWN: + strcpy(cell->node_info->details, " unknown"); + break; + } + } + + PQfinish(cell->node_info->conn); + } + + if (! runtime_options.csv) + { + int i; + printf(" Role | %-*s | %-*s | Connection string\n", name_length, name_header, upstream_length, upstream_header); + printf("----------+-"); + + for (i = 0; i < name_length; i++) + printf("-"); + + printf("-+-"); + for (i = 0; i < upstream_length; i++) + printf("-"); + + printf("-+-"); + for (i = 0; i < conninfo_length; i++) + printf("-"); + + printf("\n"); + } + + for (cell = nodes.head; cell; cell = cell->next) + { + if (runtime_options.csv) + { + int connection_status = + (PQstatus(conn) == CONNECTION_OK) ? 0 : -1; + printf("%i,%d\n", cell->node_info->node_id, connection_status); + } + else + { + printf("%-10s", cell->node_info->details); + printf("| %-*s ", name_length, cell->node_info->node_name); + printf("| %-*s ", upstream_length, cell->node_info->upstream_node_name); + printf("| %s\n", cell->node_info->conninfo); + } + } + + PQfinish(conn); +} + + /* * CLUSTER EVENT * diff --git a/repmgr-action-cluster.h b/repmgr-action-cluster.h index d1774a19..cc58ce0d 100644 --- a/repmgr-action-cluster.h +++ b/repmgr-action-cluster.h @@ -6,6 +6,7 @@ #ifndef _REPMGR_ACTION_CLUSTER_H_ #define _REPMGR_ACTION_CLUSTER_H_ +extern void do_cluster_show(void); extern void do_cluster_event(void); diff --git a/repmgr-client-global.h b/repmgr-client-global.h index b033f448..bf060abf 100644 --- a/repmgr-client-global.h +++ b/repmgr-client-global.h @@ -37,6 +37,9 @@ typedef struct bool terse; bool verbose; + /* output options */ + bool csv; + /* standard connection options */ char dbname[MAXLEN]; char host[MAXLEN]; @@ -84,6 +87,8 @@ typedef struct "", false, false, "", false, \ /* logging options */ \ "", false, false, false, \ + /* output options */ \ + false, \ /* database connection options */ \ "", "", "", "", \ /* other connection options */ \ diff --git a/repmgr-client.c b/repmgr-client.c index 1d59f762..d3f66014 100644 --- a/repmgr-client.c +++ b/repmgr-client.c @@ -16,6 +16,7 @@ * STANDBY UNREGISTER * STANDBY PROMOTE * + * CLUSTER SHOW * CLUSTER EVENT */ @@ -438,6 +439,14 @@ main(int argc, char **argv) runtime_options.verbose = true; break; + + /* output options */ + /* -------------- */ + case OPT_CSV: + runtime_options.csv = true; + break; + + /* options deprecated since 3.3 * * ---------------------------- */ case OPT_DATA_DIR: @@ -615,7 +624,10 @@ main(int argc, char **argv) else if (strcasecmp(repmgr_node_type, "CLUSTER") == 0) { - if (strcasecmp(repmgr_action, "EVENT") == 0) + + if (strcasecmp(repmgr_action, "SHOW") == 0) + action = CLUSTER_SHOW; + else if (strcasecmp(repmgr_action, "EVENT") == 0) action = CLUSTER_EVENT; } else @@ -916,6 +928,9 @@ main(int argc, char **argv) break; /* CLUSTER */ + case CLUSTER_SHOW: + do_cluster_show(); + break; case CLUSTER_EVENT: do_cluster_event(); break; diff --git a/repmgr-client.h b/repmgr-client.h index 5ce79fef..0c984bbb 100644 --- a/repmgr-client.h +++ b/repmgr-client.h @@ -93,6 +93,9 @@ static struct option long_options[] = {"terse", required_argument, NULL, 't'}, {"verbose", no_argument, NULL, 'v'}, +/* output options */ + {"csv", no_argument, NULL, OPT_CSV}, + /* standby clone options */ {"copy-external-config-files", optional_argument, NULL, OPT_COPY_EXTERNAL_CONFIG_FILES}, {"fast-checkpoint", no_argument, NULL, 'c'}, @@ -125,7 +128,7 @@ static struct option long_options[] = {"check-upstream-config", no_argument, NULL, OPT_CHECK_UPSTREAM_CONFIG}, {"pg_rewind", optional_argument, NULL, OPT_PG_REWIND}, {"pwprompt", optional_argument, NULL, OPT_PWPROMPT}, - {"csv", no_argument, NULL, OPT_CSV}, + {"node", required_argument, NULL, OPT_NODE}, {"without-barman", no_argument, NULL, OPT_WITHOUT_BARMAN}, {"copy-external-config-files", optional_argument, NULL, OPT_COPY_EXTERNAL_CONFIG_FILES},