]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: cli: Allow multiple filter entries for "show table"
authorAdis Nezirovic <anezirovic@haproxy.com>
Thu, 16 Jan 2020 14:19:29 +0000 (15:19 +0100)
committerWilly Tarreau <w@1wt.eu>
Wed, 22 Jan 2020 13:33:17 +0000 (14:33 +0100)
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
include/common/defaults.h
include/types/applet.h
src/hlua_fcn.c
src/stick_table.c

index 521a6711218376cc51819c6264bd040b1b22ed46..24969be8870d98813bbb7c71cef8445a6c9a9bc2 100644 (file)
@@ -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 <name> [ data.<type> <operator> <value> ] | [ key <key> ]
+show table <name> [ data.<type> <operator> <value> [data.<type> ...]] | [ key <key> ]
   Dump contents of stick-table <name>. 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 <name> [ data.<type> <operator> <value> ] | [ key <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 <key> is shown.  The key must be of the
   same type as the table, which currently is limited to IPv4, IPv6, integer,
index fbacca481e7f92d101ec47300762155ecdc97581..e86c9bce131f5b457bfdc35cfea3709e66485b24 100644 (file)
 #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
index dfb489c9f1692bc7dccb65410ed3154c7c408ee5..76a598d21e3b381595d3e180f2cced16f762b7cd 100644 (file)
@@ -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 {
index 7ef708ccbdf094a60505aa6bb7c98f5528c35595..f8024aa84986106751ed798830b8174c121c48fd 100644 (file)
@@ -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);
index 7b648475b080d56ec4d5030fe3a4b6e152ed3d72..1393b1ff362a5a76a1e0e48dc8fc6645b2d70027 100644 (file)
@@ -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 &&