[ovs-dev] [PATCH] ofp-print: Abbreviate duplicated table features.

Ben Pfaff blp at nicira.com
Tue Jul 7 05:17:05 UTC 2015


I spent some time recently looking at the results of "ovs-ofctl
dump-table-features".  It was really distressing because of the volume of
information.  Every table yielded well over 100 lines of output and for 253
(visible) tables that meant over 25,300 lines of output total, which is
basically unusable.

This commit cuts the volume of output greatly by eliminating most of the
duplication from one table to the next.  The command now prints the full
output only for table 0, and for each subsequent table prints only the
parts that differ.  That reduces the output volume for tables after the
first to only 9 lines each (one of which is blank), for a total of more
like 2,400 lines, which is still not short but reasonably manageable.

Signed-off-by: Ben Pfaff <blp at nicira.com>
---
 lib/ofp-print.c       | 166 +++++++++++++++++++++++++++++++----------
 lib/ofp-print.h       |   9 ++-
 tests/ofp-print.at    |  29 ++++----
 tests/ofproto-dpif.at |  69 +++++++++--------
 tests/ofproto.at      | 202 ++++++++++++++++++++++++++++++++------------------
 utilities/ovs-ofctl.c |  68 ++++++++++++++++-
 6 files changed, 378 insertions(+), 165 deletions(-)

diff --git a/lib/ofp-print.c b/lib/ofp-print.c
index 4603bb7..4d8c129 100644
--- a/lib/ofp-print.c
+++ b/lib/ofp-print.c
@@ -53,7 +53,4 @@
 static void ofp_print_queue_name(struct ds *string, uint32_t port);
 static void ofp_print_error(struct ds *, enum ofperr);
-static void ofp_print_table_features(struct ds *,
-                                     const struct ofputil_table_features *,
-                                     const struct ofputil_table_stats *);
 
 /* Returns a string that represents the contents of the Ethernet frame in the
@@ -1622,5 +1619,7 @@ ofp_print_table_stats_reply(struct ds *string, const struct ofp_header *oh)
     ofpraw_pull_assert(&b);
 
-    for (;;) {
+    struct ofputil_table_features prev_features;
+    struct ofputil_table_stats prev_stats;
+    for (int i = 0;; i++) {
         struct ofputil_table_features features;
         struct ofputil_table_stats stats;
@@ -1635,5 +1634,10 @@ ofp_print_table_stats_reply(struct ds *string, const struct ofp_header *oh)
         }
 
-        ofp_print_table_features(string, &features, &stats);
+        ds_put_char(string, '\n');
+        ofp_print_table_features(string,
+                                 &features, i ? &prev_features : NULL,
+                                 &stats, i ? &prev_stats : NULL);
+        prev_features = features;
+        prev_stats = stats;
     }
 }
@@ -2460,5 +2464,7 @@ table_action_features_empty(const struct ofputil_table_action_features *taf)
 static void
 print_table_instruction_features(
-    struct ds *s, const struct ofputil_table_instruction_features *tif)
+    struct ds *s,
+    const struct ofputil_table_instruction_features *tif,
+    const struct ofputil_table_instruction_features *prev_tif)
 {
     int start, end;
@@ -2483,17 +2489,26 @@ print_table_instruction_features(
 
     if (tif->instructions) {
-        ds_put_cstr(s, "      instructions: ");
-        int i;
+        if (prev_tif && tif->instructions == prev_tif->instructions) {
+            ds_put_cstr(s, "      (same instructions)\n");
+        } else {
+            ds_put_cstr(s, "      instructions: ");
+            int i;
 
-        for (i = 0; i < 32; i++) {
-            if (tif->instructions & (1u << i)) {
-                ds_put_format(s, "%s,", ovs_instruction_name_from_type(i));
+            for (i = 0; i < 32; i++) {
+                if (tif->instructions & (1u << i)) {
+                    ds_put_format(s, "%s,", ovs_instruction_name_from_type(i));
+                }
             }
+            ds_chomp(s, ',');
+            ds_put_char(s, '\n');
         }
-        ds_chomp(s, ',');
-        ds_put_char(s, '\n');
     }
 
-    if (!table_action_features_equal(&tif->write, &tif->apply)) {
+    if (prev_tif
+        && table_action_features_equal(&tif->write, &prev_tif->write)
+        && table_action_features_equal(&tif->apply, &prev_tif->apply)
+        && !bitmap_is_all_zeros(tif->write.set_fields.bm, MFF_N_IDS)) {
+        ds_put_cstr(s, "      (same actions)\n");
+    } else if (!table_action_features_equal(&tif->write, &tif->apply)) {
         ds_put_cstr(s, "      Write-Actions features:\n");
         print_table_action_features(s, &tif->write);
@@ -2528,16 +2543,65 @@ table_instruction_features_empty(
 }
 
-static void
+static bool
+table_features_equal(const struct ofputil_table_features *a,
+                     const struct ofputil_table_features *b)
+{
+    return (a->metadata_match == b->metadata_match
+            && a->metadata_write == b->metadata_write
+            && a->miss_config == b->miss_config
+            && a->supports_eviction == b->supports_eviction
+            && a->supports_vacancy_events == b->supports_vacancy_events
+            && a->max_entries == b->max_entries
+            && table_instruction_features_equal(&a->nonmiss, &b->nonmiss)
+            && table_instruction_features_equal(&a->miss, &b->miss)
+            && bitmap_equal(a->match.bm, b->match.bm, MFF_N_IDS));
+}
+
+static bool
+table_features_empty(const struct ofputil_table_features *tf)
+{
+    return (!tf->metadata_match
+            && !tf->metadata_write
+            && tf->miss_config == OFPUTIL_TABLE_MISS_DEFAULT
+            && tf->supports_eviction < 0
+            && tf->supports_vacancy_events < 0
+            && !tf->max_entries
+            && table_instruction_features_empty(&tf->nonmiss)
+            && table_instruction_features_empty(&tf->miss)
+            && bitmap_is_all_zeros(tf->match.bm, MFF_N_IDS));
+}
+
+static bool
+table_stats_equal(const struct ofputil_table_stats *a,
+                  const struct ofputil_table_stats *b)
+{
+    return (a->active_count == b->active_count
+            && a->lookup_count == b->lookup_count
+            && a->matched_count == b->matched_count);
+}
+
+void
 ofp_print_table_features(struct ds *s,
                          const struct ofputil_table_features *features,
-                         const struct ofputil_table_stats *stats)
+                         const struct ofputil_table_features *prev_features,
+                         const struct ofputil_table_stats *stats,
+                         const struct ofputil_table_stats *prev_stats)
 {
     int i;
 
-    ds_put_format(s, "\n  table %"PRIu8, features->table_id);
+    ds_put_format(s, "  table %"PRIu8, features->table_id);
     if (features->name[0]) {
         ds_put_format(s, " (\"%s\")", features->name);
     }
-    ds_put_cstr(s, ":\n");
+    ds_put_char(s, ':');
+
+    bool same_stats = prev_stats && table_stats_equal(stats, prev_stats);
+    bool same_features = prev_features && table_features_equal(features,
+                                                               prev_features);
+    if ((!stats || same_stats) && (!features || same_features)) {
+        ds_put_cstr(s, " ditto");
+        return;
+    }
+    ds_put_char(s, '\n');
     if (stats) {
         ds_put_format(s, "    active=%"PRIu32", ", stats->active_count);
@@ -2545,5 +2609,11 @@ ofp_print_table_features(struct ds *s,
         ds_put_format(s, "matched=%"PRIu64"\n", stats->matched_count);
     }
-    if (features->metadata_match || features->metadata_match) {
+    if (same_features) {
+        if (!table_features_empty(features)) {
+            ds_put_cstr(s, "    (same features)\n");
+        }
+        return;
+    }
+    if (features->metadata_match || features->metadata_write) {
         ds_put_format(s, "    metadata: match=%#"PRIx64" write=%#"PRIx64"\n",
                       ntohll(features->metadata_match),
@@ -2571,27 +2641,43 @@ ofp_print_table_features(struct ds *s,
     }
 
-    if (!table_instruction_features_equal(&features->nonmiss,
-                                          &features->miss)) {
+    const struct ofputil_table_instruction_features *prev_nonmiss
+        = prev_features ? &prev_features->nonmiss : NULL;
+    const struct ofputil_table_instruction_features *prev_miss
+        = prev_features ? &prev_features->miss : NULL;
+    if (prev_features
+        && table_instruction_features_equal(&features->nonmiss, prev_nonmiss)
+        && table_instruction_features_equal(&features->miss, prev_miss)) {
+        if (!table_instruction_features_empty(&features->nonmiss)) {
+            ds_put_cstr(s, "    (same instructions)\n");
+        }
+    } else if (!table_instruction_features_equal(&features->nonmiss,
+                                                 &features->miss)) {
         ds_put_cstr(s, "    instructions (other than table miss):\n");
-        print_table_instruction_features(s, &features->nonmiss);
+        print_table_instruction_features(s, &features->nonmiss, prev_nonmiss);
         ds_put_cstr(s, "    instructions (table miss):\n");
-        print_table_instruction_features(s, &features->miss);
+        print_table_instruction_features(s, &features->miss, prev_miss);
     } else if (!table_instruction_features_empty(&features->nonmiss)) {
         ds_put_cstr(s, "    instructions (table miss and others):\n");
-        print_table_instruction_features(s, &features->nonmiss);
+        print_table_instruction_features(s, &features->nonmiss, prev_nonmiss);
     }
 
-    if (!bitmap_is_all_zeros(features->match.bm, MFF_N_IDS)){
-        ds_put_cstr(s, "    matching:\n");
-        BITMAP_FOR_EACH_1 (i, MFF_N_IDS, features->match.bm) {
-            const struct mf_field *f = mf_from_id(i);
-            bool mask = bitmap_is_set(features->mask.bm, i);
-            bool wildcard = bitmap_is_set(features->wildcard.bm, i);
-
-            ds_put_format(s, "      %s: %s\n",
-                          f->name,
-                          (mask ? "arbitrary mask"
-                           : wildcard ? "exact match or wildcard"
-                           : "must exact match"));
+    if (!bitmap_is_all_zeros(features->match.bm, MFF_N_IDS)) {
+        if (prev_features
+            && bitmap_equal(features->match.bm, prev_features->match.bm,
+                            MFF_N_IDS)) {
+            ds_put_cstr(s, "    (same matching)\n");
+        } else {
+            ds_put_cstr(s, "    matching:\n");
+            BITMAP_FOR_EACH_1 (i, MFF_N_IDS, features->match.bm) {
+                const struct mf_field *f = mf_from_id(i);
+                bool mask = bitmap_is_set(features->mask.bm, i);
+                bool wildcard = bitmap_is_set(features->wildcard.bm, i);
+
+                ds_put_format(s, "      %s: %s\n",
+                              f->name,
+                              (mask ? "arbitrary mask"
+                               : wildcard ? "exact match or wildcard"
+                               : "must exact match"));
+            }
         }
     }
@@ -2605,5 +2691,6 @@ ofp_print_table_features_reply(struct ds *s, const struct ofp_header *oh)
     ofpbuf_use_const(&b, oh, ntohs(oh->length));
 
-    for (;;) {
+    struct ofputil_table_features prev;
+    for (int i = 0; ; i++) {
         struct ofputil_table_features tf;
         int retval;
@@ -2616,5 +2703,8 @@ ofp_print_table_features_reply(struct ds *s, const struct ofp_header *oh)
             return;
         }
-        ofp_print_table_features(s, &tf, NULL);
+
+        ds_put_char(s, '\n');
+        ofp_print_table_features(s, &tf, i ? &prev : NULL, NULL, NULL);
+        prev = tf;
     }
 }
diff --git a/lib/ofp-print.h b/lib/ofp-print.h
index 825e139..bbac18b 100644
--- a/lib/ofp-print.h
+++ b/lib/ofp-print.h
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2008, 2009, 2011, 2012 Nicira, Inc.
+ * Copyright (c) 2008, 2009, 2011, 2012, 2015 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -28,4 +28,6 @@ struct ofp_flow_mod;
 struct ofp_header;
 struct ofputil_flow_stats;
+struct ofputil_table_features;
+struct ofputil_table_stats;
 
 #ifdef  __cplusplus
@@ -44,4 +46,9 @@ char *ofp_packet_to_string(const void *data, size_t len);
 void ofp_print_flow_stats(struct ds *, struct ofputil_flow_stats *);
 void ofp_print_version(const struct ofp_header *, struct ds *);
+void ofp_print_table_features(
+    struct ds *, const struct ofputil_table_features *features,
+    const struct ofputil_table_features *prev_features,
+    const struct ofputil_table_stats *stats,
+    const struct ofputil_table_stats *prev_stats);
 
 #ifdef  __cplusplus
diff --git a/tests/ofp-print.at b/tests/ofp-print.at
index 6e11150..39cb41c 100644
--- a/tests/ofp-print.at
+++ b/tests/ofp-print.at
@@ -1467,5 +1467,7 @@ AT_CLEANUP
 AT_SETUP([OFPST_TABLE reply - OF1.2])
 AT_KEYWORDS([ofp-print OFPT_STATS_REPLY])
-(tail="
+(echo 'OFPST_TABLE reply (OF1.2) (xid=0x2):
+  table 0 ("classifier"):
+    active=1, lookup=74614, matched=106024
     config=controller
     max_entries=1000000
@@ -1509,19 +1511,16 @@ AT_KEYWORDS([ofp-print OFPT_STATS_REPLY])
       nd_target: exact match or wildcard
       nd_sll: exact match or wildcard
-      nd_tll: exact match or wildcard"
- echo "OFPST_TABLE reply (OF1.2) (xid=0x2):
-  table 0 (\"classifier\"):
-    active=1, lookup=74614, matched=106024$tail"
- x=1
- while test $x -lt 254; do
-   printf "
-  table %d (\"%s\"):
-    active=0, lookup=0, matched=0$tail
-" $x table$x
-   x=`expr $x + 1`
+      nd_tll: exact match or wildcard
+
+  table 1 ("table1"):
+    active=0, lookup=0, matched=0
+    (same features)
+'
+ for i in `seq 2 253`; do
+     printf '  table %d ("table%d"): ditto\n' $i $i
  done
- echo "
-  table 254 (\"table254\"):
-    active=2, lookup=0, matched=0$tail") > expout
+ echo '  table 254 ("table254"):
+    active=2, lookup=0, matched=0
+    (same features)') > expout
 
 (pad32="\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at
index 7f20786..124f33d 100644
--- a/tests/ofproto-dpif.at
+++ b/tests/ofproto-dpif.at
@@ -6458,11 +6458,11 @@ NXST_FLOW reply:
 (echo "OFPST_TABLE reply (OF1.3) (xid=0x2):
   table 0:
-    active=1, lookup=0, matched=0"
- x=1
- while test $x -lt 254; do
-   echo "
-  table $x:
-    active=0, lookup=0, matched=0"
-   x=`expr $x + 1`
+    active=1, lookup=0, matched=0
+
+  table 1:
+    active=0, lookup=0, matched=0
+"
+ for i in `seq 2 253`; do
+     printf '  table %d: ditto\n' $i
  done) > expout
 AT_CHECK([ovs-ofctl -O OpenFlow13 dump-tables br0 ], [0], [expout])
@@ -6502,11 +6502,10 @@ vlan_tci=0x0000,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,dl_type=0x1234
 ])
 
-(printf "OFPST_TABLE reply (OF1.3) (xid=0x2):"
- x=0
- while test $x -lt 254; do
-   echo "
-  table $x:
-    active=0, lookup=0, matched=0"
-   x=`expr $x + 1`
+(echo "OFPST_TABLE reply (OF1.3) (xid=0x2):
+  table 0:
+    active=0, lookup=0, matched=0
+"
+ for i in `seq 1 253`; do
+     printf '  table %d: ditto\n' $i
  done) > expout
 AT_CHECK([ovs-ofctl -O OpenFlow13 dump-tables br0 ], [0], [expout])
@@ -6514,11 +6513,11 @@ AT_CHECK([ovs-ofctl -O OpenFlow13 dump-tables br0 ], [0], [expout])
 (echo "OFPST_TABLE reply (OF1.3) (xid=0x2):
   table 0:
-    active=0, lookup=3, matched=0"
- x=1
- while test $x -lt 254; do
-   echo "
-  table $x:
-    active=0, lookup=0, matched=0"
-   x=`expr $x + 1`
+    active=0, lookup=3, matched=0
+
+  table 1:
+    active=0, lookup=0, matched=0
+"
+ for i in `seq 2 253`; do
+     printf '  table %d: ditto\n' $i
  done) > expout
 AT_CHECK([ovs-ofctl -O OpenFlow13 dump-tables br1 ], [0], [expout])
@@ -6569,12 +6568,10 @@ OFPST_FLOW reply (OF1.3):
     active=1, lookup=3, matched=3
 
-  table 1:
-    active=1, lookup=3, matched=3"
- x=2
- while test $x -lt 254; do
-   echo "
-  table $x:
-    active=0, lookup=0, matched=0"
-   x=`expr $x + 1`
+  table 1: ditto
+  table 2:
+    active=0, lookup=0, matched=0
+"
+ for i in `seq 3 253`; do
+     printf '  table %d: ditto\n' $i
  done) > expout
 AT_CHECK([ovs-ofctl -O OpenFlow13 dump-tables br0 ], [0], [expout])
@@ -6622,11 +6619,11 @@ OFPST_FLOW reply (OF1.1):
 
   table 1:
-    active=1, lookup=3, matched=3"
- x=2
- while test $x -lt 254; do
-   echo "
-  table $x:
-    active=0, lookup=0, matched=0"
-   x=`expr $x + 1`
+    active=1, lookup=3, matched=3
+
+  table 2:
+    active=0, lookup=0, matched=0
+"
+ for i in `seq 3 253`; do
+     printf '  table %d: ditto\n' $i
  done) > expout
 AT_CHECK([ovs-ofctl -O OpenFlow13 dump-tables br0 ], [0], [expout])
diff --git a/tests/ofproto.at b/tests/ofproto.at
index 232dd2f..5497ade 100644
--- a/tests/ofproto.at
+++ b/tests/ofproto.at
@@ -1308,10 +1308,7 @@ AT_SETUP([ofproto - flow table configuration (OpenFlow 1.0)])
 OVS_VSWITCHD_START
 # Check the default configuration.
-(printf "OFPST_TABLE reply (xid=0x2):"
- x=0
- name=classifier
- while test $x -lt 254; do
-   printf "
-  table %d (\"%s\"):
+head_table() {
+    printf 'OFPST_TABLE reply (xid=0x2):
+  table 0 ("%s"):
     active=0, lookup=0, matched=0
     max_entries=1000000
@@ -1329,8 +1326,13 @@ OVS_VSWITCHD_START
       tcp_src: exact match or wildcard
       tcp_dst: exact match or wildcard
-" $x $name
-   x=`expr $x + 1`
-   name=table$x
- done) > expout
+
+' $1
+}
+ditto() {
+    for i in `seq $1 $2`; do
+        printf '  table %d ("table%d"): ditto\n' $i $i
+    done
+}
+(head_table classifier; ditto 1 253) > expout
 AT_CHECK([ovs-ofctl dump-tables br0], [0], [expout])
 # Change the configuration.
@@ -1345,7 +1347,14 @@ AT_CHECK(
 ])
 # Check that the configuration was updated.
-mv expout orig-expout
-sed -e 's/classifier/main/
-21s/1000000/1024/' orig-expout > expout
+(head_table main; echo '  table 1 ("table1"):
+    active=0, lookup=0, matched=0
+    max_entries=1024
+    (same matching)
+
+  table 2 ("table2"):
+    active=0, lookup=0, matched=0
+    max_entries=1000000
+    (same matching)
+'; ditto 3 253) > expout
 AT_CHECK([ovs-ofctl dump-tables br0], [0], [expout])
 OVS_VSWITCHD_STOP
@@ -1375,10 +1384,7 @@ AT_CHECK([test `grep '240\.0\.0\.1' stdout | grep -v table_id= | wc -l` -gt 0])
 
 # Check that dump-tables doesn't count the hidden flows.
-(printf "OFPST_TABLE reply (xid=0x2):"
- x=0
- name=classifier
- while test $x -lt 254; do
-   printf "
-  table %d (\"%s\"):
+head_table() {
+    printf 'OFPST_TABLE reply (xid=0x2):
+  table 0 ("%s"):
     active=0, lookup=0, matched=0
     max_entries=1000000
@@ -1396,8 +1402,13 @@ AT_CHECK([test `grep '240\.0\.0\.1' stdout | grep -v table_id= | wc -l` -gt 0])
       tcp_src: exact match or wildcard
       tcp_dst: exact match or wildcard
-" $x $name
-   x=`expr $x + 1`
-   name=table$x
- done) > expout
+
+' $1
+}
+ditto() {
+    for i in `seq $1 $2`; do
+        printf '  table %d ("table%d"): ditto\n' $i $i
+    done
+}
+(head_table classifier; ditto 1 253) > expout
 AT_CHECK([ovs-ofctl dump-tables br0], [0], [expout])
 OVS_VSWITCHD_STOP(["/cannot find route for controller/d"])
@@ -1407,15 +1418,7 @@ AT_SETUP([ofproto - flow table configuration (OpenFlow 1.2)])
 OVS_VSWITCHD_START
 # Check the default configuration.
-(printf "OFPST_TABLE reply (OF1.2) (xid=0x2):"
- x=0
- name=classifier
- while test $x -lt 254; do
-   if test $x = 253; then
-     goto=
-   else
-     goto=,goto_table
-   fi
-   echo "
-  table $x (\"$name\"):
+head_table() {
+    printf 'OFPST_TABLE reply (OF1.2) (xid=0x2):
+  table 0 ("%s"):
     active=0, lookup=0, matched=0
     metadata: match=0xffffffffffffffff write=0xffffffffffffffff
@@ -1423,5 +1426,5 @@ OVS_VSWITCHD_START
     max_entries=1000000
     instructions (table miss and others):
-      instructions: apply_actions,clear_actions,write_actions,write_metadata$goto
+      instructions: apply_actions,clear_actions,write_actions,write_metadata,goto_table
       Write-Actions and Apply-Actions features:
         actions: output group set_field strip_vlan push_vlan mod_nw_ttl dec_ttl set_mpls_ttl dec_mpls_ttl push_mpls pop_mpls set_queue
@@ -1462,8 +1465,26 @@ OVS_VSWITCHD_START
       nd_target: exact match or wildcard
       nd_sll: exact match or wildcard
-      nd_tll: exact match or wildcard"
-   x=`expr $x + 1`
-   name=table$x
- done) > expout
+      nd_tll: exact match or wildcard
+
+' $1
+}
+ditto() {
+    for i in `seq $1 $2`; do
+        printf '  table %d ("table%d"): ditto\n' $i $i
+    done
+}
+tail_table() {
+    printf '  table 253 ("table253"):
+    active=0, lookup=0, matched=0
+    metadata: match=0xffffffffffffffff write=0xffffffffffffffff
+    config=controller
+    max_entries=1000000
+    instructions (table miss and others):
+      instructions: apply_actions,clear_actions,write_actions,write_metadata
+      (same actions)
+    (same matching)
+'
+}
+(head_table classifier; ditto 1 252; tail_table) > expout
 AT_CHECK([ovs-ofctl -O OpenFlow12 dump-tables br0], [0], [expout])
 # Change the configuration.
@@ -1478,7 +1499,20 @@ AT_CHECK(
 ])
 # Check that the configuration was updated.
-mv expout orig-expout
-sed 's/classifier/main/
-53s/1000000/1024/' < orig-expout > expout
+(head_table main; echo '  table 1 ("table1"):
+    active=0, lookup=0, matched=0
+    metadata: match=0xffffffffffffffff write=0xffffffffffffffff
+    config=controller
+    max_entries=1024
+    (same instructions)
+    (same matching)
+
+  table 2 ("table2"):
+    active=0, lookup=0, matched=0
+    metadata: match=0xffffffffffffffff write=0xffffffffffffffff
+    config=controller
+    max_entries=1000000
+    (same instructions)
+    (same matching)
+'; ditto 3 252; tail_table) > expout
 AT_CHECK([ovs-ofctl -O OpenFlow12 dump-tables br0], [0], [expout])
 OVS_VSWITCHD_STOP
@@ -1487,26 +1521,11 @@ AT_CLEANUP
 AT_SETUP([ofproto - table features (OpenFlow 1.3)])
 OVS_VSWITCHD_START
-(x=0
- name=classifier
- while test $x -lt 254; do
-   y=`expr $x + 1`
-   if test $x = 253; then
-     next=
-     goto=
-   else
-     goto=,goto_table
-     if test $x = 252; then
-       next='
-      next tables: 253'
-     else
-       next="
-      next tables: $y-253"
-     fi
-   fi
-   echo "  table $x (\"$name\"):
+head_table () {
+    printf '  table 0 ("%s"):
     metadata: match=0xffffffffffffffff write=0xffffffffffffffff
     max_entries=1000000
-    instructions (table miss and others):$next
-      instructions: meter,apply_actions,clear_actions,write_actions,write_metadata$goto
+    instructions (table miss and others):
+      next tables: 1-253
+      instructions: meter,apply_actions,clear_actions,write_actions,write_metadata,goto_table
       Write-Actions and Apply-Actions features:
         actions: output group set_field strip_vlan push_vlan mod_nw_ttl dec_ttl set_mpls_ttl dec_mpls_ttl push_mpls pop_mpls set_queue
@@ -1640,10 +1659,45 @@ OVS_VSWITCHD_START
       nd_target: arbitrary mask
       nd_sll: arbitrary mask
-      nd_tll: arbitrary mask"
-   x=$y
-   name=table$x
- done) > expout
-AT_CHECK([ovs-ofctl -O OpenFlow13 dump-table-features br0 | sed '/^$/d
-/^OFPST_TABLE_FEATURES/d'], [0], [expout])
+      nd_tll: arbitrary mask
+
+' $1
+}
+ditto() {
+    printf '  table %d ("%s"):
+    metadata: match=0xffffffffffffffff write=0xffffffffffffffff
+    max_entries=%d
+    instructions (table miss and others):
+      next tables: %d-253
+      (same instructions)
+      (same actions)
+    (same matching)
+
+' $1 $2 $3 `expr $1 + 1`
+}
+tail_tables() {
+echo '  table 252 ("table252"):
+    metadata: match=0xffffffffffffffff write=0xffffffffffffffff
+    max_entries=1000000
+    instructions (table miss and others):
+      next tables: 253
+      (same instructions)
+      (same actions)
+    (same matching)
+
+  table 253 ("table253"):
+    metadata: match=0xffffffffffffffff write=0xffffffffffffffff
+    max_entries=1000000
+    instructions (table miss and others):
+      instructions: meter,apply_actions,clear_actions,write_actions,write_metadata
+      (same actions)
+    (same matching)
+'
+}
+(head_table classifier
+ for i in `seq 1 251`; do
+     ditto $i table$i 1000000
+ done
+ tail_tables) > expout
+AT_CHECK([ovs-ofctl -O OpenFlow13 dump-table-features br0], [0], [expout])
 # Change the configuration.
 AT_CHECK(
@@ -1657,9 +1711,11 @@ AT_CHECK(
 ])
 # Check that the configuration was updated.
-mv expout orig-expout
-sed 's/classifier/main/
-141s/1000000/1024/' < orig-expout > expout
-AT_CHECK([ovs-ofctl -O OpenFlow13 dump-table-features br0 | sed '/^$/d
-/^OFPST_TABLE_FEATURES/d'], [0], [expout])
+(head_table main
+ ditto 1 table1 1024
+ for i in `seq 2 251`; do
+     ditto $i table$i 1000000
+ done
+ tail_tables) > expout
+AT_CHECK([ovs-ofctl -O OpenFlow13 dump-table-features br0], [0], [expout])
 OVS_VSWITCHD_STOP
 AT_CLEANUP
diff --git a/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c
index 0bc7089..6ec7151 100644
--- a/utilities/ovs-ofctl.c
+++ b/utilities/ovs-ofctl.c
@@ -732,6 +732,70 @@ ofctl_dump_table_features(struct ovs_cmdl_context *ctx)
     open_vconn(ctx->argv[1], &vconn);
     request = ofputil_encode_table_features_request(vconn_get_version(vconn));
-    if (request) {
-        dump_stats_transaction(vconn, request);
+
+    /* The following is similar to dump_trivial_stats_transaction(), but it
+     * maintains the previous 'ofputil_table_features' from one stats reply
+     * message to the next, which allows duplication to be eliminated in the
+     * output across messages.  Otherwise the output is much larger and harder
+     * to read, because only 17 or so ofputil_table_features elements fit in a
+     * single 64 kB OpenFlow message and therefore you get a ton of repetition
+     * (every 17th element is printed in full instead of abbreviated). */
+
+    const struct ofp_header *request_oh = request->data;
+    ovs_be32 send_xid = request_oh->xid;
+    bool done = false;
+
+    struct ofputil_table_features prev;
+    int n = 0;
+
+    send_openflow_buffer(vconn, request);
+    while (!done) {
+        ovs_be32 recv_xid;
+        struct ofpbuf *reply;
+
+        run(vconn_recv_block(vconn, &reply), "OpenFlow packet receive failed");
+        recv_xid = ((struct ofp_header *) reply->data)->xid;
+        if (send_xid == recv_xid) {
+            enum ofptype type;
+            enum ofperr error;
+            error = ofptype_decode(&type, reply->data);
+            if (error) {
+                ovs_fatal(0, "decode error: %s", ofperr_get_name(error));
+            } else if (type == OFPTYPE_ERROR) {
+                ofp_print(stdout, reply->data, reply->size, verbosity + 1);
+                done = true;
+            } else if (type == OFPTYPE_TABLE_FEATURES_STATS_REPLY) {
+                done = !ofpmp_more(reply->data);
+                for (;;) {
+                    struct ofputil_table_features tf;
+                    int retval;
+
+                    retval = ofputil_decode_table_features(reply, &tf, true);
+                    if (retval) {
+                        if (retval != EOF) {
+                            ovs_fatal(0, "decode error: %s",
+                                      ofperr_get_name(retval));
+                        }
+                        break;
+                    }
+
+                    struct ds s = DS_EMPTY_INITIALIZER;
+                    ofp_print_table_features(&s, &tf, n ? &prev : NULL,
+                                             NULL, NULL);
+                    puts(ds_cstr(&s));
+                    ds_destroy(&s);
+
+                    prev = tf;
+                    n++;
+                }
+            } else {
+                ovs_fatal(0, "received bad reply: %s",
+                          ofp_to_string(reply->data, reply->size,
+                                        verbosity + 1));
+            }
+        } else {
+            VLOG_DBG("received reply with xid %08"PRIx32" "
+                     "!= expected %08"PRIx32, recv_xid, send_xid);
+        }
+        ofpbuf_delete(reply);
     }
 
-- 
2.1.3




More information about the dev mailing list