diff --git a/doc/management.txt b/doc/management.txt index 151c96ed8..eef05b0fc 100644 --- a/doc/management.txt +++ b/doc/management.txt @@ -945,7 +945,9 @@ CSV output format for monitoring tools. The same format is provided on the Unix socket. Statistics are regroup in categories labelled as domains, corresponding to the -multiple components of HAProxy. Only the proxy domain is available. +multiple components of HAProxy. There are two domains avaiable: proxy and dns. +If not specified, the proxy domain is selected. Note that only the proxy +statistics are printed on the HTTP page. 9.1. CSV format --------------- diff --git a/include/haproxy/dns-t.h b/include/haproxy/dns-t.h index 8240372ed..c77b97eb5 100644 --- a/include/haproxy/dns-t.h +++ b/include/haproxy/dns-t.h @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -210,26 +211,31 @@ struct dns_nameserver { struct dgram_conn *dgram; /* transport layer */ struct sockaddr_storage addr; /* IP address */ - struct { /* numbers relted to this name server: */ - long long sent; /* - queries sent */ - long long snd_error; /* - sending errors */ - long long valid; /* - valid response */ - long long update; /* - valid response used to update server's IP */ - long long cname; /* - CNAME response requiring new resolution */ - long long cname_error; /* - error when resolving CNAMEs */ - long long any_err; /* - void response (usually because ANY qtype) */ - long long nx; /* - NX response */ - long long timeout; /* - queries which reached timeout */ - long long refused; /* - queries refused */ - long long other; /* - other type of response */ - long long invalid; /* - malformed DNS response */ - long long too_big; /* - too big response */ - long long outdated; /* - outdated response (server slower than the other ones) */ - long long truncated; /* - truncated response */ - } counters; + EXTRA_COUNTERS(extra_counters); + struct dns_counters *counters; + struct list list; /* nameserver chained list */ }; +struct dns_counters { + char *id; + long long sent; /* - queries sent */ + long long snd_error; /* - sending errors */ + long long valid; /* - valid response */ + long long update; /* - valid response used to update server's IP */ + long long cname; /* - CNAME response requiring new resolution */ + long long cname_error; /* - error when resolving CNAMEs */ + long long any_err; /* - void response (usually because ANY qtype) */ + long long nx; /* - NX response */ + long long timeout; /* - queries which reached timeout */ + long long refused; /* - queries refused */ + long long other; /* - other type of response */ + long long invalid; /* - malformed DNS response */ + long long too_big; /* - too big response */ + long long outdated; /* - outdated response (server slower than the other ones) */ + long long truncated; /* - truncated response */; +}; + struct dns_options { int family_prio; /* which IP family should the resolver use when both are returned */ struct { diff --git a/include/haproxy/dns.h b/include/haproxy/dns.h index d20977833..4f2cb8bc7 100644 --- a/include/haproxy/dns.h +++ b/include/haproxy/dns.h @@ -48,5 +48,10 @@ void dns_trigger_resolution(struct dns_requester *requester); enum act_parse_ret dns_parse_do_resolve(const char **args, int *orig_arg, struct proxy *px, struct act_rule *rule, char **err); int check_action_do_resolve(struct act_rule *rule, struct proxy *px, char **err); +int stats_dump_dns(struct stream_interface *si, + struct field *stats, size_t stats_count, + struct list *stat_modules); +void dns_stats_clear_counters(int clrall, struct list *stat_modules); +int dns_allocate_counters(struct list *stat_modules); #endif // _HAPROXY_DNS_H diff --git a/include/haproxy/stats-t.h b/include/haproxy/stats-t.h index d14865124..b9e255ca0 100644 --- a/include/haproxy/stats-t.h +++ b/include/haproxy/stats-t.h @@ -51,7 +51,7 @@ #define STATS_TYPE_SV 2 #define STATS_TYPE_SO 3 -#define STATS_DOMAIN (0) /* used for bitshifting, type of statistics, for now only proxy is available */ +#define STATS_DOMAIN (0) /* used for bitshifting, type of statistics: proxy or dns */ #define STATS_PX_CAP (8) /* used for bitshifting, differentiate obj1 type for proxy statistics */ /* HTTP stats : applet.st0 */ @@ -459,6 +459,7 @@ enum counters_type { COUNTERS_BE, COUNTERS_SV, COUNTERS_LI, + COUNTERS_DNS, COUNTERS_OFF_END }; @@ -490,6 +491,7 @@ struct extra_counters { /* stats_domain is used in a flag as a 1 byte field */ enum stats_domain { STATS_DOMAIN_PROXY = 0, + STATS_DOMAIN_DNS, STATS_DOMAIN_COUNT, STATS_DOMAIN_MASK = 0xff diff --git a/src/dns.c b/src/dns.c index c38152d7f..3d484263c 100644 --- a/src/dns.c +++ b/src/dns.c @@ -36,7 +36,7 @@ #include #include #include -#include +#include #include #include #include @@ -57,6 +57,78 @@ DECLARE_POOL(dns_requester_pool, "dns_requester", sizeof(struct dns_requester) static unsigned int resolution_uuid = 1; unsigned int dns_failed_resolutions = 0; +enum { + DNS_STAT_ID, + DNS_STAT_SND_ERROR, + DNS_STAT_VALID, + DNS_STAT_UPDATE, + DNS_STAT_CNAME, + DNS_STAT_CNAME_ERROR, + DNS_STAT_ANY_ERR, + DNS_STAT_NX, + DNS_STAT_TIMEOUT, + DNS_STAT_REFUSED, + DNS_STAT_OTHER, + DNS_STAT_INVALID, + DNS_STAT_TOO_BIG, + DNS_STAT_TRUNCATED, + DNS_STAT_OUTDATED, + DNS_STAT_END, +}; + +static struct name_desc dns_stats[] = { + [DNS_STAT_ID] = { .name = "id", .desc = "ID" }, + [DNS_STAT_SND_ERROR] = { .name = "send_error", .desc = "Send error" }, + [DNS_STAT_VALID] = { .name = "valid", .desc = "Valid" }, + [DNS_STAT_UPDATE] = { .name = "update", .desc = "Update" }, + [DNS_STAT_CNAME] = { .name = "cname", .desc = "CNAME" }, + [DNS_STAT_CNAME_ERROR] = { .name = "cname_error", .desc = "CNAME error" }, + [DNS_STAT_ANY_ERR] = { .name = "any_err", .desc = "Any errors" }, + [DNS_STAT_NX] = { .name = "nx", .desc = "NX" }, + [DNS_STAT_TIMEOUT] = { .name = "timeout", .desc = "Timeout" }, + [DNS_STAT_REFUSED] = { .name = "refused", .desc = "Refused" }, + [DNS_STAT_OTHER] = { .name = "other", .desc = "Other" }, + [DNS_STAT_INVALID] = { .name = "invalid", .desc = "Invalid" }, + [DNS_STAT_TOO_BIG] = { .name = "too_big", .desc = "Too big" }, + [DNS_STAT_TRUNCATED] = { .name = "truncated", .desc = "Truncated" }, + [DNS_STAT_OUTDATED] = { .name = "outdated", .desc = "Outdated" }, +}; + +static struct dns_counters dns_counters; + +static void dns_fill_stats(void *d, struct field *stats) +{ + struct dns_counters *counters = d; + stats[DNS_STAT_ID] = mkf_str(FO_CONFIG, counters->id); + stats[DNS_STAT_SND_ERROR] = mkf_u64(FN_GAUGE, counters->snd_error); + stats[DNS_STAT_VALID] = mkf_u64(FN_GAUGE, counters->valid); + stats[DNS_STAT_UPDATE] = mkf_u64(FN_GAUGE, counters->update); + stats[DNS_STAT_CNAME] = mkf_u64(FN_GAUGE, counters->cname); + stats[DNS_STAT_CNAME_ERROR] = mkf_u64(FN_GAUGE, counters->cname_error); + stats[DNS_STAT_ANY_ERR] = mkf_u64(FN_GAUGE, counters->any_err); + stats[DNS_STAT_NX] = mkf_u64(FN_GAUGE, counters->nx); + stats[DNS_STAT_TIMEOUT] = mkf_u64(FN_GAUGE, counters->timeout); + stats[DNS_STAT_REFUSED] = mkf_u64(FN_GAUGE, counters->refused); + stats[DNS_STAT_OTHER] = mkf_u64(FN_GAUGE, counters->other); + stats[DNS_STAT_INVALID] = mkf_u64(FN_GAUGE, counters->invalid); + stats[DNS_STAT_TOO_BIG] = mkf_u64(FN_GAUGE, counters->too_big); + stats[DNS_STAT_TRUNCATED] = mkf_u64(FN_GAUGE, counters->truncated); + stats[DNS_STAT_OUTDATED] = mkf_u64(FN_GAUGE, counters->outdated); +} + +static struct stats_module dns_stats_module = { + .name = "dns", + .domain_flags = STATS_DOMAIN_DNS << STATS_DOMAIN, + .fill_stats = dns_fill_stats, + .stats = dns_stats, + .stats_count = DNS_STAT_END, + .counters = &dns_counters, + .counters_size = sizeof(dns_counters), + .clearable = 0, +}; + +INITCALL1(STG_REGISTER, stats_register_module, &dns_stats_module); + /* Returns a pointer to the resolvers matching the id . NULL is returned if * no match is found. */ @@ -307,7 +379,7 @@ static int dns_send_query(struct dns_resolution *resolution) ret = send(fd, trash.area, len, 0); if (ret == len) { - ns->counters.sent++; + ns->counters->sent++; resolution->nb_queries++; continue; } @@ -319,7 +391,7 @@ static int dns_send_query(struct dns_resolution *resolution) } snd_error: - ns->counters.snd_error++; + ns->counters->snd_error++; resolution->nb_queries++; } @@ -1834,7 +1906,7 @@ static void dns_resolve_recv(struct dgram_conn *dgram) /* message too big */ if (buflen > resolvers->accepted_payload_size) { - ns->counters.too_big++; + ns->counters->too_big++; continue; } @@ -1843,7 +1915,7 @@ static void dns_resolve_recv(struct dgram_conn *dgram) /* read the query id from the packet (16 bits) */ if (buf + 2 > bufend) { - ns->counters.invalid++; + ns->counters->invalid++; continue; } query_id = dns_response_get_query_id(buf); @@ -1852,7 +1924,7 @@ static void dns_resolve_recv(struct dgram_conn *dgram) eb = eb32_lookup(&resolvers->query_ids, query_id); if (eb == NULL) { /* unknown query id means an outdated response and can be safely ignored */ - ns->counters.outdated++; + ns->counters->outdated++; continue; } @@ -1872,39 +1944,39 @@ static void dns_resolve_recv(struct dgram_conn *dgram) case DNS_RESP_QUERY_COUNT_ERROR: case DNS_RESP_WRONG_NAME: res->status = RSLV_STATUS_INVALID; - ns->counters.invalid++; + ns->counters->invalid++; break; case DNS_RESP_NX_DOMAIN: res->status = RSLV_STATUS_NX; - ns->counters.nx++; + ns->counters->nx++; break; case DNS_RESP_REFUSED: res->status = RSLV_STATUS_REFUSED; - ns->counters.refused++; + ns->counters->refused++; break; case DNS_RESP_ANCOUNT_ZERO: res->status = RSLV_STATUS_OTHER; - ns->counters.any_err++; + ns->counters->any_err++; break; case DNS_RESP_CNAME_ERROR: res->status = RSLV_STATUS_OTHER; - ns->counters.cname_error++; + ns->counters->cname_error++; break; case DNS_RESP_TRUNCATED: res->status = RSLV_STATUS_OTHER; - ns->counters.truncated++; + ns->counters->truncated++; break; case DNS_RESP_NO_EXPECTED_RECORD: case DNS_RESP_ERROR: case DNS_RESP_INTERNAL: res->status = RSLV_STATUS_OTHER; - ns->counters.other++; + ns->counters->other++; break; } @@ -1943,14 +2015,14 @@ static void dns_resolve_recv(struct dgram_conn *dgram) query = LIST_NEXT(&res->response.query_list, struct dns_query_item *, list); if (query && dns_hostname_cmp(query->name, res->hostname_dn, res->hostname_dn_len) != 0) { dns_resp = DNS_RESP_WRONG_NAME; - ns->counters.other++; + ns->counters->other++; goto report_res_error; } /* So the resolution succeeded */ res->status = RSLV_STATUS_VALID; res->last_valid = now_ms; - ns->counters.valid++; + ns->counters->valid++; goto report_res_success; report_res_error: @@ -2032,12 +2104,13 @@ static void dns_resolve_send(struct dgram_conn *dgram) goto snd_error; } - ns->counters.sent++; + ns->counters->sent++; + res->nb_queries++; continue; snd_error: - ns->counters.snd_error++; + ns->counters->snd_error++; res->nb_queries++; } HA_SPIN_UNLOCK(DNS_LOCK, &resolvers->lock); @@ -2144,6 +2217,7 @@ static void dns_deinit(void) fd_delete(ns->dgram->t.sock.fd); free(ns->dgram); LIST_DEL(&ns->list); + EXTRA_COUNTERS_FREE(ns->extra_counters); free(ns); } @@ -2229,6 +2303,12 @@ static int dns_finalize_config(void) dgram->data = &resolve_dgram_cb; dgram->t.sock.fd = -1; ns->dgram = dgram; + + /* Store the ns counters pointer */ + if (ns->extra_counters) { + ns->counters = EXTRA_COUNTERS_GET(ns->extra_counters, &dns_stats_module); + ns->counters->id = ns->id; + } } /* Create the task associated to the resolvers section */ @@ -2290,6 +2370,144 @@ static int dns_finalize_config(void) } +static int stats_dump_dns_to_buffer(struct stream_interface *si, + struct dns_nameserver *ns, + struct field *stats, size_t stats_count, + struct list *stat_modules) +{ + struct appctx *appctx = __objt_appctx(si->end); + struct channel *rep = si_ic(si); + struct stats_module *mod; + size_t idx = 0; + + memset(stats, 0, sizeof(struct field) * stats_count); + + list_for_each_entry(mod, stat_modules, list) { + struct counters_node *counters = EXTRA_COUNTERS_GET(ns->extra_counters, mod); + + mod->fill_stats(counters, stats + idx); + idx += mod->stats_count; + } + + if (!stats_dump_one_line(stats, idx, appctx)) + return 0; + + if (!stats_putchk(rep, NULL, &trash)) + goto full; + + return 1; + + full: + si_rx_room_rdy(si); + return 0; +} + +/* Uses as a pointer to the current resolver and + * as a pointer to the current nameserver. + */ +int stats_dump_dns(struct stream_interface *si, + struct field *stats, size_t stats_count, + struct list *stat_modules) +{ + struct appctx *appctx = __objt_appctx(si->end); + struct channel *rep = si_ic(si); + struct dns_resolvers *resolver = appctx->ctx.stats.obj1; + struct dns_nameserver *ns = appctx->ctx.stats.obj2; + + if (!resolver) + resolver = LIST_NEXT(&dns_resolvers, struct dns_resolvers *, list); + + /* dump resolvers */ + list_for_each_entry_from(resolver, &dns_resolvers, list) { + appctx->ctx.stats.obj1 = resolver; + + ns = appctx->ctx.stats.obj2 ? + appctx->ctx.stats.obj2 : + LIST_NEXT(&resolver->nameservers, struct dns_nameserver *, list); + + list_for_each_entry_from(ns, &resolver->nameservers, list) { + appctx->ctx.stats.obj2 = ns; + + if (buffer_almost_full(&rep->buf)) + goto full; + + if (!stats_dump_dns_to_buffer(si, ns, + stats, stats_count, + stat_modules)) { + return 0; + } + } + + appctx->ctx.stats.obj2 = NULL; + } + + return 1; + + full: + si_rx_room_blk(si); + return 0; +} + +void dns_stats_clear_counters(int clrall, struct list *stat_modules) +{ + struct dns_resolvers *resolvers; + struct dns_nameserver *ns; + struct stats_module *mod; + void *counters; + + list_for_each_entry(mod, stat_modules, list) { + if (!mod->clearable && !clrall) + continue; + + list_for_each_entry(resolvers, &dns_resolvers, list) { + list_for_each_entry(ns, &resolvers->nameservers, list) { + counters = EXTRA_COUNTERS_GET(ns->extra_counters, mod); + memcpy(counters, mod->counters, mod->counters_size); + } + } + } + +} + +int dns_allocate_counters(struct list *stat_modules) +{ + struct stats_module *mod; + struct dns_resolvers *resolvers; + struct dns_nameserver *ns; + + list_for_each_entry(resolvers, &dns_resolvers, list) { + list_for_each_entry(ns, &resolvers->nameservers, list) { + EXTRA_COUNTERS_REGISTER(&ns->extra_counters, COUNTERS_DNS, + alloc_failed); + + list_for_each_entry(mod, stat_modules, list) { + EXTRA_COUNTERS_ADD(mod, + ns->extra_counters, + mod->counters, + mod->counters_size); + } + + EXTRA_COUNTERS_ALLOC(ns->extra_counters, alloc_failed); + + list_for_each_entry(mod, stat_modules, list) { + memcpy(ns->extra_counters->data + mod->counters_off[ns->extra_counters->type], + mod->counters, mod->counters_size); + + /* Store the ns counters pointer */ + if (!strcmp(mod->name, "dns")) { + ns->counters = (struct dns_counters *)ns->extra_counters->data + mod->counters_off[COUNTERS_DNS]; + ns->counters->id = ns->id; + } + } + } + } + + return 1; + +alloc_failed: + return 0; +} + /* if an arg is found, it sets the resolvers section pointer into cli.p0 */ static int cli_parse_stat_resolvers(char **args, char *payload, struct appctx *appctx, void *private) { @@ -2338,21 +2556,21 @@ static int cli_io_handler_dump_resolvers_to_buffer(struct appctx *appctx) chunk_appendf(&trash, "Resolvers section %s\n", resolvers->id); list_for_each_entry(ns, &resolvers->nameservers, list) { chunk_appendf(&trash, " nameserver %s:\n", ns->id); - chunk_appendf(&trash, " sent: %lld\n", ns->counters.sent); - chunk_appendf(&trash, " snd_error: %lld\n", ns->counters.snd_error); - chunk_appendf(&trash, " valid: %lld\n", ns->counters.valid); - chunk_appendf(&trash, " update: %lld\n", ns->counters.update); - chunk_appendf(&trash, " cname: %lld\n", ns->counters.cname); - chunk_appendf(&trash, " cname_error: %lld\n", ns->counters.cname_error); - chunk_appendf(&trash, " any_err: %lld\n", ns->counters.any_err); - chunk_appendf(&trash, " nx: %lld\n", ns->counters.nx); - chunk_appendf(&trash, " timeout: %lld\n", ns->counters.timeout); - chunk_appendf(&trash, " refused: %lld\n", ns->counters.refused); - chunk_appendf(&trash, " other: %lld\n", ns->counters.other); - chunk_appendf(&trash, " invalid: %lld\n", ns->counters.invalid); - chunk_appendf(&trash, " too_big: %lld\n", ns->counters.too_big); - chunk_appendf(&trash, " truncated: %lld\n", ns->counters.truncated); - chunk_appendf(&trash, " outdated: %lld\n", ns->counters.outdated); + chunk_appendf(&trash, " sent: %lld\n", ns->counters->sent); + chunk_appendf(&trash, " snd_error: %lld\n", ns->counters->snd_error); + chunk_appendf(&trash, " valid: %lld\n", ns->counters->valid); + chunk_appendf(&trash, " update: %lld\n", ns->counters->update); + chunk_appendf(&trash, " cname: %lld\n", ns->counters->cname); + chunk_appendf(&trash, " cname_error: %lld\n", ns->counters->cname_error); + chunk_appendf(&trash, " any_err: %lld\n", ns->counters->any_err); + chunk_appendf(&trash, " nx: %lld\n", ns->counters->nx); + chunk_appendf(&trash, " timeout: %lld\n", ns->counters->timeout); + chunk_appendf(&trash, " refused: %lld\n", ns->counters->refused); + chunk_appendf(&trash, " other: %lld\n", ns->counters->other); + chunk_appendf(&trash, " invalid: %lld\n", ns->counters->invalid); + chunk_appendf(&trash, " too_big: %lld\n", ns->counters->too_big); + chunk_appendf(&trash, " truncated: %lld\n", ns->counters->truncated); + chunk_appendf(&trash, " outdated: %lld\n", ns->counters->outdated); } chunk_appendf(&trash, "\n"); } diff --git a/src/server.c b/src/server.c index b1656d5ce..4de4543b0 100644 --- a/src/server.c +++ b/src/server.c @@ -3868,7 +3868,7 @@ int snr_resolution_cb(struct dns_requester *requester, struct dns_nameserver *na save_ip: if (nameserver) { - nameserver->counters.update++; + nameserver->counters->update++; /* save the first ip we found */ chunk_printf(chk, "%s/%s", nameserver->resolvers->id, nameserver->id); } @@ -3882,7 +3882,7 @@ int snr_resolution_cb(struct dns_requester *requester, struct dns_nameserver *na invalid: if (nameserver) { - nameserver->counters.invalid++; + nameserver->counters->invalid++; goto update_status; } snr_update_srv_status(s, has_no_ip); diff --git a/src/stats.c b/src/stats.c index f84488a17..aa3fe8b4a 100644 --- a/src/stats.c +++ b/src/stats.c @@ -265,6 +265,7 @@ static struct field *stat_l[STATS_DOMAIN_COUNT]; /* list of all registered stats module */ static struct list stats_module_list[STATS_DOMAIN_COUNT] = { LIST_HEAD_INIT(stats_module_list[STATS_DOMAIN_PROXY]), + LIST_HEAD_INIT(stats_module_list[STATS_DOMAIN_DNS]), }; static inline uint8_t stats_get_domain(uint32_t domain) @@ -604,6 +605,11 @@ static int stats_dump_fields_typed(struct buffer *out, stats[ST_F_PID].u.u32); break; + case STATS_DOMAIN_DNS: + chunk_appendf(out, "D.%d.%s:", field, + stat_f[domain][field].name); + break; + default: break; } @@ -701,6 +707,18 @@ static void stats_print_proxy_field_json(struct buffer *out, obj_type, iid, sid, pos, name, pid); } +static void stats_print_dns_field_json(struct buffer *out, + const struct field *stat, + const char *name, + int pos) +{ + chunk_appendf(out, + "{" + "\"field\":{\"pos\":%d,\"name\":\"%s\"},", + pos, name); +} + + /* Dump all fields from into using a typed "field:desc:type:value" format */ static int stats_dump_fields_json(struct buffer *out, const struct field *stats, size_t stats_count, @@ -734,6 +752,10 @@ static int stats_dump_fields_json(struct buffer *out, stats[ST_F_IID].u.u32, stats[ST_F_SID].u.u32, stats[ST_F_PID].u.u32); + } else if (domain == STATS_DOMAIN_DNS) { + stats_print_dns_field_json(out, &stats[field], + stat_f[domain][field].name, + field); } if (old_len == out->data) @@ -3052,6 +3074,7 @@ static int stats_dump_stat_to_buffer(struct stream_interface *si, struct htx *ht { struct appctx *appctx = __objt_appctx(si->end); struct channel *rep = si_ic(si); + enum stats_domain domain = appctx->ctx.stats.domain; chunk_reset(&trash); @@ -3087,15 +3110,30 @@ static int stats_dump_stat_to_buffer(struct stream_interface *si, struct htx *ht goto full; } - appctx->ctx.stats.obj1 = proxies_list; + if (domain == STATS_DOMAIN_PROXY) + appctx->ctx.stats.obj1 = proxies_list; + appctx->ctx.stats.px_st = STAT_PX_ST_INIT; appctx->st2 = STAT_ST_LIST; /* fall through */ case STAT_ST_LIST: - /* dump proxies */ - if (!stats_dump_proxies(si, htx, uri)) - return 0; + switch (domain) { + case STATS_DOMAIN_DNS: + if (!stats_dump_dns(si, stat_l[domain], + stat_count[domain], + &stats_module_list[domain])) { + return 0; + } + break; + + case STATS_DOMAIN_PROXY: + default: + /* dump proxies */ + if (!stats_dump_proxies(si, htx, uri)) + return 0; + break; + } appctx->st2 = STAT_ST_END; /* fall through */ @@ -4194,6 +4232,8 @@ static int cli_parse_clear_counters(char **args, char *payload, struct appctx *a } } + dns_stats_clear_counters(clrall, &stats_module_list[STATS_DOMAIN_DNS]); + memset(activity, 0, sizeof(activity)); return 1; } @@ -4236,10 +4276,14 @@ static int cli_parse_show_stat(char **args, char *payload, struct appctx *appctx if (!strcmp(args[arg], "domain")) { ++args; - if (!strcmp(args[arg], "proxy")) + if (!strcmp(args[arg], "proxy")) { ++args; - else + } else if (!strcmp(args[arg], "dns")) { + appctx->ctx.stats.domain = STATS_DOMAIN_DNS; + ++args; + } else { return cli_err(appctx, "Invalid statistics domain.\n"); + } } if (appctx->ctx.stats.domain == STATS_DOMAIN_PROXY @@ -4417,6 +4461,44 @@ static int allocate_stats_px_postcheck(void) REGISTER_CONFIG_POSTPARSER("allocate-stats-px", allocate_stats_px_postcheck); +static int allocate_stats_dns_postcheck(void) +{ + struct stats_module *mod; + size_t i = 0; + int err_code = 0; + + stat_f[STATS_DOMAIN_DNS] = malloc(stat_count[STATS_DOMAIN_DNS] * sizeof(struct name_desc)); + if (!stat_f[STATS_DOMAIN_DNS]) { + ha_alert("stats: cannot allocate all fields for dns statistics\n"); + err_code |= ERR_ALERT | ERR_FATAL; + return err_code; + } + + list_for_each_entry(mod, &stats_module_list[STATS_DOMAIN_DNS], list) { + memcpy(stat_f[STATS_DOMAIN_DNS] + i, + mod->stats, + mod->stats_count * sizeof(struct name_desc)); + i += mod->stats_count; + } + + if (!dns_allocate_counters(&stats_module_list[STATS_DOMAIN_DNS])) { + ha_alert("stats: cannot allocate all counters for dns statistics\n"); + err_code |= ERR_ALERT | ERR_FATAL; + return err_code; + } + + stat_l[STATS_DOMAIN_DNS] = malloc(stat_count[STATS_DOMAIN_DNS] * sizeof(struct field)); + if (!stat_l[STATS_DOMAIN_DNS]) { + ha_alert("stats: cannot allocate a line for dns statistics\n"); + err_code |= ERR_ALERT | ERR_FATAL; + return err_code; + } + + return err_code; +} + +REGISTER_CONFIG_POSTPARSER("allocate-stats-dns", allocate_stats_dns_postcheck); + /* register cli keywords */ static struct cli_kw_list cli_kws = {{ },{ { { "clear", "counters", NULL }, "clear counters : clear max statistics counters (add 'all' for all counters)", cli_parse_clear_counters, NULL, NULL },