diff --git a/doc/configuration.txt b/doc/configuration.txt index 61f113235..63a28304b 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -15257,6 +15257,23 @@ show stat [ ] A similar empty line appears at the end of the second block (stats) so that the reader knows the output has not been truncated. +show stat resolvers + Dump statistics for the given resolvers section. + For each name server, the following counters are reported: + sent: number of DNS requests sent to this server + valid: number of DNS valid responses received from this server + update: number of DNS responses used to update the server's IP address + cname: number of CNAME responses + cname_error: CNAME errors encountered with this server + any_err: number of empty response (IE: server does not support ANY type) + nx: non existent domain response received from this server + timeout: how many time this server did not answer in time + refused: number of requests refused by this server + other: any other DNS errors + invalid: invalid DNS response (from a protocol point of view) + too_big: too big response + outdated: number of response arrived too late (after an other name server) + show table Dump general information on all known stick-tables. Their name is returned (the name of the proxy which holds them), their type (currently zero, always diff --git a/include/types/applet.h b/include/types/applet.h index 5efeea553..ff82ef3c5 100644 --- a/include/types/applet.h +++ b/include/types/applet.h @@ -110,6 +110,9 @@ struct appctx { struct list wake_on_read; struct list wake_on_write; } hlua; + struct { + struct dns_resolvers *ptr; + } resolvers; } ctx; /* used by stats I/O handlers to dump the stats */ }; diff --git a/src/dumpstats.c b/src/dumpstats.c index cd1927955..96180fe71 100644 --- a/src/dumpstats.c +++ b/src/dumpstats.c @@ -39,6 +39,7 @@ #include #include +#include #include #include @@ -90,6 +91,7 @@ enum { STAT_CLI_O_MLOOK, /* lookup a map entry */ 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 */ }; /* Actions available for the stats admin forms */ @@ -133,6 +135,7 @@ static int stats_dump_errors_to_buffer(struct stream_interface *si); static int stats_table_request(struct stream_interface *si, int show); static int stats_dump_proxy_to_buffer(struct stream_interface *si, struct proxy *px, struct uri_auth *uri); static int stats_dump_stat_to_buffer(struct stream_interface *si, struct uri_auth *uri); +static int stats_dump_resolvers_to_buffer(struct stream_interface *si); static int stats_pats_list(struct stream_interface *si); static int stats_pat_list(struct stream_interface *si); static int stats_map_lookup(struct stream_interface *si); @@ -147,6 +150,7 @@ static void cli_release_handler(struct appctx *appctx); * -> stats_dump_errors_to_buffer() // "show errors" * -> stats_dump_info_to_buffer() // "show info" * -> stats_dump_stat_to_buffer() // "show stat" + * -> stats_dump_resolvers_to_buffer() // "show stat resolver " * -> stats_dump_csv_header() * -> stats_dump_proxy_to_buffer() * -> stats_dump_fe_stats() @@ -1145,7 +1149,33 @@ static int stats_sock_parse_request(struct stream_interface *si, char *line) appctx->ctx.stats.flags = 0; if (strcmp(args[0], "show") == 0) { if (strcmp(args[1], "stat") == 0) { - if (*args[2] && *args[3] && *args[4]) { + if (strcmp(args[2], "resolvers") == 0) { + struct dns_resolvers *presolvers; + + if (!*args[3]) { + appctx->ctx.cli.msg = "Missing resolver section identifier.\n"; + appctx->st0 = STAT_CLI_PRINT; + return 1; + } + + appctx->ctx.resolvers.ptr = NULL; + list_for_each_entry(presolvers, &dns_resolvers, list) { + if (strcmp(presolvers->id, args[3]) == 0) { + appctx->ctx.resolvers.ptr = presolvers; + break; + } + } + if (appctx->ctx.resolvers.ptr == NULL) { + appctx->ctx.cli.msg = "Can't find resolvers section.\n"; + appctx->st0 = STAT_CLI_PRINT; + return 1; + } + + appctx->st2 = STAT_ST_INIT; + appctx->st0 = STAT_CLI_O_RESOLVERS; + return 1; + } + else if (*args[2] && *args[3] && *args[4]) { appctx->ctx.stats.flags |= STAT_BOUND; appctx->ctx.stats.iid = atoi(args[2]); appctx->ctx.stats.type = atoi(args[3]); @@ -2450,6 +2480,10 @@ static void cli_io_handler(struct appctx *appctx) if (stats_dump_stat_to_buffer(si, NULL)) appctx->st0 = STAT_CLI_PROMPT; break; + case STAT_CLI_O_RESOLVERS: + if (stats_dump_resolvers_to_buffer(si)) + appctx->st0 = STAT_CLI_PROMPT; + break; case STAT_CLI_O_SESS: if (stats_dump_sess_to_buffer(si)) appctx->st0 = STAT_CLI_PROMPT; @@ -6178,6 +6212,61 @@ static int dump_text_line(struct chunk *out, const char *buf, int bsize, int len return ptr; } +/* This function dumps counters from all resolvers section and associated name servers. + * It returns 0 if the output buffer is full and it needs + * to be called again, otherwise non-zero. + */ +static int stats_dump_resolvers_to_buffer(struct stream_interface *si) +{ + struct appctx *appctx = __objt_appctx(si->end); + struct dns_resolvers *presolvers; + struct dns_nameserver *pnameserver; + + chunk_reset(&trash); + + switch (appctx->st2) { + case STAT_ST_INIT: + appctx->st2 = STAT_ST_LIST; /* let's start producing data */ + /* fall through */ + + case STAT_ST_LIST: + presolvers = appctx->ctx.resolvers.ptr; + chunk_appendf(&trash, "Resolvers section %s\n", presolvers->id); + list_for_each_entry(pnameserver, &presolvers->nameserver_list, list) { + chunk_appendf(&trash, " nameserver %s:\n", pnameserver->id); + chunk_appendf(&trash, " sent: %ld\n", pnameserver->counters.sent); + chunk_appendf(&trash, " valid: %ld\n", pnameserver->counters.valid); + chunk_appendf(&trash, " update: %ld\n", pnameserver->counters.update); + chunk_appendf(&trash, " cname: %ld\n", pnameserver->counters.cname); + chunk_appendf(&trash, " cname_error: %ld\n", pnameserver->counters.cname_error); + chunk_appendf(&trash, " any_err: %ld\n", pnameserver->counters.any_err); + chunk_appendf(&trash, " nx: %ld\n", pnameserver->counters.nx); + chunk_appendf(&trash, " timeout: %ld\n", pnameserver->counters.timeout); + chunk_appendf(&trash, " refused: %ld\n", pnameserver->counters.refused); + chunk_appendf(&trash, " other: %ld\n", pnameserver->counters.other); + chunk_appendf(&trash, " invalid: %ld\n", pnameserver->counters.invalid); + chunk_appendf(&trash, " too_big: %ld\n", pnameserver->counters.too_big); + chunk_appendf(&trash, " outdated: %ld\n", pnameserver->counters.outdated); + } + + /* display response */ + if (bi_putchk(si_ic(si), &trash) == -1) { + /* let's try again later from this session. We add ourselves into + * this session's users so that it can remove us upon termination. + */ + si->flags |= SI_FL_WAIT_ROOM; + return 0; + } + + appctx->st2 = STAT_ST_FIN; + /* fall through */ + + default: + appctx->st2 = STAT_ST_FIN; + return 1; + } +} + /* This function dumps all captured errors onto the stream interface's * read buffer. It returns 0 if the output buffer is full and it needs * to be called again, otherwise non-zero.