2
0
mirror of https://github.com/openvswitch/ovs synced 2025-08-22 01:51:26 +00:00

db-ctl-base: Added filter option in show command.

The --filter option allows filtering output in two ways:
1. Basic filtering (comma-separated list, e.g., 'filter1,filter2').
2. Recursive filtering by table (e.g., 'interface(geneve)').

Signed-off-by: Alexandra Rukomoinikova <arukomoinikova@k2.cloud>
Signed-off-by: Ilya Maximets <i.maximets@ovn.org>
This commit is contained in:
Alexandra Rukomoinikova 2025-07-15 13:29:07 +03:00 committed by Ilya Maximets
parent 943f9096e2
commit 6f2ca3fb26
4 changed files with 218 additions and 6 deletions

1
NEWS
View File

@ -26,6 +26,7 @@ Post-v3.5.0
ovs-vswitchd to finish reconfiguring itself after a successful
database transaction. E.g., when ovs-vswitchd fails to add a new
port or a bridge.
* Added '--filter' option to the 'show' command.
- SSL/TLS:
* Support for deprecated TLSv1 and TLSv1.1 protocols on OpenFlow and
database connections is now removed.

View File

@ -2143,14 +2143,40 @@ cmd_show_weak_ref(struct ctl_context *ctx, const struct cmd_show_table *show,
}
}
static void
table_filter(struct ctl_context *ctx,
const struct cmd_show_table *show,
const struct shash *filters, size_t base_length)
{
if (!show || !show->table || shash_is_empty(filters)) {
return;
}
const char *table_name = show->table->name;
struct sset *table_filter = shash_find_data(filters, table_name);
if (table_filter) {
const char *output = &ctx->output.string[base_length];
const char *value;
SSET_FOR_EACH (value, table_filter) {
if (strcasestr(output, value)) {
return;
}
}
ds_truncate(&ctx->output, base_length);
}
}
/* 'shown' records the tables that has been displayed by the current
* command to avoid duplicated prints.
*/
static void
cmd_show_row(struct ctl_context *ctx, const struct ovsdb_idl_row *row,
int level, struct sset *shown)
int level, struct sset *shown, const struct shash *filters)
{
const struct cmd_show_table *show = cmd_show_find_table_by_row(row);
size_t start_pos = ctx->output.length;
size_t i;
ds_put_char_multiple(&ctx->output, ' ', level * 4);
@ -2194,7 +2220,7 @@ cmd_show_row(struct ctl_context *ctx, const struct ovsdb_idl_row *row,
ref_show->table,
&datum->keys[j].uuid);
if (ref_row) {
cmd_show_row(ctx, ref_row, level + 1, shown);
cmd_show_row(ctx, ref_row, level + 1, shown, filters);
}
}
continue;
@ -2249,6 +2275,85 @@ cmd_show_row(struct ctl_context *ctx, const struct ovsdb_idl_row *row,
}
cmd_show_weak_ref(ctx, show, row, level);
sset_find_and_delete_assert(shown, show->table->name);
table_filter(ctx, show, filters, start_pos);
}
static char * OVS_WARN_UNUSED_RESULT
init_filters(const char *filter_str,
const struct ovsdb_idl_table_class *top_table,
struct shash *filters)
{
struct sset initialized_filters;
char *error = NULL;
const char *item;
sset_init(&initialized_filters);
sset_from_delimited_string(&initialized_filters, filter_str, ",");
SSET_FOR_EACH (item, &initialized_filters) {
const struct ovsdb_idl_table_class *table;
const char *ptr = strchr(item, '(');
size_t len = strlen(item);
char *table_name = NULL;
char *values = NULL;
struct sset parsed_values;
sset_init(&parsed_values);
if (ptr && item[len - 1] == ')') {
table_name = xmemdup0(item, ptr - item);
error = get_table(table_name, &table);
if (error) {
goto cleanup_item;
}
values = xmemdup0(ptr + 1, len - (ptr - item + 2));
} else if (!ptr) {
table = top_table;
values = xstrdup(item);
} else {
error = xasprintf("Malformed filter: missing closing ')'");
goto cleanup_item;
}
sset_from_delimited_string(&parsed_values, values, "|");
struct sset *value_set = shash_find_data(filters, table->name);
if (!value_set) {
value_set = xzalloc(sizeof *value_set);
sset_init(value_set);
shash_add(filters, table->name, value_set);
}
const char *v;
SSET_FOR_EACH (v, &parsed_values) {
sset_add(value_set, v);
}
cleanup_item:
sset_destroy(&parsed_values);
free(table_name);
free(values);
if (error) {
goto cleanup;
}
}
cleanup:
sset_destroy(&initialized_filters);
return error;
}
static void
destroy_filters(struct shash *filters)
{
struct shash_node *node;
SHASH_FOR_EACH (node, filters) {
struct sset *value_set = node->data;
sset_destroy(value_set);
free(value_set);
}
shash_destroy(filters);
}
static void
@ -2256,12 +2361,25 @@ cmd_show(struct ctl_context *ctx)
{
const struct ovsdb_idl_row *row;
struct sset shown = SSET_INITIALIZER(&shown);
struct shash filters = SHASH_INITIALIZER(&filters);
char *filter_str = shash_find_data(&ctx->options, "--filter");
if (filter_str && *filter_str) {
char *error = init_filters(filter_str,
cmd_show_tables[0].table, &filters);
if (error) {
destroy_filters(&filters);
ctx->error = error;
return;
}
}
for (row = ovsdb_idl_first_row(ctx->idl, cmd_show_tables[0].table);
row; row = ovsdb_idl_next_row(row)) {
cmd_show_row(ctx, row, 0, &shown);
cmd_show_row(ctx, row, 0, &shown, &filters);
}
destroy_filters(&filters);
ovs_assert(sset_is_empty(&shown));
sset_destroy(&shown);
}
@ -2556,7 +2674,7 @@ ctl_init__(const struct ovsdb_idl_class *idl_class_,
cmd_show_tables = cmd_show_tables_;
if (cmd_show_tables) {
static const struct ctl_command_syntax show =
{"show", 0, 0, "", pre_cmd_show, cmd_show, NULL, "", RO};
{"show", 0, 0, "", pre_cmd_show, cmd_show, NULL, "--filter=", RO};
ctl_register_command(&show);
}
}

View File

@ -1859,3 +1859,92 @@ OVS_VSWITCHD_STOP(["/is not a valid IP address/d
/netdev_vport|WARN|gre0: bad ip6gre 'remote_ip'/d
/netdev|WARN|gre0: could not set configuration/d"])
AT_CLEANUP
AT_SETUP([ovs-vsctl filter option usage])
AT_KEYWORDS([ovs-vsctl filter option])
OVS_VSWITCHD_START([dnl
add-port br0 p1 -- set Interface p1 type=internal ofport_request=1 -- \
add-port br0 tunnel_port \
-- set Interface tunnel_port type=geneve \
options:remote_ip=1.2.3.4 options:key=123 -- \
add-port br0 tunnel_port2 \
-- set Interface tunnel_port2 type=geneve \
options:remote_ip=1.2.3.5 options:key=125
])
m4_define([FILTER], [grep -E '(Port|Interface)' | sort])
AT_CHECK([ovs-vsctl --filter='geneve,interface(1.2.3.5)' show | FILTER], [0], [dnl
Interface tunnel_port2
Port br0
Port p1
Port tunnel_port
Port tunnel_port2
])
AT_CHECK([ovs-vsctl --filter='interface(1.2.3.5),geneve' show | FILTER], [0], [dnl
Interface tunnel_port2
Port br0
Port p1
Port tunnel_port
Port tunnel_port2
])
AT_CHECK([ovs-vsctl --filter='interface(1.2.3.5)' show | FILTER], [0], [dnl
Interface tunnel_port2
Port br0
Port p1
Port tunnel_port
Port tunnel_port2
])
AT_CHECK([ovs-vsctl --filter='geneve' show | FILTER], [0], [dnl
Interface br0
Interface p1
Interface tunnel_port
Interface tunnel_port2
Port br0
Port p1
Port tunnel_port
Port tunnel_port2
])
AT_CHECK([ovs-vsctl --filter='port(geneve),interface(1.2.3.5)' show | FILTER], [0], [dnl
Interface tunnel_port2
Port tunnel_port2
])
AT_CHECK([ovs-vsctl --filter='port(geneve),interface(123|125)' show | FILTER], [0], [dnl
Interface tunnel_port
Interface tunnel_port2
Port tunnel_port
Port tunnel_port2
])
AT_CHECK([ovs-vsctl --filter='interface(geneve)' show | FILTER], [0], [dnl
Interface tunnel_port
Interface tunnel_port2
Port br0
Port p1
Port tunnel_port
Port tunnel_port2
])
AT_CHECK([ovs-vsctl --filter='interface(p1),interface(p2)' show | FILTER], [0], [dnl
Interface p1
Port br0
Port p1
Port tunnel_port
Port tunnel_port2
])
AT_CHECK([ovs-vsctl --filter='interface1(p1)' show], [1], [stdout], [dnl
ovs-vsctl: unknown table "interface1"
])
AT_CHECK([ovs-vsctl --filter=$'interface\x28p1' show], [1], [stdout], [dnl
ovs-vsctl: Malformed filter: missing closing ')'
])
AT_CLEANUP

View File

@ -150,8 +150,12 @@ Any successful \fBovs\-vsctl\fR command automatically initializes the
Open vSwitch database if it is empty. This command is provided to
initialize the database without executing any other command.
.
.IP "\fBshow\fR"
Prints a brief overview of the database contents.
.IP "[\fB\-\-filter\fR=\fIfilter\-rule\fR[,\fIfilter\-rule\fR]...] \fBshow\fR"
Prints a brief overview of the database contents. If \fB\-\-filter\fR is
specified, output is filtered according to the rules. Each \fIfilter\-rule\fR
has the form \fBtable\-name\fR(\fIfilter\fR[|\fIfilter\fR]...). The row in
this table is shown only if the printed version of it, including all the rows
referenced from this one, contains one of the \fIfilter\fRs as a sub-string.
.
.IP "\fBemer\-reset\fR"
Reset the configuration into a clean state. It deconfigures OpenFlow