On 5/29/25 6:02 PM, Ilya Maximets wrote:
> 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.

Ignore that part, we do not control the UUIDs of referenced rows, so the output
order will change between runs, unfortunately.

> 
> 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
> 

_______________________________________________
dev mailing list
d...@openvswitch.org
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to