From 1a693fc2fd0416c8f46be207d6e14a52868b20b7 Mon Sep 17 00:00:00 2001 From: Adis Nezirovic Date: Thu, 16 Jan 2020 15:19:29 +0100 Subject: [PATCH] MEDIUM: cli: Allow multiple filter entries for "show table" For complex stick tables with many entries/columns, it can be beneficial to filter using multiple criteria. The maximum number of filter entries can be controlled by defining STKTABLE_FILTER_LEN during build time. This patch can be backported to older releases. --- doc/management.txt | 4 +- include/common/defaults.h | 5 ++ include/types/applet.h | 6 +-- src/hlua_fcn.c | 7 ++- src/stick_table.c | 111 +++++++++++++++++++++----------------- 5 files changed, 76 insertions(+), 57 deletions(-) diff --git a/doc/management.txt b/doc/management.txt index 521a671121..24969be887 100644 --- a/doc/management.txt +++ b/doc/management.txt @@ -2569,7 +2569,7 @@ show table >>> # table: front_pub, type: ip, size:204800, used:171454 >>> # table: back_rdp, type: ip, size:204800, used:0 -show table [ data. ] | [ key ] +show table [ data. [data. ...]] | [ key ] Dump contents of stick-table . In this mode, a first line of generic information about the table is reported as with "show table", then all entries are dumped. Since this can be quite heavy, it is possible to specify @@ -2588,6 +2588,8 @@ show table [ data. ] | [ key ] - lt : match entries whose data is less than this value - gt : match entries whose data is greater than this value + In this form, you can use multiple data filter entries, up to a maximum + defined during build time (4 by default). When the key form is used the entry is shown. The key must be of the same type as the table, which currently is limited to IPv4, IPv6, integer, diff --git a/include/common/defaults.h b/include/common/defaults.h index fbacca481e..e86c9bce13 100644 --- a/include/common/defaults.h +++ b/include/common/defaults.h @@ -116,6 +116,11 @@ #define STKTABLE_EXTRA_DATA_TYPES 0 #endif +// max # of stick-table filter entries that can be used during dump +#ifndef STKTABLE_FILTER_LEN +#define STKTABLE_FILTER_LEN 4 +#endif + // max # of loops we can perform around a read() which succeeds. // It's very frequent that the system returns a few TCP segments at a time. #ifndef MAX_READ_POLL_LOOPS diff --git a/include/types/applet.h b/include/types/applet.h index dfb489c9f1..76a598d21e 100644 --- a/include/types/applet.h +++ b/include/types/applet.h @@ -150,9 +150,9 @@ struct appctx { void *target; /* table we want to dump, or NULL for all */ struct stktable *t; /* 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 */ + long long value[STKTABLE_FILTER_LEN]; /* value to compare against */ + signed char data_type[STKTABLE_FILTER_LEN]; /* type of data to compare, or -1 if none */ + signed char data_op[STKTABLE_FILTER_LEN]; /* operator (STD_OP_*) when data_type set */ char action; /* action on the table : one of STK_CLI_ACT_* */ } table; struct { diff --git a/src/hlua_fcn.c b/src/hlua_fcn.c index 7ef708ccbd..f8024aa849 100644 --- a/src/hlua_fcn.c +++ b/src/hlua_fcn.c @@ -39,7 +39,6 @@ static int class_listener_ref; static int class_regex_ref; static int class_stktable_ref; -#define MAX_STK_FILTER_LEN 4 #define STATS_LEN (MAX((int)ST_F_TOTAL_FIELDS, (int)INF_TOTAL_FIELDS)) static THREAD_LOCAL struct field stats[STATS_LEN]; @@ -682,7 +681,7 @@ int hlua_stktable_dump(lua_State *L) int op; int dt; long long val; - struct stk_filter filter[MAX_STK_FILTER_LEN]; + struct stk_filter filter[STKTABLE_FILTER_LEN]; int filter_count = 0; int i; int skip_entry; @@ -700,8 +699,8 @@ int hlua_stktable_dump(lua_State *L) while (lua_next(L, 2) != 0) { int entry_idx = 0; - if (filter_count >= MAX_STK_FILTER_LEN) - return hlua_error(L, "Filter table too large (len > %d)", MAX_STK_FILTER_LEN); + if (filter_count >= STKTABLE_FILTER_LEN) + return hlua_error(L, "Filter table too large (len > %d)", STKTABLE_FILTER_LEN); if (lua_type(L, -1) != LUA_TTABLE || lua_rawlen(L, -1) != 3) return hlua_error(L, "Filter table entry must be a triplet: {\"data_col\", \"op\", val} (entry #%d)", filter_count + 1); diff --git a/src/stick_table.c b/src/stick_table.c index 7b648475b0..1393b1ff36 100644 --- a/src/stick_table.c +++ b/src/stick_table.c @@ -3600,23 +3600,29 @@ static int table_process_entry_per_key(struct appctx *appctx, char **args) */ static int table_prepare_data_request(struct appctx *appctx, char **args) { + int i; + if (appctx->ctx.table.action != STK_CLI_ACT_SHOW && appctx->ctx.table.action != STK_CLI_ACT_CLR) return cli_err(appctx, "content-based lookup is only supported with the \"show\" and \"clear\" actions\n"); - /* condition on stored data value */ - appctx->ctx.table.data_type = stktable_get_data_type(args[3] + 5); - if (appctx->ctx.table.data_type < 0) - return cli_err(appctx, "Unknown data type\n"); + for (i = 0; i < STKTABLE_FILTER_LEN; i++) { + if (i > 0 && !*args[3+3*i]) // number of filter entries can be less than STKTABLE_FILTER_LEN + break; + /* condition on stored data value */ + appctx->ctx.table.data_type[i] = stktable_get_data_type(args[3+3*i] + 5); + if (appctx->ctx.table.data_type[i] < 0) + return cli_err(appctx, "Unknown data type\n"); - if (!((struct stktable *)appctx->ctx.table.target)->data_ofs[appctx->ctx.table.data_type]) - return cli_err(appctx, "Data type not stored in this table\n"); + if (!((struct stktable *)appctx->ctx.table.target)->data_ofs[appctx->ctx.table.data_type[i]]) + return cli_err(appctx, "Data type not stored in this table\n"); - appctx->ctx.table.data_op = get_std_op(args[4]); - if (appctx->ctx.table.data_op < 0) - return cli_err(appctx, "Require and operator among \"eq\", \"ne\", \"le\", \"ge\", \"lt\", \"gt\"\n"); + appctx->ctx.table.data_op[i] = get_std_op(args[4+3*i]); + if (appctx->ctx.table.data_op < 0) + return cli_err(appctx, "Require and operator among \"eq\", \"ne\", \"le\", \"ge\", \"lt\", \"gt\"\n"); - if (!*args[5] || strl2llrc(args[5], strlen(args[5]), &appctx->ctx.table.value) != 0) - return cli_err(appctx, "Require a valid integer value to compare against\n"); + if (!*args[5] || strl2llrc(args[5+3*i], strlen(args[5+3*i]), &appctx->ctx.table.value[i]) != 0) + return cli_err(appctx, "Require a valid integer value to compare against\n"); + } /* OK we're done, all the fields are set */ return 0; @@ -3625,7 +3631,10 @@ static int table_prepare_data_request(struct appctx *appctx, char **args) /* returns 0 if wants to be called, 1 if has ended processing */ static int cli_parse_table_req(char **args, char *payload, struct appctx *appctx, void *private) { - appctx->ctx.table.data_type = -1; + int i; + + for (i = 0; i < STKTABLE_FILTER_LEN; i++) + appctx->ctx.table.data_type[i] = -1; appctx->ctx.table.target = NULL; appctx->ctx.table.entry = NULL; appctx->ctx.table.action = (long)private; // keyword argument, one of STK_CLI_ACT_* @@ -3672,7 +3681,6 @@ static int cli_io_handler_table(struct appctx *appctx) struct stream_interface *si = appctx->owner; struct stream *s = si_strm(si); struct ebmb_node *eb; - int dt; int skip_entry; int show = appctx->ctx.table.action == STK_CLI_ACT_SHOW; @@ -3744,48 +3752,53 @@ static int cli_io_handler_table(struct appctx *appctx) HA_RWLOCK_RDLOCK(STK_SESS_LOCK, &appctx->ctx.table.entry->lock); - if (appctx->ctx.table.data_type >= 0) { + if (appctx->ctx.table.data_type[0] >= 0) { /* we're filtering on some data contents */ void *ptr; - long long data; + int dt; + signed char op; + long long data, value; - dt = appctx->ctx.table.data_type; - ptr = stktable_data_ptr(appctx->ctx.table.t, - appctx->ctx.table.entry, - dt); + for (int i = 0; i < STKTABLE_FILTER_LEN; i++) { + if (appctx->ctx.table.data_type[i] == -1) + break; + dt = appctx->ctx.table.data_type[i]; + ptr = stktable_data_ptr(appctx->ctx.table.t, + appctx->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), + appctx->ctx.table.t->data_arg[dt].u); + break; + } - 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), - appctx->ctx.table.t->data_arg[dt].u); - break; + op = appctx->ctx.table.data_op[i]; + value = appctx->ctx.table.value[i]; + + /* skip the entry if the data does not match the test and the value */ + if ((data < value && + (op == STD_OP_EQ || op == STD_OP_GT || op == STD_OP_GE)) || + (data == value && + (op == STD_OP_NE || op == STD_OP_GT || op == STD_OP_LT)) || + (data > value && + (op == STD_OP_EQ || op == STD_OP_LT || op == STD_OP_LE))) { + skip_entry = 1; + break; + } } - - /* skip the entry if the data does not match the test and the value */ - if ((data < appctx->ctx.table.value && - (appctx->ctx.table.data_op == STD_OP_EQ || - appctx->ctx.table.data_op == STD_OP_GT || - appctx->ctx.table.data_op == STD_OP_GE)) || - (data == appctx->ctx.table.value && - (appctx->ctx.table.data_op == STD_OP_NE || - appctx->ctx.table.data_op == STD_OP_GT || - appctx->ctx.table.data_op == STD_OP_LT)) || - (data > appctx->ctx.table.value && - (appctx->ctx.table.data_op == STD_OP_EQ || - appctx->ctx.table.data_op == STD_OP_LT || - appctx->ctx.table.data_op == STD_OP_LE))) - skip_entry = 1; } if (show && !skip_entry && -- 2.39.5