Message ID | 20250223075415.2716137-1-roid@nvidia.com |
---|---|
State | Superseded |
Headers | show |
Series | [ovs-dev,v4] ofproto: Add JSON output for 'fdb/show' command. | expand |
Context | Check | Description |
---|---|---|
ovsrobot/apply-robot | success | apply and check: success |
ovsrobot/cirrus-robot | fail | cirrus build: failed |
ovsrobot/github-robot-_Build_and_Test | success | github build: passed |
On 23/02/2025 9:54, Roi Dayan wrote: > From: Dima Chumak <dchumak@nvidia.com> > > 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 <dchumak@nvidia.com> > Acked-by: Roi Dayan <roid@nvidia.com> > --- > > Notes: > 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 | 16 ++++++++ > 2 files changed, 90 insertions(+), 16 deletions(-) > > diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c > index bf43d5d4bc59..692c6508db0d 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) > +{ > + size_t num_entries = hmap_count(&ofproto->ml->table); > + struct json **json_entries = NULL; > + const struct mac_entry *entry; > + int i = 0; > + > + if (!num_entries) { > + goto done; > + } > + > + json_entries = xmalloc(num_entries * sizeof *json_entries); > + ovs_rwlock_rdlock(&ofproto->ml->rwlock); > + LIST_FOR_EACH (entry, lru_node, &ofproto->ml->lrus) { > + struct ofbundle *bundle = mac_entry_get_port(ofproto->ml, entry); > + struct json *json_entry = json_object_create(); > + int age = mac_entry_age(ofproto->ml, entry); > + long long port; > + > + port = (OVS_FORCE long long) ofbundle_get_a_port(bundle)->up.ofp_port; > + json_object_put(json_entry, "port", json_integer_create(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_string(json_entry, "age", "static"); oops sorry forgot to remove the string when checked something. i'll send v5. > + 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; > + > + if (i >= num_entries) { > + break; > + } > + } > + ovs_rwlock_unlock(&ofproto->ml->rwlock); > +done: > + *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..d184b05af90d 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],
diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c index bf43d5d4bc59..692c6508db0d 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) +{ + size_t num_entries = hmap_count(&ofproto->ml->table); + struct json **json_entries = NULL; + const struct mac_entry *entry; + int i = 0; + + if (!num_entries) { + goto done; + } + + json_entries = xmalloc(num_entries * sizeof *json_entries); + ovs_rwlock_rdlock(&ofproto->ml->rwlock); + LIST_FOR_EACH (entry, lru_node, &ofproto->ml->lrus) { + struct ofbundle *bundle = mac_entry_get_port(ofproto->ml, entry); + struct json *json_entry = json_object_create(); + int age = mac_entry_age(ofproto->ml, entry); + long long port; + + port = (OVS_FORCE long long) ofbundle_get_a_port(bundle)->up.ofp_port; + json_object_put(json_entry, "port", json_integer_create(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_string(json_entry, "age", "static"); + 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; + + if (i >= num_entries) { + break; + } + } + ovs_rwlock_unlock(&ofproto->ml->rwlock); +done: + *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..d184b05af90d 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],