On 5/20/25 7:16 PM, Alexandra Rukomoinikova wrote: > Added the ability to filter the output of the show command > using the filter option. > > Signed-off-by: Alexandra Rukomoinikova <arukomoinikova@k2.cloud> > --- > v1 --> v2 : did as Ilya said. > --- > lib/db-ctl-base.c | 75 +++++++++++++++++++++++++++++++++++++++++++++- > tests/ovs-vsctl.at | 28 +++++++++++++++++ > 2 files changed, 102 insertions(+), 1 deletion(-)
Hi, Alexandra. Thanks for the new version! nit: The 'lib:' in the patch name doesn't make a lot of sense as vast majority of OVS code is in lib. It should be 'db-ctl-base:' instead. Since this patch adds a new option, it needs to be documented in the man page (utilities/ovs-vsctl.8.in) and a NEWS entry should be added. Some more comments below. > > diff --git a/lib/db-ctl-base.c b/lib/db-ctl-base.c > index 1f157e46c..45d2d51c0 100644 > --- a/lib/db-ctl-base.c > +++ b/lib/db-ctl-base.c > @@ -2243,19 +2243,92 @@ cmd_show_row(struct ctl_context *ctx, const struct > ovsdb_idl_row *row, > sset_find_and_delete_assert(shown, show->table->name); > } > > +struct output_filter { > + char **filters; > + size_t n_filters; > +}; > + > +static void > +filter_row(struct ctl_context *ctx, > + struct output_filter *filter, > + size_t old_lenght) s/lenght/length/ here and in a few other places in the code. > +{ > + bool filter_match = false; > + > + for (size_t i = 0; i < filter->n_filters; i++) { > + if (strstr(&ctx->output.string[old_lenght], filter->filters[i])) { > + filter_match = true; break here. There is no need to continue the loop. > + } > + } > + > + if (!filter_match) { > + ds_truncate(&ctx->output, old_lenght); > + } > +} > + > +static struct output_filter * > +init_filters(struct ctl_context *ctx) > +{ > + char *filter_str = shash_find_data(&ctx->options, "--filter"); > + if (!filter_str || !*filter_str) { > + return NULL; > + } > + > + size_t count = 1; > + for (const char *p = filter_str; *p; p++) { > + if (*p == '|') { > + count++; > + } > + } > + > + struct output_filter *filter = xmalloc(sizeof(struct output_filter)); > + filter->filters = xmalloc(count * (sizeof(char *))); > + filter->n_filters = count; > + > + char *ptr = NULL; > + char *token = strtok_r(filter_str, "|", &ptr); > + size_t idx = 0; > + > + while (token && idx < count) { > + filter->filters[idx++] = xstrdup(token); > + token = strtok_r(NULL, "|", &ptr); > + } > + > + return filter; > +} If we use sset as a data structure for the filters, the code above can be mostly replaced with an sset_from_delimited_string() call. The sset is slightly less memory-efficient, but I don't think we care in this particular case. > + > +static void > +free_filters(struct output_filter *filter) > +{ > + if (!filter) { > + return; > + } > + for (size_t i = 0; i < filter->n_filters; i++) { > + free(filter->filters[i]); > + } > + free(filter->filters); > + free(filter); > +} > + > static void > cmd_show(struct ctl_context *ctx) > { > const struct ovsdb_idl_row *row; > + struct output_filter *filter = init_filters(ctx); > struct sset shown = SSET_INITIALIZER(&shown); > > for (row = ovsdb_idl_first_row(ctx->idl, cmd_show_tables[0].table); > row; row = ovsdb_idl_next_row(row)) { > + size_t lenght_before = ctx->output.length; > cmd_show_row(ctx, row, 0, &shown); > + if (filter) { > + filter_row(ctx, filter, lenght_before); > + } This only covers the top-level elements, but the idea was to perform the filtering recursively for every level. You should move the filtering into the cmd_show_row() itself, i.e. save the length at the top of cmd_show_row() and run the filtering at the bottom. This will ensure that we're filtering all the way through. For example, if we have a setup like this: $ ovs-vsctl add-br br1 $ ovs-vsctl add-br br0 $ ovs-vsctl add-port br0 p1 $ ovs-vsctl add-port br0 tunnel_port \ -- set Interface tunnel_port type=geneve \ options:remote_ip=flow options:key=123 $ ovs-vsctl add-port br0 tunnel_port2 \ -- set Interface tunnel_port2 type=geneve \ options:remote_ip=flow options:key=125 Then I can get a list of bridges only with: $ ovs-vsctl --filter="Bridge" show d55f21cd-a59a-4548-b1bc-fc165b0ec0a0 Bridge br1 Bridge br0 Since the 'Bridge' match happens at the higher level, all the port and interface information is filtered out. Or a list of ports per bridge: $ ovs-vsctl --filter="Port" show d55f21cd-a59a-4548-b1bc-fc165b0ec0a0 Bridge br1 Port br1 Bridge br0 Port p1 Port tunnel_port Port tunnel_port2 Port br0 Here only the interface information is filtered out since once we match on the 'Port' string, all the higher-level nodes will match as well. Or if I want to only show geneve interfaces: $ ovs-vsctl --filter="geneve" show d55f21cd-a59a-4548-b1bc-fc165b0ec0a0 Bridge br0 Port tunnel_port Interface tunnel_port type: geneve options: {key="123", remote_ip=flow} Port tunnel_port2 Interface tunnel_port2 type: geneve options: {key="125", remote_ip=flow} Or if I want to show all tunnels with the tunnel key 123: $ ovs-vsctl --filter='key="123"' show d55f21cd-a59a-4548-b1bc-fc165b0ec0a0 Bridge br0 Port tunnel_port Interface tunnel_port type: geneve options: {key="123", remote_ip=flow} Or I want the previous one, but also names of all the ports per bridge: $ ovs-vsctl --filter='key="123"|Port' show d55f21cd-a59a-4548-b1bc-fc165b0ec0a0 Bridge br1 Port br1 Bridge br0 Port p1 Port tunnel_port Interface tunnel_port type: geneve options: {key="123", remote_ip=flow} Port tunnel_port2 Port br0 What do you think? > } > > ovs_assert(sset_is_empty(&shown)); > sset_destroy(&shown); > + free_filters(filter); > } > > > @@ -2548,7 +2621,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); > } > } > diff --git a/tests/ovs-vsctl.at b/tests/ovs-vsctl.at > index e488e292d..010889789 100644 > --- a/tests/ovs-vsctl.at > +++ b/tests/ovs-vsctl.at > @@ -1831,3 +1831,31 @@ AT_CHECK([ovs-vsctl --no-wait --bare --columns > _uuid,name list bridge tst1], [0] > > OVS_VSCTL_CLEANUP > AT_CLEANUP > + > +AT_SETUP([ovs-vsctl filter option usage]) nit: I'd name it 'ovs-vsctl show filtered' or something like that. > +AT_KEYWORDS([ovs-vsctl filter option]) nit :I'd replace the 'option' with 'show'. 'option' is a not very specific keyword to use. > + > +OVS_VSWITCHD_START([dnl > + add-port br0 p1 -- set Interface p1 type=internal ofport_request=1 -- \ > + add-port br0 p2 -- set Interface p2 type=internal ofport_request=2 -- \ > + add-port br0 p3 -- set Interface p3 type=internal ofport_request=3 > +]) > + > +AT_CHECK([ovs-vsctl --filter='p1' show | grep Port | sort], [0], [dnl > + Port br0 > + Port p1 > + Port p2 > + Port p3 > +]) > + > +AT_CHECK([ovs-vsctl --filter='test' show | grep Port | sort], [0], []) > + > +AT_CHECK([ovs-vsctl --filter='Bridge' show | grep Port | sort], [0], [dnl > + Port br0 > + Port p1 > + Port p2 > + Port p3 > +]) We should print the whole thing here. It will not change from run to run, because database structures are sorted and our top-level table only has one row. The only thing that will change is the datapath uuid at the very top. You may just cut out the first line with tail -n +2 or feed the output into the uuidfilt. Maybe add a bit more variety to the tests. Different interface types. > + > +OVS_VSCTL_CLEANUP > +AT_CLEANUP > \ No newline at end of file Add the new line here! Best regards, Ilya Maximets. _______________________________________________ dev mailing list d...@openvswitch.org https://mail.openvswitch.org/mailman/listinfo/ovs-dev