On 04/03/2025 12:50, Roi Dayan wrote:
> From: Dima Chumak <[email protected]>
> 
> The 'fdb/show' command now supports machine-readable JSON output in
> addition to the plain-text output for humans. An example would be:
> 
>   ovs-appctl --format=json --pretty fdb/show br-phy
>   [
>     {
>       "static": true,
>       "mac": "e4:8c:07:08:00:02",
>       "port": 2,
>       "vlan": 0
>     },
>     {
>       "age": 14,
>       "mac": "e4:8c:07:08:00:03",
>       "port": 3,
>       "vlan": 0
>     }
>   ]
> 
> Signed-off-by: Dima Chumak <[email protected]>
> Acked-by: Roi Dayan <[email protected]>
> ---
> 
> Notes:
>     v6
>     - Move hmap_count under lock.
>     - Assert if item count does not match.
>     - Change test to use regex * instead of \+.
>     
>     v5
>     - Forgot to remove the wrong string output of age static.
>     - Added test case for static entry. The two testsuite case numbers are 
> 1279 1282.
>     
>     v4
>     - Make sure not to set more entries than allocated in json output.
>     - Fix json output to have either boolean static or integer age and not 
> mix the types.
>     
>     v3
>     - Fix assert. No need to call json_destroy() after 
> unixctl_command_reply_json() as it takes
>       ownership of the allocated body.
>     
>     v2
>     - Fix sparse check error.
>     - Add case checking fdb/show json output.
> 
>  ofproto/ofproto-dpif.c | 90 ++++++++++++++++++++++++++++++++++--------
>  tests/ofproto-dpif.at  | 42 ++++++++++++++++++++
>  2 files changed, 116 insertions(+), 16 deletions(-)
> 
> diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
> index bf43d5d4bc59..25b1d932259f 100644
> --- a/ofproto/ofproto-dpif.c
> +++ b/ofproto/ofproto-dpif.c
> @@ -6151,20 +6151,12 @@ ofbundle_get_a_port(const struct ofbundle *bundle)
>  }
>  
>  static void
> -ofproto_unixctl_fdb_show(struct unixctl_conn *conn, int argc OVS_UNUSED,
> -                         const char *argv[], void *aux OVS_UNUSED)
> +ofproto_unixctl_fdb_show_text(const struct ofproto_dpif *ofproto,
> +                              struct ds *ds)
>  {
> -    struct ds ds = DS_EMPTY_INITIALIZER;
> -    const struct ofproto_dpif *ofproto;
>      const struct mac_entry *e;
>  
> -    ofproto = ofproto_dpif_lookup_by_name(argv[1]);
> -    if (!ofproto) {
> -        unixctl_command_reply_error(conn, "no such bridge");
> -        return;
> -    }
> -
> -    ds_put_cstr(&ds, " port  VLAN  MAC                Age\n");
> +    ds_put_cstr(ds, " port  VLAN  MAC                Age\n");
>      ovs_rwlock_rdlock(&ofproto->ml->rwlock);
>      LIST_FOR_EACH (e, lru_node, &ofproto->ml->lrus) {
>          struct ofbundle *bundle = mac_entry_get_port(ofproto->ml, e);
> @@ -6173,17 +6165,83 @@ ofproto_unixctl_fdb_show(struct unixctl_conn *conn, 
> int argc OVS_UNUSED,
>  
>          ofputil_port_to_string(ofbundle_get_a_port(bundle)->up.ofp_port,
>                  NULL, name, sizeof name);
> -        ds_put_format(&ds, "%5s  %4d  "ETH_ADDR_FMT"  ",
> +        ds_put_format(ds, "%5s  %4d  "ETH_ADDR_FMT"  ",
>                  name, e->vlan, ETH_ADDR_ARGS(e->mac));
>          if (MAC_ENTRY_AGE_STATIC_ENTRY == age) {
> -            ds_put_format(&ds, "static\n");
> +            ds_put_format(ds, "static\n");
>          } else {
> -            ds_put_format(&ds, "%3d\n", age);
> +            ds_put_format(ds, "%3d\n", age);
>          }
>      }
>      ovs_rwlock_unlock(&ofproto->ml->rwlock);
> -    unixctl_command_reply(conn, ds_cstr(&ds));
> -    ds_destroy(&ds);
> +}
> +
> +static void
> +ofproto_unixctl_fdb_show_json(const struct ofproto_dpif *ofproto,
> +                              struct json **fdb_entries)
> +{
> +    struct json **json_entries = NULL;
> +    const struct mac_entry *entry;
> +    size_t num_entries;
> +    int i = 0;
> +
> +    ovs_rwlock_rdlock(&ofproto->ml->rwlock);
> +
> +    num_entries = hmap_count(&ofproto->ml->table);
> +    if (!num_entries) {
> +        goto done_unlock;
> +    }
> +
> +    json_entries = xmalloc(num_entries * sizeof *json_entries);
> +
> +    LIST_FOR_EACH (entry, lru_node, &ofproto->ml->lrus) {
> +        struct ofbundle *bundle = mac_entry_get_port(ofproto->ml, entry);
> +        struct ofport_dpif *port = ofbundle_get_a_port(bundle);
> +        struct json *json_entry = json_object_create();
> +        int age = mac_entry_age(ofproto->ml, entry);
> +
> +        ovs_assert(i < num_entries);
> +        json_object_put(json_entry, "port",
> +                        json_integer_create(
> +                            (OVS_FORCE long long) port->up.ofp_port));
> +        json_object_put(json_entry, "vlan", 
> json_integer_create(entry->vlan));
> +        json_object_put_format(json_entry, "mac", ETH_ADDR_FMT,
> +                               ETH_ADDR_ARGS(entry->mac));
> +        if (MAC_ENTRY_AGE_STATIC_ENTRY == age) {
> +            json_object_put(json_entry, "static", json_boolean_create(true));
> +        } else {
> +            json_object_put(json_entry, "age", json_integer_create(age));
> +        }
> +        json_entries[i++] = json_entry;
> +    }
> +done_unlock:
> +    ovs_rwlock_unlock(&ofproto->ml->rwlock);
> +    *fdb_entries = json_array_create(json_entries, i);
> +}
> +
> +static void
> +ofproto_unixctl_fdb_show(struct unixctl_conn *conn, int argc OVS_UNUSED,
> +                          const char *argv[] OVS_UNUSED, void *aux 
> OVS_UNUSED)
> +{
> +    const struct ofproto_dpif *ofproto = 
> ofproto_dpif_lookup_by_name(argv[1]);
> +
> +    if (!ofproto) {
> +        unixctl_command_reply_error(conn, "no such bridge");
> +        return;
> +    }
> +
> +    if (unixctl_command_get_output_format(conn) == UNIXCTL_OUTPUT_FMT_JSON) {
> +        struct json *fdb_entries;
> +
> +        ofproto_unixctl_fdb_show_json(ofproto, &fdb_entries);
> +        unixctl_command_reply_json(conn, fdb_entries);
> +    } else {
> +        struct ds ds = DS_EMPTY_INITIALIZER;
> +
> +        ofproto_unixctl_fdb_show_text(ofproto, &ds);
> +        unixctl_command_reply(conn, ds_cstr(&ds));
> +        ds_destroy(&ds);
> +    }
>  }
>  
>  static void
> diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at
> index fa5f148b4c28..e0ae42d77c7e 100644
> --- a/tests/ofproto-dpif.at
> +++ b/tests/ofproto-dpif.at
> @@ -7100,6 +7100,22 @@ AT_CHECK_UNQUOTED([ovs-appctl fdb/show br0 | sed 
> 's/[[0-9]]\{1,\}$/?/'], [0], [d
>      1     0  50:54:00:00:00:06    ?
>  ])
>  
> +dnl Check json output.
> +AT_CHECK([ovs-appctl --format json --pretty fdb/show br0 \
> +          | sed 's/"age": [[0-9]]*/"age": ?/g'], [0], [dnl
> +[[
> +  {
> +    "age": ?,
> +    "mac": "50:54:00:00:00:05",
> +    "port": 3,
> +    "vlan": 0},
> +  {
> +    "age": ?,
> +    "mac": "50:54:00:00:00:06",
> +    "port": 1,
> +    "vlan": 0}]]
> +])
> +
>  # Trace a packet arrival that updates the first learned MAC entry.
>  OFPROTO_TRACE(
>    [ovs-dummy],
> @@ -7708,6 +7724,32 @@ AT_CHECK_UNQUOTED([ovs-appctl fdb/show br0 | sed 's/ 
> *[[0-9]]\{1,\}$//' | grep -
>      2     0  50:54:00:00:02:02  static
>  ])
>  
> +dnl Check json output.
> +AT_CHECK([ovs-appctl --format json --pretty fdb/show br0 \
> +          | sed 's/"age": [[0-9]]\+/"age": ?/g'], [0], [dnl

forgot to change this case to *. i'll send v7.

> +[[
> +  {
> +    "age": ?,
> +    "mac": "50:54:00:00:00:01",
> +    "port": 1,
> +    "vlan": 0},
> +  {
> +    "age": ?,
> +    "mac": "50:54:00:00:00:02",
> +    "port": 2,
> +    "vlan": 0},
> +  {
> +    "mac": "50:54:00:00:01:01",
> +    "port": 1,
> +    "static": true,
> +    "vlan": 0},
> +  {
> +    "mac": "50:54:00:00:02:02",
> +    "port": 2,
> +    "static": true,
> +    "vlan": 0}]]
> +])
> +
>  dnl Remove static mac entry.
>  AT_CHECK([ovs-appctl fdb/del br0 0 50:54:00:00:01:01])
>  

_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to