Previously fdb/show required exactly one bridge argument.  This change
lets it accept several bridge names in a single call, and show all
bridges when no argument is given.  This reduces the number of unixctl
round-trips for clients that poll FDB entries across many bridges
(e.g. EVPN agents managing multiple VNIs), while still allowing a
client to query only the subset of bridges it cares about.

When more than one bridge is shown, the entries are grouped under a
"Bridge" column, sized to the widest bridge name shown, that prints
each bridge name only on its first row; a bridge with no entries is
listed as a name-only row so it stays visible.  JSON output is now an
object keyed by bridge name whose values are arrays of that bridge's
entries, e.g. {"br0": [...], "br1": [...]}; this replaces the bare
entries array previously returned for a single bridge.  The "port"
column in the text output is renamed to "Port" so that all column
names are capitalized; apart from that, single-bridge text output is
unchanged.

Signed-off-by: Takeru Hayasaka <[email protected]>
---
v5:
- Renamed 'name_width' to 'br_width' to avoid confusion with the port
  names used inside the function.
- The "no such bridge" error now includes the offending bridge name.
- Dropped the intermediate 'len' variable; strlen() is now passed
  directly to MAX().
- Widened the "Bridge" column by one so that all column names in the
  header are separated by at least two spaces.
- Renamed the "port" column to "Port" so that all column names are
  capitalized; adjusted existing tests (including tests/stp.at) and
  NEWS accordingly.
  (All of the above suggested by Ilya Maximets.)
v4:
- Rebased on current master; the NEWS entry now lives under the existing
  "ovs-appctl" section.  No functional changes.
v3:
- No arguments now shows all bridges, matching commands like fdb/flush
  (suggested by Ilya Maximets); one or more bridge names still restricts
  output to just those bridges.
- Replaced the v2 "bridge <name>" header line with a "Bridge" column,
  added only when more than one bridge is shown, so single-bridge output
  is byte-for-byte identical to before.  This reverts the test churn
  introduced in v2 (including tests/stp.at).
- The "Bridge" column is sized to the widest bridge name shown and
  groups each bridge's entries, printing the name only on the bridge's
  first row.  A bridge with no entries is listed as a name-only row, so
  that (as in JSON) an existing-but-empty bridge stays visible in a
  multi-bridge listing.  Added tests covering long names, a bridge with
  multiple entries, and an empty bridge.
- JSON output is now an object keyed by bridge name ({"br0": [...]})
  rather than an array of {"bridge", "entries"} objects.
- Trimmed the NEWS entry to a single item.
- Updated the ovs-vswitchd(8) man page to match the above.
v2:
- Moved the "bridge <name>" prefixing into the per-bridge text and JSON
  helpers (suggested by Mike Pattrick).
- Updated the unixctl usage hint and the ovs-vswitchd(8) man page.
- Added tests for an empty-FDB JSON response and for the error path when
  a requested bridge does not exist.

 NEWS                       |   4 +
 ofproto/ofproto-dpif.c     |  88 ++++++++++++++---
 tests/ofproto-dpif.at      | 192 ++++++++++++++++++++++++++++---------
 tests/stp.at               |   6 +-
 vswitchd/ovs-vswitchd.8.in |  10 +-
 5 files changed, 235 insertions(+), 65 deletions(-)

diff --git a/NEWS b/NEWS
index c9331321c0ad..fcbf76157635 100644
--- a/NEWS
+++ b/NEWS
@@ -16,6 +16,10 @@ Post-v3.7.0
      * 'dpif-netdev/pmd-stats-show' command was removed in favor of the more
        informative and better structured 'dpif-netdev/pmd-perf-show', which
        now also provides statistics for the "main" thread.
+     * "fdb/show" now accepts multiple bridge names, and shows all bridges
+       when no bridge is specified.  The JSON output is now an object keyed
+       by bridge name, and the "port" column in the text output was renamed
+       to "Port".
    - Python:
      * The ovstest Python module is now removed.  It was only used by the
        ovs-test and ovs-l3ping utilities.
diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
index 729bc6f1d4ac..0f0f71d1426b 100644
--- a/ofproto/ofproto-dpif.c
+++ b/ofproto/ofproto-dpif.c
@@ -6165,11 +6165,11 @@ ofbundle_get_a_port(const struct ofbundle *bundle)
 
 static void
 ofproto_unixctl_fdb_show_text(const struct ofproto_dpif *ofproto,
-                              struct ds *ds)
+                              struct ds *ds, int br_width)
 {
     const struct mac_entry *e;
+    bool first = true;
 
-    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);
@@ -6178,6 +6178,12 @@ ofproto_unixctl_fdb_show_text(const struct ofproto_dpif 
*ofproto,
 
         ofputil_port_to_string(ofbundle_get_a_port(bundle)->up.ofp_port,
                 NULL, name, sizeof name);
+        if (br_width) {
+            /* Print the bridge name only on its first entry so that a
+             * bridge's rows read as a single group. */
+            ds_put_format(ds, " %-*s", br_width,
+                          first ? ofproto->up.name : "");
+        }
         ds_put_format(ds, "%5s  %4d  "ETH_ADDR_FMT"  ",
                 name, e->vlan, ETH_ADDR_ARGS(e->mac));
         if (MAC_ENTRY_AGE_STATIC_ENTRY == age) {
@@ -6185,6 +6191,12 @@ ofproto_unixctl_fdb_show_text(const struct ofproto_dpif 
*ofproto,
         } else {
             ds_put_format(ds, "%3d\n", age);
         }
+        first = false;
+    }
+    if (br_width && first) {
+        /* Bridge has no entries; still show its name so that it stays
+         * visible in a multi-bridge listing, matching JSON output. */
+        ds_put_format(ds, " %s\n", ofproto->up.name);
     }
     ovs_rwlock_unlock(&ofproto->ml->rwlock);
 }
@@ -6233,28 +6245,78 @@ done_unlock:
 }
 
 static void
-ofproto_unixctl_fdb_show(struct unixctl_conn *conn, int argc OVS_UNUSED,
-                          const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED)
+ofproto_unixctl_fdb_show(struct unixctl_conn *conn, int argc,
+                          const char *argv[], void *aux OVS_UNUSED)
 {
-    const struct ofproto_dpif *ofproto = ofproto_dpif_lookup_by_name(argv[1]);
+    const struct ofproto_dpif **ofprotos;
+    size_t n = 0;
 
-    if (!ofproto) {
-        unixctl_command_reply_error(conn, "no such bridge");
-        return;
+    if (argc > 1) {
+        /* Validate all requested bridges up front so that a bad name
+         * produces an error with no partial output. */
+        ofprotos = xmalloc((argc - 1) * sizeof *ofprotos);
+        for (int i = 1; i < argc; i++) {
+            const struct ofproto_dpif *ofproto =
+                ofproto_dpif_lookup_by_name(argv[i]);
+
+            if (!ofproto) {
+                char *err = xasprintf("no such bridge %s", argv[i]);
+
+                unixctl_command_reply_error(conn, err);
+                free(err);
+                free(ofprotos);
+                return;
+            }
+            ofprotos[n++] = ofproto;
+        }
+    } else {
+        /* No bridge specified: show all of them. */
+        struct ofproto_dpif *ofproto;
+
+        ofprotos = xmalloc(hmap_count(&all_ofproto_dpifs_by_name)
+                           * sizeof *ofprotos);
+        HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_by_name_node,
+                       &all_ofproto_dpifs_by_name) {
+            ofprotos[n++] = ofproto;
+        }
     }
 
     if (unixctl_command_get_output_format(conn) == UNIXCTL_OUTPUT_FMT_JSON) {
-        struct json *fdb_entries;
+        struct json *obj = json_object_create();
+
+        for (size_t i = 0; i < n; i++) {
+            struct json *entries;
 
-        ofproto_unixctl_fdb_show_json(ofproto, &fdb_entries);
-        unixctl_command_reply_json(conn, fdb_entries);
+            ofproto_unixctl_fdb_show_json(ofprotos[i], &entries);
+            json_object_put(obj, ofprotos[i]->up.name, entries);
+        }
+
+        unixctl_command_reply_json(conn, obj);
     } else {
         struct ds ds = DS_EMPTY_INITIALIZER;
+        int br_width = 0;
+
+        if (n > 1) {
+            br_width = strlen("Bridge");
+            for (size_t i = 0; i < n; i++) {
+                br_width = MAX(br_width, (int) strlen(ofprotos[i]->up.name));
+            }
+            /* One extra space so columns are separated by at least two
+             * spaces, like the rest of the header. */
+            br_width++;
+            ds_put_format(&ds, " %-*s", br_width, "Bridge");
+        }
+        ds_put_cstr(&ds, " Port  VLAN  MAC                Age\n");
+
+        for (size_t i = 0; i < n; i++) {
+            ofproto_unixctl_fdb_show_text(ofprotos[i], &ds, br_width);
+        }
 
-        ofproto_unixctl_fdb_show_text(ofproto, &ds);
         unixctl_command_reply(conn, ds_cstr(&ds));
         ds_destroy(&ds);
     }
+
+    free(ofprotos);
 }
 
 static void
@@ -7122,7 +7184,7 @@ ofproto_unixctl_init(void)
                              ofproto_unixctl_fdb_delete, NULL);
     unixctl_command_register("fdb/flush", "[bridge]", 0, 1,
                              ofproto_unixctl_fdb_flush, NULL);
-    unixctl_command_register("fdb/show", "bridge", 1, 1,
+    unixctl_command_register("fdb/show", "[bridge...]", 0, INT_MAX,
                              ofproto_unixctl_fdb_show, NULL);
     unixctl_command_register("fdb/stats-clear", "[bridge]", 0, 1,
                              ofproto_unixctl_fdb_stats_clear, NULL);
diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at
index 9e64e8ee4bd7..894f809b144d 100644
--- a/tests/ofproto-dpif.at
+++ b/tests/ofproto-dpif.at
@@ -7229,21 +7229,21 @@ 
br_flow="arp,in_port=3,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=ff:ff:ff:
 AT_CHECK([ovs-appctl ofproto/trace "$odp_flow"], [0], [stdout])
 # Check for no MAC learning entry
 AT_CHECK_UNQUOTED([ovs-appctl fdb/show br0 | sed 's/[[0-9]]\{1,\}$/?/'], [0], 
[dnl
- port  VLAN  MAC                Age
+ Port  VLAN  MAC                Age
 ])
 
 # Test command: ofproto/trace br_name br_flow
 AT_CHECK([ovs-appctl ofproto/trace br0 "$br_flow"], [0], [stdout])
 # Check for no MAC learning entry
 AT_CHECK_UNQUOTED([ovs-appctl fdb/show br0 | sed 's/[[0-9]]\{1,\}$/?/'], [0], 
[dnl
- port  VLAN  MAC                Age
+ Port  VLAN  MAC                Age
 ])
 
 # Test command: ofproto/trace odp_flow -generate
 AT_CHECK([ovs-appctl ofproto/trace "$odp_flow" -generate], [0], [stdout])
 # Check for the MAC learning entry
 AT_CHECK_UNQUOTED([ovs-appctl fdb/show br0 | sed 's/[[0-9]]\{1,\}$/?/'], [0], 
[dnl
- port  VLAN  MAC                Age
+ Port  VLAN  MAC                Age
     3     0  50:54:00:00:00:05    ?
 ])
 
@@ -7253,7 +7253,7 @@ AT_CHECK([ovs-appctl ofproto/trace ovs-dummy \
   -generate], [0], [stdout])
 # Check for both MAC learning entries
 AT_CHECK_UNQUOTED([ovs-appctl fdb/show br0 | sed 's/[[0-9]]\{1,\}$/?/'], [0], 
[dnl
- port  VLAN  MAC                Age
+ Port  VLAN  MAC                Age
     3     0  50:54:00:00:00:05    ?
     1     0  50:54:00:00:00:06    ?
 ])
@@ -7264,7 +7264,7 @@ AT_CHECK([ovs-appctl ofproto/trace br0 \
   -generate], [0], [stdout])
 # Check for both MAC learning entries.
 AT_CHECK_UNQUOTED([ovs-appctl fdb/show br0 | sed 's/[[0-9]]\{1,\}$/?/'], [0], 
[dnl
- port  VLAN  MAC                Age
+ Port  VLAN  MAC                Age
     3     0  50:54:00:00:00:05    ?
     1     0  50:54:00:00:00:06    ?
     2     0  50:54:00:00:00:07    ?
@@ -7543,6 +7543,12 @@ add_of_ports br0 1 2 3
 
 
arp='eth_type(0x0806),arp(sip=192.168.0.1,tip=192.168.0.2,op=1,sha=50:54:00:00:00:05,tha=00:00:00:00:00:00)'
 
+dnl Check json output for an empty FDB.
+AT_CHECK([ovs-appctl --format json --pretty fdb/show br0], [0], [dnl
+[{
+  "br0": []}]
+])
+
 # Trace an ARP packet arriving on p3, to create a MAC learning entry.
 OFPROTO_TRACE(
   [ovs-dummy],
@@ -7552,7 +7558,7 @@ OFPROTO_TRACE(
 
 # Check for the MAC learning entry.
 AT_CHECK_UNQUOTED([ovs-appctl fdb/show br0 | sed 's/[[0-9]]\{1,\}$/?/'], [0], 
[dnl
- port  VLAN  MAC                Age
+ Port  VLAN  MAC                Age
     3     0  50:54:00:00:00:05    ?
 ])
 
@@ -7566,7 +7572,7 @@ OFPROTO_TRACE(
 
 # Check for both MAC learning entries.
 AT_CHECK_UNQUOTED([ovs-appctl fdb/show br0 | sed 's/[[0-9]]\{1,\}$/?/'], [0], 
[dnl
- port  VLAN  MAC                Age
+ Port  VLAN  MAC                Age
     3     0  50:54:00:00:00:05    ?
     1     0  50:54:00:00:00:06    ?
 ])
@@ -7574,17 +7580,18 @@ AT_CHECK_UNQUOTED([ovs-appctl fdb/show br0 | sed 
's/[[0-9]]\{1,\}$/?/'], [0], [d
 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}]]
+[{
+  "br0": [
+    {
+      "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.
@@ -7596,7 +7603,7 @@ OFPROTO_TRACE(
 
 # Check that the MAC learning entry was updated.
 AT_CHECK_UNQUOTED([ovs-appctl fdb/show br0 | sed 's/[[0-9]]\{1,\}$/?/'], [0], 
[dnl
- port  VLAN  MAC                Age
+ Port  VLAN  MAC                Age
     1     0  50:54:00:00:00:06    ?
     2     0  50:54:00:00:00:05    ?
 ])
@@ -7622,7 +7629,7 @@ OFPROTO_TRACE(
 
 # Check that the MAC learning entries were added.
 AT_CHECK_UNQUOTED([ovs-appctl fdb/show br1 | sed 's/[[0-9]]\{1,\}$/?/'], [0], 
[dnl
- port  VLAN  MAC                Age
+ Port  VLAN  MAC                Age
     4     0  50:54:00:00:00:06    ?
     5     0  50:54:00:00:00:07    ?
 ])
@@ -7631,14 +7638,104 @@ AT_CHECK_UNQUOTED([ovs-appctl fdb/show br1 | sed 
's/[[0-9]]\{1,\}$/?/'], [0], [d
 # that the MAC learning entry for the same MAC was also deleted from br1.
 AT_CHECK([ovs-vsctl del-port p1])
 AT_CHECK_UNQUOTED([ovs-appctl fdb/show br0 | sed 's/[[0-9]]\{1,\}$/?/'], [0], 
[dnl
- port  VLAN  MAC                Age
+ Port  VLAN  MAC                Age
     2     0  50:54:00:00:00:05    ?
 ])
 AT_CHECK_UNQUOTED([ovs-appctl fdb/show br1 | sed 's/[[0-9]]\{1,\}$/?/'], [0], 
[dnl
- port  VLAN  MAC                Age
+ Port  VLAN  MAC                Age
     5     0  50:54:00:00:00:07    ?
 ])
 
+# Test fdb/show with multiple bridges.  A "Bridge" column groups each
+# bridge's entries, printing the name only on the bridge's first row.
+AT_CHECK_UNQUOTED([ovs-appctl fdb/show br0 br1 | sed 's/[[0-9]]\{1,\}$/?/'], 
[0], [dnl
+ Bridge  Port  VLAN  MAC                Age
+ br0        2     0  50:54:00:00:00:05    ?
+ br1        5     0  50:54:00:00:00:07    ?
+])
+
+# Test fdb/show without arguments: show all bridges.
+AT_CHECK_UNQUOTED([ovs-appctl fdb/show | sed 's/[[0-9]]\{1,\}$/?/' | sort], 
[0], [dnl
+ Bridge  Port  VLAN  MAC                Age
+ br0        2     0  50:54:00:00:00:05    ?
+ br1        5     0  50:54:00:00:00:07    ?
+])
+
+dnl Check json output with multiple bridges: keyed by bridge name.
+AT_CHECK([ovs-appctl --format json --pretty fdb/show br0 br1 \
+          | sed 's/"age": [[0-9]]*/"age": ?/g'], [0], [dnl
+[{
+  "br0": [
+    {
+      "age": ?,
+      "mac": "50:54:00:00:00:05",
+      "port": 2,
+      "vlan": 0}],
+  "br1": [
+    {
+      "age": ?,
+      "mac": "50:54:00:00:00:07",
+      "port": 5,
+      "vlan": 0}]}]
+])
+
+# Test fdb/show with a non-existent bridge mixed in: must reply with an
+# error and emit no partial output.
+AT_CHECK([ovs-appctl fdb/show br0 nonexistent], [2], [],
+  [no such bridge nonexistent
+ovs-appctl: ovs-vswitchd: server returned an error
+])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto-dpif - fdb/show bridge column width])
+OVS_VSWITCHD_START
+AT_CHECK([ovs-vsctl -- add-br br-nexthop -- set bridge br-nexthop 
datapath-type=dummy])
+add_of_ports br0 1 3
+add_of_ports br-nexthop 2
+
+dnl br0 holds several MAC entries; br-nexthop holds one.
+AT_CHECK([ovs-appctl fdb/add br0 p1 0 50:54:00:00:00:05])
+AT_CHECK([ovs-appctl fdb/add br0 p3 0 50:54:00:00:00:06])
+AT_CHECK([ovs-appctl fdb/add br-nexthop p2 0 50:54:00:00:00:07])
+
+dnl The "Bridge" column widens to the longest bridge name shown.  A
+dnl bridge with several MAC entries prints its name only on the first
+dnl row; the remaining rows leave the column blank so the entries read
+dnl as one group.
+AT_CHECK([ovs-appctl fdb/show br0 br-nexthop], [0], [dnl
+ Bridge      Port  VLAN  MAC                Age
+ br0            1     0  50:54:00:00:00:05  static
+                3     0  50:54:00:00:00:06  static
+ br-nexthop     2     0  50:54:00:00:00:07  static
+])
+
+dnl A bridge with no entries still shows a name-only row so it stays
+dnl visible in a multi-bridge listing (matching JSON, which lists []).
+AT_CHECK([ovs-vsctl -- add-br br-empty -- set bridge br-empty 
datapath-type=dummy])
+AT_CHECK([ovs-appctl fdb/show br0 br-empty], [0], [dnl
+ Bridge    Port  VLAN  MAC                Age
+ br0          1     0  50:54:00:00:00:05  static
+              3     0  50:54:00:00:00:06  static
+ br-empty
+])
+AT_CHECK([ovs-appctl --format json --pretty fdb/show br0 br-empty], [0], [dnl
+[{
+  "br-empty": [],
+  "br0": [
+    {
+      "mac": "50:54:00:00:00:05",
+      "port": 1,
+      "static": true,
+      "vlan": 0},
+    {
+      "mac": "50:54:00:00:00:06",
+      "port": 3,
+      "static": true,
+      "vlan": 0}]}]
+])
+
 OVS_VSWITCHD_STOP
 AT_CLEANUP
 
@@ -7674,7 +7771,7 @@ AT_CHECK_UNQUOTED([ovs-appctl fdb/show br0 | sed 's/ 
*[[0-9]]\{1,\}$//' | sort],
     3     0  50:54:00:00:00:07
     3     0  50:54:00:00:00:08
     3     0  50:54:00:00:00:09
- port  VLAN  MAC                Age
+ Port  VLAN  MAC                Age
 ])
 
 # Trace another ARP packet on another MAC.
@@ -7697,7 +7794,7 @@ AT_CHECK_UNQUOTED([ovs-appctl fdb/show br0 | sed 
's/[[0-9]]\{1,\}$/?/' | sort],
     3     0  50:54:00:00:00:08    ?
     3     0  50:54:00:00:00:09    ?
     3     0  50:54:00:00:00:10    ?
- port  VLAN  MAC                Age
+ Port  VLAN  MAC                Age
 ])
 OVS_VSWITCHD_STOP
 AT_CLEANUP
@@ -7732,7 +7829,7 @@ AT_CHECK_UNQUOTED([ovs-appctl fdb/show br0 | sed 's/ 
*[[0-9]]\{1,\}$//' | sort],
     4     0  50:54:00:00:00:07
     5     0  50:54:00:00:00:08
     5     0  50:54:00:00:00:09
- port  VLAN  MAC                Age
+ Port  VLAN  MAC                Age
 ])
 
 # Now trace 16 new MACs on another port.
@@ -7763,7 +7860,7 @@ AT_CHECK_UNQUOTED([ovs-appctl fdb/show br0 | sed 's/ 
*[[0-9]]\{1,\}$//' | sort],
     6     0  50:54:00:00:0d:ff
     6     0  50:54:00:00:0e:ff
     6     0  50:54:00:00:0f:ff
- port  VLAN  MAC                Age
+ Port  VLAN  MAC                Age
 ])
 OVS_VSWITCHD_STOP
 AT_CLEANUP
@@ -8197,27 +8294,28 @@ AT_CHECK_UNQUOTED([ovs-appctl fdb/show br0 | sed 's/ 
*[[0-9]]\{1,\}$//' | grep -
 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: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}]]
+[{
+  "br0": [
+    {
+      "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.
diff --git a/tests/stp.at b/tests/stp.at
index 75abe8e5ca07..f9a4080c9a67 100644
--- a/tests/stp.at
+++ b/tests/stp.at
@@ -632,13 +632,13 @@ ovs-appctl time/warp 36000 3000
 
 # check fdb and mdb
 AT_CHECK([ovs-appctl fdb/show br0], [0], [dnl
- port  VLAN  MAC                Age
+ Port  VLAN  MAC                Age
 ])
 AT_CHECK([ovs-appctl fdb/show br1], [0], [dnl
- port  VLAN  MAC                Age
+ Port  VLAN  MAC                Age
 ])
 AT_CHECK([ovs-appctl fdb/show br2], [0], [dnl
- port  VLAN  MAC                Age
+ Port  VLAN  MAC                Age
 ])
 
 AT_CHECK([ovs-appctl mdb/show br0], [0], [dnl
diff --git a/vswitchd/ovs-vswitchd.8.in b/vswitchd/ovs-vswitchd.8.in
index b21b1f08e893..3d80bceb655b 100644
--- a/vswitchd/ovs-vswitchd.8.in
+++ b/vswitchd/ovs-vswitchd.8.in
@@ -176,10 +176,16 @@ Deletes \fImac\fR address from a \fIport\fR and 
\fIvlan\fR on a \fIbridge\fR.
 .IP "\fBfdb/flush\fR [\fIbridge\fR]"
 Flushes \fIbridge\fR MAC address learning table, or all learning tables
 if no \fIbridge\fR is given.
-.IP "\fBfdb/show\fR \fIbridge\fR"
+.IP "\fBfdb/show\fR [\fIbridge\fR...]"
 Lists each MAC address/VLAN pair learned by the specified \fIbridge\fR,
 along with the port on which it was learned and the age of the entry,
-in seconds.
+in seconds.  Multiple bridge names may be given to query several bridges
+in a single call, and if no bridge is given the tables of all bridges are
+shown.  When more than one bridge is shown, text output gains a leading
+"Bridge" column that groups each bridge's entries, printing the bridge
+name only on its first row; a bridge with no entries is listed as a
+name-only row.  JSON output is an object keyed by bridge name, whose
+values are arrays of that bridge's entries.
 .IP "\fBfdb/stats-clear\fR [\fIbridge\fR]"
 Clear \fIbridge\fR MAC address learning table statistics, or all
 statistics if no \fIbridge\fR is given.
-- 
2.43.0

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

Reply via email to