From 8811fc0ae150f51078321798b22d49e9f98fa454 Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Mon, 6 Jul 2015 22:15:40 -0700 Subject: [PATCH] ofp-print: Abbreviate duplicated table features. 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 Acked-by: Andy Zhou --- 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 6db32d14b..44f3115bf 100644 --- a/lib/ofp-print.c +++ b/lib/ofp-print.c @@ -52,9 +52,6 @@ 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 * 'len' bytes starting at 'data'. The caller must free the returned string.*/ @@ -1621,7 +1618,9 @@ ofp_print_table_stats_reply(struct ds *string, const struct ofp_header *oh) ofpbuf_use_const(&b, oh, ntohs(oh->length)); 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; int retval; @@ -1634,7 +1633,12 @@ ofp_print_table_stats_reply(struct ds *string, const struct ofp_header *oh) return; } - 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; } } @@ -2615,7 +2619,9 @@ 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; @@ -2638,19 +2644,28 @@ 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); ds_put_cstr(s, " Apply-Actions features:\n"); @@ -2683,24 +2698,79 @@ table_instruction_features_empty( && table_action_features_empty(&tif->apply)); } -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); ds_put_format(s, "lookup=%"PRIu64", ", stats->lookup_count); 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), ntohll(features->metadata_write)); @@ -2726,29 +2796,45 @@ ofp_print_table_features(struct ds *s, ds_put_format(s, " max_entries=%"PRIu32"\n", features->max_entries); } - 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")); + } } } } @@ -2760,7 +2846,8 @@ 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; @@ -2771,7 +2858,10 @@ 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 825e139df..bbac18bd9 100644 --- a/lib/ofp-print.h +++ b/lib/ofp-print.h @@ -1,5 +1,5 @@ /* - * 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"); * you may not use this file except in compliance with the License. @@ -27,6 +27,8 @@ struct ofp10_match; struct ofp_flow_mod; struct ofp_header; struct ofputil_flow_stats; +struct ofputil_table_features; +struct ofputil_table_stats; #ifdef __cplusplus extern "C" { @@ -43,6 +45,11 @@ 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 127bcf1d7..3b4a4e8ae 100644 --- a/tests/ofp-print.at +++ b/tests/ofp-print.at @@ -1466,7 +1466,9 @@ 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 instructions (table miss and others): @@ -1508,21 +1510,18 @@ AT_KEYWORDS([ofp-print OFPT_STATS_REPLY]) icmpv6_code: exact match or wildcard 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" pad7="00 00 00 00 00 00 00 " diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at index 83986e27b..7000e4f16 100644 --- a/tests/ofproto-dpif.at +++ b/tests/ofproto-dpif.at @@ -6726,13 +6726,13 @@ 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]) @@ -6770,25 +6770,24 @@ NXT_PACKET_IN (xid=0x0): cookie=0x0 total_len=14 in_port=1 (via no_match) data_l 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]) (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]) @@ -6837,14 +6836,12 @@ OFPST_FLOW reply (OF1.3): table 0: 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]) @@ -6890,13 +6887,13 @@ OFPST_FLOW reply (OF1.1): active=0, lookup=3, matched=0 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 cf5ab9fb3..e3f08a83a 100644 --- a/tests/ofproto.at +++ b/tests/ofproto.at @@ -1307,12 +1307,9 @@ AT_CLEANUP 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 matching: @@ -1328,10 +1325,15 @@ OVS_VSWITCHD_START nw_tos: exact match or wildcard 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. AT_CHECK( @@ -1344,9 +1346,16 @@ AT_CHECK( <1> ]) # 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 AT_CLEANUP @@ -1374,12 +1383,9 @@ AT_CHECK([ovs-appctl bridge/dump-flows br0], [0], [stdout]) 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 matching: @@ -1395,10 +1401,15 @@ AT_CHECK([test `grep '240\.0\.0\.1' stdout | grep -v table_id= | wc -l` -gt 0]) nw_tos: exact match or wildcard 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"]) AT_CLEANUP @@ -1406,23 +1417,15 @@ AT_CLEANUP 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 config=controller 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 supported on Set-Field: metadata in_port_oxm eth_src eth_dst vlan_vid vlan_pcp mpls_label mpls_tc ip_src ip_dst ipv6_src ipv6_dst ipv6_label ip_dscp nw_ecn arp_op arp_spa arp_tpa arp_sha arp_tha tcp_src tcp_dst udp_src udp_dst sctp_src sctp_dst nd_target nd_sll nd_tll @@ -1461,10 +1464,28 @@ OVS_VSWITCHD_START icmpv6_code: exact match or wildcard 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. AT_CHECK( @@ -1477,37 +1498,35 @@ AT_CHECK( <1> ]) # 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 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 supported on Set-Field: tun_id tun_src tun_dst tun_flags tun_gbp_id tun_gbp_flags tun_metadata0 tun_metadata1 tun_metadata2 tun_metadata3 tun_metadata4 tun_metadata5 tun_metadata6 tun_metadata7 tun_metadata8 tun_metadata9 tun_metadata10 tun_metadata11 tun_metadata12 tun_metadata13 tun_metadata14 tun_metadata15 tun_metadata16 tun_metadata17 tun_metadata18 tun_metadata19 tun_metadata20 tun_metadata21 tun_metadata22 tun_metadata23 tun_metadata24 tun_metadata25 tun_metadata26 tun_metadata27 tun_metadata28 tun_metadata29 tun_metadata30 tun_metadata31 tun_metadata32 tun_metadata33 tun_metadata34 tun_metadata35 tun_metadata36 tun_metadata37 tun_metadata38 tun_metadata39 tun_metadata40 tun_metadata41 tun_metadata42 tun_metadata43 tun_metadata44 tun_metadata45 tun_metadata46 tun_metadata47 tun_metadata48 tun_metadata49 tun_metadata50 tun_metadata51 tun_metadata52 tun_metadata53 tun_metadata54 tun_metadata55 tun_metadata56 tun_metadata57 tun_metadata58 tun_metadata59 tun_metadata60 tun_metadata61 tun_metadata62 tun_metadata63 metadata in_port in_port_oxm pkt_mark reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 xreg0 xreg1 xreg2 xreg3 eth_src eth_dst vlan_tci vlan_vid vlan_pcp mpls_label mpls_tc ip_src ip_dst ipv6_src ipv6_dst ipv6_label nw_tos ip_dscp nw_ecn nw_ttl arp_op arp_spa arp_tpa arp_sha arp_tha tcp_src tcp_dst udp_src udp_dst sctp_src sctp_dst nd_target nd_sll nd_tll @@ -1640,12 +1659,47 @@ OVS_VSWITCHD_START icmpv6_code: exact match or wildcard 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( [ovs-vsctl \ @@ -1657,11 +1711,13 @@ AT_CHECK( <1> ]) # Check that the configuration was updated. -mv expout orig-expout -sed 's/classifier/main/ -142s/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 0bc708928..6ec715146 100644 --- a/utilities/ovs-ofctl.c +++ b/utilities/ovs-ofctl.c @@ -731,8 +731,72 @@ 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); } vconn_close(vconn); -- 2.20.1