On 2 Apr 2026, at 14:28, Ilya Maximets wrote:
> On 4/2/26 11:19 AM, Eelco Chaudron via dev wrote:
>>
>>
>> On 13 Mar 2026, at 21:50, Timothy Redaelli via dev wrote:
>>
>>> When --format json is passed to ovs-appctl, fdb/stats-show returns a
>>> flat JSON object with the bridge name and all counters from the MAC
>>> learning table rather than the existing text representation.
>>>
>>> Example output:
>>> {"bridge":"br0","current_entries":17,"max_entries":8192,
>>> "static_entries":17,"total_expired":0,"total_evicted":0,
>>> "total_learned":17,"total_moved":0}
>
> General note for all patches in the set:
>
> 1. Don't use underscores in JSON output. Always use dashes.
>
> 2. Not applicable to this patch, but in other patches, use full words
> whenever possible, i.e. s/pkts/packets/, except for some things
> like 'ms' for milliseconds, which are acceptable short names.
> There is no point in seving a little bit of space in JSON.
>
> 3. All of the patches need a NEW entry.
>
>>>
>>> Reported-at: https://issues.redhat.com/browse/FDP-2444
>>> Signed-off-by: Timothy Redaelli <[email protected]>
>>
>> Hi Timothy,
>>
>> Thanks for the series. I have a few comments on this patch below.
>>
>> Cheers,
>>
>>
>> Eelco
>>
>>> ---
>>> ofproto/ofproto-dpif.c | 52 +++++++++++++++++++++++++++++++-----------
>>> tests/ofproto-dpif.at | 9 ++++++++
>>> 2 files changed, 48 insertions(+), 13 deletions(-)
>>>
>>> diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
>>> index a02afe8ef..add77ca4c 100644
>>> --- a/ofproto/ofproto-dpif.c
>>> +++ b/ofproto/ofproto-dpif.c
>>> @@ -6388,7 +6388,6 @@ static void
>>> ofproto_unixctl_fdb_stats_show(struct unixctl_conn *conn, int argc
>>> OVS_UNUSED,
>>> const char *argv[], void *aux OVS_UNUSED)
>>> {
>>> - struct ds ds = DS_EMPTY_INITIALIZER;
>>> const struct ofproto_dpif *ofproto;
>>> ofproto = ofproto_dpif_lookup_by_name(argv[1]);
>>> if (!ofproto) {
>>> @@ -6396,27 +6395,54 @@ ofproto_unixctl_fdb_stats_show(struct unixctl_conn
>>> *conn, int argc OVS_UNUSED,
>>> return;
>>> }
>>>
>>> - ds_put_format(&ds, "Statistics for bridge \"%s\":\n", argv[1]);
>>> ovs_rwlock_rdlock(&ofproto->ml->rwlock);
>>> + if (unixctl_command_get_output_format(conn) ==
>>> UNIXCTL_OUTPUT_FMT_JSON) {
>>
>>
>> The current output looks like this:
>>
>> {
>> "bridge": "br0",
>> "current_entries": 17,
>> "max_entries": 8192,
>> "static_entries": 17,
>> "total_evicted": 0,
>> "total_expired": 0,
>> "total_learned": 18,
>> "total_moved": 0}
>>
>> Where it should look something like this, this clearly shows the data is
>> related to br0:
>>
>> "br0": {
>> "current_entries": 17,
>> "max_entries": 8192,
>> "static_entries": 17,
>> "total_evicted": 0,
>> "total_expired": 0,
>> "total_learned": 18,
>> "total_moved": 0}}
>>
>>
>> In addition can we split up the functions, rather than inlining it.
>> It's cleaner this way. Here's a quick diff:
>>
>> diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
>> index add77ca4c..f62bff605 100644
>> --- a/ofproto/ofproto-dpif.c
>> +++ b/ofproto/ofproto-dpif.c
>> @@ -6385,68 +6385,87 @@ ofproto_unixctl_fdb_stats_clear(struct unixctl_conn
>> *conn, int argc,
>> }
>>
>> static void
>> -ofproto_unixctl_fdb_stats_show(struct unixctl_conn *conn, int argc
>> OVS_UNUSED,
>> - const char *argv[], void *aux OVS_UNUSED)
>> +ofproto_unixctl_fdb_stats_text(const struct ofproto_dpif *ofproto,
>> + struct ds *ds)
>> {
>> - const struct ofproto_dpif *ofproto;
>> - ofproto = ofproto_dpif_lookup_by_name(argv[1]);
>> - if (!ofproto) {
>> - unixctl_command_reply_error(conn, "no such bridge");
>> - return;
>> - }
>> -
>> ovs_rwlock_rdlock(&ofproto->ml->rwlock);
>> - if (unixctl_command_get_output_format(conn) == UNIXCTL_OUTPUT_FMT_JSON)
>> {
>> - struct json *json = json_object_create();
>> -
>> - json_object_put_string(json, "bridge", argv[1]);
>> - json_object_put(json, "current_entries",
>> - json_integer_create(
>> - hmap_count(&ofproto->ml->table)));
>> - json_object_put(json, "max_entries",
>> - json_integer_create(ofproto->ml->max_entries));
>> - json_object_put(json, "static_entries",
>> - json_integer_create(ofproto->ml->static_entries));
>> - json_object_put(json, "total_learned",
>> - json_integer_create(ofproto->ml->total_learned));
>> - json_object_put(json, "total_expired",
>> - json_integer_create(ofproto->ml->total_expired));
>> - json_object_put(json, "total_evicted",
>> - json_integer_create(ofproto->ml->total_evicted));
>> - json_object_put(json, "total_moved",
>> - json_integer_create(ofproto->ml->total_moved));
>> -
>> - ovs_rwlock_unlock(&ofproto->ml->rwlock);
>> - unixctl_command_reply_json(conn, json);
>> - return;
>> - }
>>
>> - struct ds ds = DS_EMPTY_INITIALIZER;
>> -
>> - ds_put_format(&ds, "Statistics for bridge \"%s\":\n", argv[1]);
>> - ds_put_format(&ds, " Current/maximum MAC entries in the table: %"
>> + ds_put_format(ds, "Statistics for bridge \"%s\":\n", ofproto->up.name);
>> + ds_put_format(ds, " Current/maximum MAC entries in the table: %"
>> PRIuSIZE"/%"PRIuSIZE"\n",
>> hmap_count(&ofproto->ml->table),
>> ofproto->ml->max_entries);
>> - ds_put_format(&ds,
>> + ds_put_format(ds,
>> " Current static MAC entries in the table : %"
>> PRIuSIZE"\n", ofproto->ml->static_entries);
>> - ds_put_format(&ds,
>> + ds_put_format(ds,
>> " Total number of learned MAC entries : %"
>> PRIu64"\n", ofproto->ml->total_learned);
>> - ds_put_format(&ds,
>> + ds_put_format(ds,
>> " Total number of expired MAC entries : %"
>> PRIu64"\n", ofproto->ml->total_expired);
>> - ds_put_format(&ds,
>> + ds_put_format(ds,
>> " Total number of evicted MAC entries : %"
>> PRIu64"\n", ofproto->ml->total_evicted);
>> - ds_put_format(&ds,
>> + ds_put_format(ds,
>> " Total number of port moved MAC entries : %"
>> PRIu64"\n", ofproto->ml->total_moved);
>>
>> ovs_rwlock_unlock(&ofproto->ml->rwlock);
>> - unixctl_command_reply(conn, ds_cstr(&ds));
>> - ds_destroy(&ds);
>> +}
>> +
>> +static void
>> +ofproto_unixctl_fdb_stats_json(const struct ofproto_dpif *ofproto,
>> + struct json *fdb_stats)
>> +{
>> +
>> + struct json *json = json_object_create();
>> +
>> + json_object_put(fdb_stats, ofproto->up.name, json);
>> +
>> + ovs_rwlock_rdlock(&ofproto->ml->rwlock);
>> +
>> + json_object_put(json, "current_entries",
>> + json_integer_create(hmap_count(&ofproto->ml->table)));
>> + json_object_put(json, "max_entries",
>> + json_integer_create(ofproto->ml->max_entries));
>> + json_object_put(json, "static_entries",
>> + json_integer_create(ofproto->ml->static_entries));
>> + json_object_put(json, "total_learned",
>> + json_integer_create(ofproto->ml->total_learned));
>> + json_object_put(json, "total_expired",
>> + json_integer_create(ofproto->ml->total_expired));
>> + json_object_put(json, "total_evicted",
>> + json_integer_create(ofproto->ml->total_evicted));
>> + json_object_put(json, "total_moved",
>> + json_integer_create(ofproto->ml->total_moved));
>> +
>> + ovs_rwlock_unlock(&ofproto->ml->rwlock);
>> +}
>> +
>> +static void
>> +ofproto_unixctl_fdb_stats_show(struct unixctl_conn *conn, int argc
>> OVS_UNUSED,
>> + const char *argv[], void *aux OVS_UNUSED)
>> +{
>> + const struct ofproto_dpif *ofproto;
>> + 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_stats = json_object_create();
>> +
>> + ofproto_unixctl_fdb_stats_json(ofproto, fdb_stats);
>> + unixctl_command_reply_json(conn, fdb_stats);
>> + } else {
>> + struct ds ds = DS_EMPTY_INITIALIZER;
>> +
>> + ofproto_unixctl_fdb_stats_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 9dfdfe29b..b66938212 100644
>> --- a/tests/ofproto-dpif.at
>> +++ b/tests/ofproto-dpif.at
>> @@ -8249,12 +8249,16 @@ AT_CHECK_UNQUOTED([ovs-appctl fdb/stats-show br0 |
>> grep static], [0], [dnl
>> ])
>>
>> dnl Check JSON output.
>> -AT_CHECK([ovs-appctl --format json --pretty fdb/stats-show br0 | dnl
>> - grep '"bridge"\|entries'], [0], [dnl
>> - "bridge": "br0",
>> - "current_entries": 17,
>> - "max_entries": 8192,
>> - "static_entries": 17,
>> +AT_CHECK([ovs-appctl --format json --pretty fdb/stats-show br0], [0], [dnl
>> +{
>> + "br0": {
>> + "current_entries": 17,
>> + "max_entries": 8192,
>> + "static_entries": 17,
>> + "total_evicted": 0,
>> + "total_expired": 0,
>> + "total_learned": 18,
>> + "total_moved": 0}}
>> ])
>>
>> OVS_VSWITCHD_STOP
>>
>>> + struct json *json = json_object_create();
>>> +
>>> + json_object_put_string(json, "bridge", argv[1]);
>>> + json_object_put(json, "current_entries",
>>> + json_integer_create(
>>> + hmap_count(&ofproto->ml->table)));
>>> + json_object_put(json, "max_entries",
>>> + json_integer_create(ofproto->ml->max_entries));
>>> + json_object_put(json, "static_entries",
>>> + json_integer_create(ofproto->ml->static_entries));
>>> + json_object_put(json, "total_learned",
>>> + json_integer_create(ofproto->ml->total_learned));
>>> + json_object_put(json, "total_expired",
>>> + json_integer_create(ofproto->ml->total_expired));
>>> + json_object_put(json, "total_evicted",
>>> + json_integer_create(ofproto->ml->total_evicted));
>>> + json_object_put(json, "total_moved",
>>> + json_integer_create(ofproto->ml->total_moved));
>>> +
>>> + ovs_rwlock_unlock(&ofproto->ml->rwlock);
>>> + unixctl_command_reply_json(conn, json);
>>> + return;
>>> + }
>>>
>>> + struct ds ds = DS_EMPTY_INITIALIZER;
>>> +
>>> + ds_put_format(&ds, "Statistics for bridge \"%s\":\n", argv[1]);
>>> ds_put_format(&ds, " Current/maximum MAC entries in the table: %"
>>> PRIuSIZE"/%"PRIuSIZE"\n",
>>> - hmap_count(&ofproto->ml->table),
>>> ofproto->ml->max_entries);
>>> + hmap_count(&ofproto->ml->table),
>>> + ofproto->ml->max_entries);
>>> ds_put_format(&ds,
>>> - " Current static MAC entries in the table :
>>> %"PRIuSIZE"\n",
>>> - ofproto->ml->static_entries);
>>> + " Current static MAC entries in the table : %"
>>> + PRIuSIZE"\n", ofproto->ml->static_entries);
>>> ds_put_format(&ds,
>>> - " Total number of learned MAC entries :
>>> %"PRIu64"\n",
>>> - ofproto->ml->total_learned);
>>> + " Total number of learned MAC entries : %"
>>> + PRIu64"\n", ofproto->ml->total_learned);
>>> ds_put_format(&ds,
>>> - " Total number of expired MAC entries :
>>> %"PRIu64"\n",
>>> - ofproto->ml->total_expired);
>>> + " Total number of expired MAC entries : %"
>>> + PRIu64"\n", ofproto->ml->total_expired);
>>> ds_put_format(&ds,
>>> - " Total number of evicted MAC entries :
>>> %"PRIu64"\n",
>>> - ofproto->ml->total_evicted);
>>> + " Total number of evicted MAC entries : %"
>>> + PRIu64"\n", ofproto->ml->total_evicted);
>>> ds_put_format(&ds,
>>> - " Total number of port moved MAC entries :
>>> %"PRIu64"\n",
>>> - ofproto->ml->total_moved);
>>> + " Total number of port moved MAC entries : %"
>>> + PRIu64"\n", ofproto->ml->total_moved);
>>>
>>> ovs_rwlock_unlock(&ofproto->ml->rwlock);
>>> unixctl_command_reply(conn, ds_cstr(&ds));
>>> diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at
>>> index 39e43d376..9dfdfe29b 100644
>>> --- a/tests/ofproto-dpif.at
>>> +++ b/tests/ofproto-dpif.at
>>> @@ -8248,6 +8248,15 @@ AT_CHECK_UNQUOTED([ovs-appctl fdb/stats-show br0 |
>>> grep static], [0], [dnl
>>> Current static MAC entries in the table : 17
>>> ])
>>>
>>> +dnl Check JSON output.
>>> +AT_CHECK([ovs-appctl --format json --pretty fdb/stats-show br0 | dnl
>>> + grep '"bridge"\|entries'], [0], [dnl
>>
>> I would not re-format the output here, just use the raw output of the
>> --pretty option.
>>
>> AT_CHECK([ovs-appctl --format json --pretty fdb/stats-show br0], [0], [dnl
>> {
>> "br0": {
>> "current_entries": 17,
>> "max_entries": 8192,
>> "static_entries": 17,
>> "total_evicted": 0,
>> "total_expired": 0,
>> "total_learned": 18,
>> "total_moved": 0}}
>> ])
>>
>> Looking at the naming, I think static_entries should be renamed to
>> current_static_entries to make
>> its meaning clearer. Thoughts?
>
> Not sure about that particular point, but I'd suggest we group the stats
> here into two separate blocks:
>
> "br0": {
> "entries": {
> "current": 17,
> "max": 8192,
> "static": 17
> },
> "events": {
> "evicted": 0,
> "expired": 0,
> "learned": 18,
> "moved": 0
> }
> }
>
> WDYT?
Grouping makes sense, than current might not be needed, maybe max should be
maximum?
>>
>>
>> The unfiltered output and split of the show functions should be true for all
>> other patches also.
>>
>>> + "bridge": "br0",
>>> + "current_entries": 17,
>>> + "max_entries": 8192,
>>> + "static_entries": 17,
>>> +])
>>> +
>>> OVS_VSWITCHD_STOP
>>> AT_CLEANUP
>>>
>>> --
>>> 2.53.0
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev