]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: peers: Add a new command to the CLI for peers.
authorFrédéric Lécaille <flecaille@haproxy.com>
Mon, 15 Apr 2019 08:25:27 +0000 (10:25 +0200)
committerWilly Tarreau <w@1wt.eu>
Tue, 16 Apr 2019 07:58:40 +0000 (09:58 +0200)
Implements "show peers [peers section]" new CLI command to dump information
about the peers and their stick-tables to be synchronized and others internal.

May be backported as far as 1.5.

include/types/applet.h
src/peers.c

index 9fc9ec8f45c9bbd4a00777911529685f38d72825..21c0e90e78fde1ba2b8ffe20a8d7b9c677a48b3f 100644 (file)
@@ -161,6 +161,11 @@ struct appctx {
                        struct task *task;
                        struct hlua_function *fcn;
                } hlua_cli;
+               struct {
+                       void *target;
+                       struct peers *peers; /* "peers" section being currently dumped. */
+                       struct peer *peer;   /* "peer" being currently dumped. */
+               } cfgpeers;
                /* NOTE: please add regular applet contexts (ie: not
                 * CLI-specific ones) above, before "cli".
                 */
index 01120e61c1dee625dc80941983b278e2cec71224..ee597dcaa731f895e942f2380c5250d28d25c4db 100644 (file)
 #include <types/listener.h>
 #include <types/obj_type.h>
 #include <types/peers.h>
+#include <types/stats.h>
 
 #include <proto/acl.h>
 #include <proto/applet.h>
 #include <proto/channel.h>
+#include <proto/cli.h>
 #include <proto/fd.h>
 #include <proto/frontend.h>
 #include <proto/log.h>
@@ -210,6 +212,30 @@ static size_t proto_len = sizeof(PEER_SESSION_PROTO_NAME) - 1;
 struct peers *cfg_peers = NULL;
 static void peer_session_forceshutdown(struct peer *peer);
 
+static const char *statuscode_str(int statuscode)
+{
+       switch (statuscode) {
+       case PEER_SESS_SC_CONNECTCODE:
+               return "CONN";
+       case PEER_SESS_SC_CONNECTEDCODE:
+               return "HSHK";
+       case PEER_SESS_SC_SUCCESSCODE:
+               return "ESTA";
+       case PEER_SESS_SC_TRYAGAIN:
+               return "RETR";
+       case PEER_SESS_SC_ERRPROTO:
+               return "PROT";
+       case PEER_SESS_SC_ERRVERSION:
+               return "VERS";
+       case PEER_SESS_SC_ERRHOST:
+               return "NAME";
+       case PEER_SESS_SC_ERRPEER:
+               return "UNKN";
+       default:
+               return "NONE";
+       }
+}
+
 /* This function encode an uint64 to 'dynamic' length format.
    The encoded value is written at address *str, and the
    caller must assure that size after *str is large enought.
@@ -2697,3 +2723,261 @@ void peers_register_table(struct peers *peers, struct stktable *table)
        table->sync_task = peers->sync_task;
 }
 
+/*
+ * Parse the "show peers" command arguments.
+ * Returns 0 if succeeded, 1 if not with the ->msg of the appctx set as
+ * error message.
+ */
+static int cli_parse_show_peers(char **args, char *payload, struct appctx *appctx, void *private)
+{
+       appctx->ctx.cfgpeers.target = NULL;
+
+       if (*args[2]) {
+               struct peers *p;
+
+               for (p = cfg_peers; p; p = p->next) {
+                       if (!strcmp(p->id, args[2])) {
+                               appctx->ctx.cfgpeers.target = p;
+                               break;
+                       }
+               }
+
+               if (!p) {
+                       appctx->ctx.cli.severity = LOG_ERR;
+                       appctx->ctx.cli.msg = "No such peers\n";
+                       appctx->st0 = CLI_ST_PRINT;
+                       return 1;
+               }
+       }
+
+       return 0;
+}
+
+/*
+ * This function dumps the peer state information of <peers> "peers" section.
+ * Returns 0 if the output buffer is full and needs to be called again, non-zero if not.
+ * Dedicated to be called by cli_io_handler_show_peers() cli I/O handler.
+ */
+static int peers_dump_head(struct buffer *msg, struct stream_interface *si, struct peers *peers)
+{
+       struct tm tm;
+
+       get_localtime(peers->last_change, &tm);
+       chunk_appendf(msg, "%p: [%02d/%s/%04d:%02d:%02d:%02d] id=%s state=%d flags=0x%x resync_timeout=%s\n",
+                     peers,
+                     tm.tm_mday, monthname[tm.tm_mon], tm.tm_year+1900,
+                     tm.tm_hour, tm.tm_min, tm.tm_sec,
+                     peers->id, peers->state, peers->flags,
+                     peers->resync_timeout ?
+                                    tick_is_expired(peers->resync_timeout, now_ms) ? "<PAST>" :
+                                            human_time(TICKS_TO_MS(peers->resync_timeout - now_ms),
+                                            TICKS_TO_MS(1000)) : "<NEVER>");
+
+       if (ci_putchk(si_ic(si), msg) == -1) {
+               si_rx_room_blk(si);
+               return 0;
+       }
+
+       return 1;
+}
+
+/*
+ * This function dumps <peer> state information.
+ * Returns 0 if the output buffer is full and needs to be called again, non-zero
+ * if not. Dedicated to be called by cli_io_handler_show_peers() cli I/O handler.
+ */
+static int peers_dump_peer(struct buffer *msg, struct stream_interface *si, struct peer *peer)
+{
+       struct connection *conn;
+       char pn[INET6_ADDRSTRLEN];
+       struct stream_interface *peer_si;
+       struct stream *peer_s;
+       struct appctx *appctx;
+       struct shared_table *st;
+
+       addr_to_str(&peer->addr, pn, sizeof pn);
+       chunk_appendf(msg, "  %p: id=%s(%s) addr=%s:%d status=%s reconnect=%s confirm=%u\n",
+                     peer, peer->id,
+                     peer->local ? "local" : "remote",
+                     pn, get_host_port(&peer->addr),
+                     statuscode_str(peer->statuscode),
+                     peer->reconnect ?
+                                    tick_is_expired(peer->reconnect, now_ms) ? "<PAST>" :
+                                            human_time(TICKS_TO_MS(peer->reconnect - now_ms),
+                                            TICKS_TO_MS(1000)) : "<NEVER>",
+                     peer->confirm);
+
+       chunk_appendf(&trash, "        flags=0x%x", peer->flags);
+
+       appctx = peer->appctx;
+       if (!appctx)
+               goto end;
+
+       chunk_appendf(&trash, " appctx:%p st0=%d st1=%d", appctx, appctx->st0, appctx->st1);
+
+       peer_si = peer->appctx->owner;
+       if (!peer_si)
+               goto end;
+
+       peer_s = si_strm(peer_si);
+       if (!peer_s)
+               goto end;
+
+       chunk_appendf(&trash, " state=%s", si_state_str(si_opposite(peer_si)->state));
+
+       conn = objt_conn(strm_orig(peer_s));
+       if (conn)
+               chunk_appendf(&trash, "\n        xprt=%s", conn_get_xprt_name(conn));
+
+       switch (conn ? addr_to_str(&conn->addr.from, pn, sizeof(pn)) : AF_UNSPEC) {
+       case AF_INET:
+       case AF_INET6:
+               chunk_appendf(&trash, " src=%s:%d", pn, get_host_port(&conn->addr.from));
+               break;
+       case AF_UNIX:
+               chunk_appendf(&trash, " src=unix:%d", strm_li(peer_s)->luid);
+               break;
+       }
+
+       if (conn)
+               conn_get_to_addr(conn);
+
+       switch (conn ? addr_to_str(&conn->addr.to, pn, sizeof(pn)) : AF_UNSPEC) {
+       case AF_INET:
+       case AF_INET6:
+               chunk_appendf(&trash, " addr=%s:%d", pn, get_host_port(&conn->addr.to));
+               break;
+       case AF_UNIX:
+               chunk_appendf(&trash, " addr=unix:%d", strm_li(peer_s)->luid);
+               break;
+       }
+
+       if (peer->remote_table)
+               chunk_appendf(&trash, "\n        remote_table:%p id=%s local_id=%d remote_id=%d",
+                             peer->remote_table,
+                             peer->remote_table->table->id,
+                             peer->remote_table->local_id,
+                             peer->remote_table->remote_id);
+
+       if (peer->last_local_table)
+               chunk_appendf(&trash, "\n        last_local_table:%p id=%s local_id=%d remote_id=%d",
+                             peer->last_local_table,
+                             peer->last_local_table->table->id,
+                             peer->last_local_table->local_id,
+                             peer->last_local_table->remote_id);
+
+       if (peer->tables) {
+               chunk_appendf(&trash, "\n        shared tables:");
+               for (st = peer->tables; st; st = st->next) {
+                       struct stktable *t;
+                       t = st->table;
+                       chunk_appendf(&trash, "\n          %p local_id=%d remote_id=%d "
+                                     "flags=0x%x remote_data=0x%llx",
+                                     st, st->local_id, st->remote_id,
+                                     st->flags, (unsigned long long)st->remote_data);
+                       chunk_appendf(&trash, "\n              last_acked=%u last_pushed=%u last_get=%u"
+                                     " teaching_origin=%u update=%u",
+                                     st->last_acked, st->last_pushed, st->last_get,
+                                     st->teaching_origin, st->update);
+                       chunk_appendf(&trash, "\n              table:%p id=%s update=%u localupdate=%u"
+                                     " commitupdate=%u syncing=%u",
+                                     t, t->id, t->update, t->localupdate, t->commitupdate, t->syncing);
+               }
+       }
+
+ end:
+       chunk_appendf(&trash, "\n");
+       if (ci_putchk(si_ic(si), msg) == -1) {
+               si_rx_room_blk(si);
+               return 0;
+       }
+
+       return 1;
+}
+
+/*
+ * This function dumps all the peers of "peers" section.
+ * Returns 0 if the output buffer is full and needs to be called
+ * again, non-zero if not. It proceeds in an isolated thread, so
+ * there is no thread safety issue here.
+ */
+static int cli_io_handler_show_peers(struct appctx *appctx)
+{
+       int show_all;
+       int ret = 0, first_peers = 1;
+       struct stream_interface *si = appctx->owner;
+
+       thread_isolate();
+
+       show_all = !appctx->ctx.cfgpeers.target;
+
+       chunk_reset(&trash);
+
+       while (appctx->st2 != STAT_ST_FIN) {
+               switch (appctx->st2) {
+               case STAT_ST_INIT:
+                       if (show_all)
+                               appctx->ctx.cfgpeers.peers = cfg_peers;
+                       else
+                               appctx->ctx.cfgpeers.peers = appctx->ctx.cfgpeers.target;
+
+                       appctx->st2 = STAT_ST_LIST;
+                       /* fall through */
+
+               case STAT_ST_LIST:
+                       if (!appctx->ctx.cfgpeers.peers) {
+                               /* No more peers list. */
+                               appctx->st2 = STAT_ST_END;
+                       }
+                       else {
+                               if (!first_peers)
+                                       chunk_appendf(&trash, "\n");
+                               else
+                                       first_peers = 0;
+                               if (!peers_dump_head(&trash, si, appctx->ctx.cfgpeers.peers))
+                                       goto out;
+
+                               appctx->ctx.cfgpeers.peer = appctx->ctx.cfgpeers.peers->remote;
+                               appctx->ctx.cfgpeers.peers = appctx->ctx.cfgpeers.peers->next;
+                               appctx->st2 = STAT_ST_INFO;
+                       }
+                       break;
+
+               case STAT_ST_INFO:
+                       if (!appctx->ctx.cfgpeers.peer) {
+                               /* End of peer list */
+                               if (show_all)
+                                       appctx->st2 = STAT_ST_LIST;
+                           else
+                                       appctx->st2 = STAT_ST_END;
+                       }
+                       else {
+                               if (!peers_dump_peer(&trash, si, appctx->ctx.cfgpeers.peer))
+                                       goto out;
+
+                               appctx->ctx.cfgpeers.peer = appctx->ctx.cfgpeers.peer->next;
+                       }
+                   break;
+
+               case STAT_ST_END:
+                       appctx->st2 = STAT_ST_FIN;
+                       break;
+               }
+       }
+       ret = 1;
+ out:
+       thread_release();
+       return ret;
+}
+
+/*
+ * CLI keywords.
+ */
+static struct cli_kw_list cli_kws = {{ }, {
+       { { "show", "peers", NULL }, "show peers [peers section]: dump some information about all the peers or this peers section", cli_parse_show_peers, cli_io_handler_show_peers, },
+       {},
+}};
+
+/* Register cli keywords */
+INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws);
+