diff --git a/include/types/applet.h b/include/types/applet.h index 2e8a297ff..6b46f4945 100644 --- a/include/types/applet.h +++ b/include/types/applet.h @@ -112,6 +112,9 @@ struct appctx { struct { struct dns_resolvers *ptr; } resolvers; + struct { + struct proxy *backend; + } server_state; } ctx; /* used by stats I/O handlers to dump the stats */ }; diff --git a/src/dumpstats.c b/src/dumpstats.c index 92f2dead5..27f83404b 100644 --- a/src/dumpstats.c +++ b/src/dumpstats.c @@ -92,6 +92,7 @@ enum { STAT_CLI_O_POOLS, /* dump memory pools */ STAT_CLI_O_TLSK, /* list all TLS ticket keys references */ STAT_CLI_O_RESOLVERS,/* dump a resolver's section nameservers counters */ + STAT_CLI_O_SERVERS_STATE, /* dump server state and changing information */ }; /* Actions available for the stats admin forms */ @@ -128,6 +129,7 @@ enum { }; static int stats_dump_info_to_buffer(struct stream_interface *si); +static int stats_dump_servers_state_to_buffer(struct stream_interface *si); static int stats_dump_pools_to_buffer(struct stream_interface *si); static int stats_dump_full_sess_to_buffer(struct stream_interface *si, struct stream *sess); static int stats_dump_sess_to_buffer(struct stream_interface *si); @@ -144,11 +146,14 @@ static int stats_tlskeys_list(struct stream_interface *si); #endif static void cli_release_handler(struct appctx *appctx); +static void dump_servers_state(struct proxy *backend, struct chunk *buf); + /* * cli_io_handler() * -> stats_dump_sess_to_buffer() // "show sess" * -> stats_dump_errors_to_buffer() // "show errors" * -> stats_dump_info_to_buffer() // "show info" + * -> stats_dump_servers_state_to_buffer() // "show servers state []" * -> stats_dump_stat_to_buffer() // "show stat" * -> stats_dump_resolvers_to_buffer() // "show stat resolver " * -> stats_dump_csv_header() @@ -188,6 +193,7 @@ static const char stats_sock_usage_msg[] = " show errors : report last request and response errors for each proxy\n" " show sess [id] : report the list of current sessions or dump this session\n" " show table [id]: report table usage stats or dump this table's contents\n" + " show servers state [id]: dump volatile server information (for backend )\n" " get weight : report a server's current weight\n" " set weight : change a server's weight\n" " set server : change a server's state, weight or address\n" @@ -1189,6 +1195,24 @@ static int stats_sock_parse_request(struct stream_interface *si, char *line) appctx->st2 = STAT_ST_INIT; appctx->st0 = STAT_CLI_O_INFO; // stats_dump_info_to_buffer } + else if (strcmp(args[1], "servers") == 0 && strcmp(args[2], "state") == 0) { + appctx->ctx.server_state.backend = NULL; + + /* check if a backend name has been provided */ + if (*args[3]) { + /* read server state from local file */ + appctx->ctx.server_state.backend = proxy_be_by_name(args[3]); + + if (appctx->ctx.server_state.backend == NULL) { + appctx->ctx.cli.msg = "Can't find backend.\n"; + appctx->st0 = STAT_CLI_PRINT; + return 1; + } + } + appctx->st2 = STAT_ST_INIT; + appctx->st0 = STAT_CLI_O_SERVERS_STATE; // stats_dump_servers_state_to_buffer + return 1; + } else if (strcmp(args[1], "pools") == 0) { appctx->st2 = STAT_ST_INIT; appctx->st0 = STAT_CLI_O_POOLS; // stats_dump_pools_to_buffer @@ -2476,6 +2500,10 @@ static void cli_io_handler(struct appctx *appctx) if (stats_dump_info_to_buffer(si)) appctx->st0 = STAT_CLI_PROMPT; break; + case STAT_CLI_O_SERVERS_STATE: + if (stats_dump_servers_state_to_buffer(si)) + appctx->st0 = STAT_CLI_PROMPT; + break; case STAT_CLI_O_STAT: if (stats_dump_stat_to_buffer(si, NULL)) appctx->st0 = STAT_CLI_PROMPT; @@ -2694,6 +2722,95 @@ static int stats_dump_info_to_buffer(struct stream_interface *si) return 1; } +/* dumps server state information into for all the servers found in + * These information are all the parameters which may change during HAProxy runtime. + * By default, we only export to the last known server state file format. + * These information can be used at next startup to recover same level of server state. + */ +static void dump_servers_state(struct proxy *backend, struct chunk *buf) +{ + struct server *srv; + char srv_addr[INET6_ADDRSTRLEN + 1]; + time_t srv_time_since_last_change; + int bk_f_forced_id, srv_f_forced_id; + + /* we don't want to report any state if the backend is not enabled on this process */ + if (backend->bind_proc && !(backend->bind_proc & (1UL << (relative_pid - 1)))) + return; + + srv = backend->srv; + + while (srv) { + srv_addr[0] = '\0'; + srv_time_since_last_change = 0; + bk_f_forced_id = 0; + srv_f_forced_id = 0; + + switch (srv->addr.ss_family) { + case AF_INET: + inet_ntop(srv->addr.ss_family, &((struct sockaddr_in *)&srv->addr)->sin_addr, + srv_addr, INET_ADDRSTRLEN + 1); + break; + case AF_INET6: + inet_ntop(srv->addr.ss_family, &((struct sockaddr_in6 *)&srv->addr)->sin6_addr, + srv_addr, INET6_ADDRSTRLEN + 1); + break; + } + srv_time_since_last_change = now.tv_sec - srv->last_change; + bk_f_forced_id = backend->options & PR_O_FORCED_ID ? 1 : 0; + srv_f_forced_id = srv->flags & SRV_F_FORCED_ID ? 1 : 0; + + chunk_appendf(buf, + "%d %s " + "%d %s %s " + "%d %d %d %d %ld " + "%d %d %d %d %d " + "%d %d" + "\n", + backend->uuid, backend->id, + srv->puid, srv->id, srv_addr, + srv->state, srv->admin, srv->uweight, srv->iweight, srv_time_since_last_change, + srv->check.status, srv->check.result, srv->check.health, srv->check.state, srv->agent.state, + bk_f_forced_id, srv_f_forced_id); + + srv = srv->next; + } +} + +/* Parses backend list or simply use backend name provided by the user to return + * states of servers to stdout. + */ +static int stats_dump_servers_state_to_buffer(struct stream_interface *si) +{ + struct appctx *appctx = __objt_appctx(si->end); + extern struct proxy *proxy; + struct proxy *curproxy; + + chunk_reset(&trash); + + chunk_printf(&trash, "%d\n# %s\n", SRV_STATE_FILE_VERSION, SRV_STATE_FILE_FIELD_NAMES); + + if (appctx->ctx.server_state.backend) { + dump_servers_state(appctx->ctx.server_state.backend, &trash); + } + else { + for (curproxy = proxy; curproxy != NULL; curproxy = curproxy->next) { + /* servers are only in backends */ + if (!(curproxy->cap & PR_CAP_BE)) + continue; + + dump_servers_state(curproxy, &trash); + } + } + + if (bi_putchk(si_ic(si), &trash) == -1) { + si_applet_cant_put(si); + return 0; + } + + return 1; +} + /* This function dumps memory usage information onto the stream interface's * read buffer. It returns 0 as long as it does not complete, non-zero upon * completion. No state is used.