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}
>
> 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?
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
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev