[MEDIUM] stats: add the ability to dump table entries matching criteria

It is now possible to dump some select table entries based on criteria
which apply to the stored data. This is enabled by appending the following
options to the end of the "show table" statement :

  data.<data_type> {eq|ne|lt|gt|le|ge} <value>

For intance :

  show table http_proxy data.conn_rate gt 5
  show table http_proxy data.gpc0 ne 0

The compare applies to the integer value as it would be displayed, and
operates on signed long long integers.
This commit is contained in:
Willy Tarreau 2010-07-18 11:46:20 +02:00
parent 603861ed9d
commit 4f3f01fa39
2 changed files with 83 additions and 0 deletions

View File

@ -234,6 +234,9 @@ struct session {
void *target; /* table we want to dump, or NULL for all */
struct proxy *proxy; /* table being currently dumped (first if NULL) */
struct stksess *entry; /* last entry we were trying to dump (or first if NULL) */
long long value; /* value to compare against */
signed char data_type; /* type of data to compare, or -1 if none */
signed char data_op; /* operator (STD_OP_*) when data_type set */
} table;
struct {
const char *msg; /* pointer to a persistent message to be returned in PRINT state */

View File

@ -391,6 +391,42 @@ int stats_sock_parse_request(struct stream_interface *si, char *line)
}
else
s->data_ctx.table.target = NULL;
s->data_ctx.table.data_type = -1;
if (s->data_ctx.table.target && strncmp(args[3], "data.", 5) == 0) {
/* condition on stored data value */
s->data_ctx.table.data_type = stktable_get_data_type(args[3] + 5);
if (s->data_ctx.table.data_type < 0) {
s->data_ctx.cli.msg = "Unknown data type\n";
si->st0 = STAT_CLI_PRINT;
return 1;
}
if (!((struct proxy *)s->data_ctx.table.target)->table.data_ofs[s->data_ctx.table.data_type]) {
s->data_ctx.cli.msg = "Data type not stored in this table\n";
si->st0 = STAT_CLI_PRINT;
return 1;
}
s->data_ctx.table.data_op = get_std_op(args[4]);
if (s->data_ctx.table.data_op < 0) {
s->data_ctx.cli.msg = "Require and operator among \"eq\", \"ne\", \"le\", \"ge\", \"lt\", \"gt\"\n";
si->st0 = STAT_CLI_PRINT;
return 1;
}
if (!*args[5] || strl2llrc(args[5], strlen(args[5]), &s->data_ctx.table.value) != 0) {
s->data_ctx.cli.msg = "Require a valid integer value to compare against\n";
si->st0 = STAT_CLI_PRINT;
return 1;
}
}
else if (*args[3]) {
s->data_ctx.cli.msg = "Optional argument only supports \"data.<store_data_type>\" <operator> <value>\n";
si->st0 = STAT_CLI_PRINT;
return 1;
}
s->data_ctx.table.proxy = NULL;
s->data_ctx.table.entry = NULL;
si->st0 = STAT_CLI_O_TAB; // stats_dump_table_to_buffer
@ -2943,6 +2979,49 @@ int stats_dump_table_to_buffer(struct session *s, struct buffer *rep)
break;
case DATA_ST_LIST:
if (s->data_ctx.table.data_type >= 0) {
/* we're filtering on some data contents */
void *ptr;
long long data;
dt = s->data_ctx.table.data_type;
ptr = stktable_data_ptr(&s->data_ctx.table.proxy->table,
s->data_ctx.table.entry,
dt);
data = 0;
switch (stktable_data_types[dt].std_type) {
case STD_T_SINT:
data = stktable_data_cast(ptr, std_t_sint);
break;
case STD_T_UINT:
data = stktable_data_cast(ptr, std_t_uint);
break;
case STD_T_ULL:
data = stktable_data_cast(ptr, std_t_ull);
break;
case STD_T_FRQP:
data = read_freq_ctr_period(&stktable_data_cast(ptr, std_t_frqp),
s->data_ctx.table.proxy->table.data_arg[dt].u);
break;
}
/* skip the entry if the data does not match the test and the value */
if ((data < s->data_ctx.table.value &&
(s->data_ctx.table.data_op == STD_OP_EQ ||
s->data_ctx.table.data_op == STD_OP_GT ||
s->data_ctx.table.data_op == STD_OP_GE)) ||
(data == s->data_ctx.table.value &&
(s->data_ctx.table.data_op == STD_OP_NE ||
s->data_ctx.table.data_op == STD_OP_GT ||
s->data_ctx.table.data_op == STD_OP_LT)) ||
(data > s->data_ctx.table.value &&
(s->data_ctx.table.data_op == STD_OP_EQ ||
s->data_ctx.table.data_op == STD_OP_LT ||
s->data_ctx.table.data_op == STD_OP_LE)))
goto skip_entry;
}
chunk_printf(&msg, "%p:", s->data_ctx.table.entry);
if (s->data_ctx.table.proxy->table.type == STKTABLE_TYPE_IP) {
@ -2996,6 +3075,7 @@ int stats_dump_table_to_buffer(struct session *s, struct buffer *rep)
if (buffer_feed_chunk(rep, &msg) >= 0)
return 0;
skip_entry:
s->data_ctx.table.entry->ref_cnt--;
eb = ebmb_next(&s->data_ctx.table.entry->key);