[ovs-dev] [PATCH V2] ovs-ofctl: Ability to sort dump-flows by priority/other flow fields.

Arun Sharma arun.sharma at calsoftinc.com
Fri Jun 29 18:11:19 UTC 2012


ovs-ofctl will have --sort[=field]|--rsort[=field] option for 'dump-flows'.
Sort by field if specified else sort by default field 'priority'.
Option "--sort" to sort in ascending order and "--rsort" to sort in descending
order for flow fields. If field values found equal in some entries then
those entries will be displayed after sorting by priority field in
descending order.

Feature# 8754
Signed-off-by: Arun Sharma <arun.sharma at calsoftinc.com>
---
 lib/ofp-print.c          |   80 +++++++------
 lib/ofp-print.h          |    6 +
 tests/ovs-ofctl.at       |   60 +++++++++
 utilities/ovs-ofctl.8.in |   20 +++-
 utilities/ovs-ofctl.c    |  297 +++++++++++++++++++++++++++++++++++++++++++++-
 5 files changed, 426 insertions(+), 37 deletions(-)

diff --git a/lib/ofp-print.c b/lib/ofp-print.c
index 5103c3e..d1faac9 100644
--- a/lib/ofp-print.c
+++ b/lib/ofp-print.c
@@ -1223,6 +1223,37 @@ ofp_print_flow_stats_request(struct ds *string,
     cls_rule_format(&fsr.match, string);
 }
 
+void
+ofp_print_flow_stats(struct ds *string, struct ofputil_flow_stats *fs)
+{
+    ds_put_format(string, " cookie=0x%"PRIx64", duration=",
+                  ntohll(fs->cookie));
+
+    ofp_print_duration(string, fs->duration_sec, fs->duration_nsec);
+    ds_put_format(string, ", table=%"PRIu8", ", fs->table_id);
+    ds_put_format(string, "n_packets=%"PRIu64", ", fs->packet_count);
+    ds_put_format(string, "n_bytes=%"PRIu64", ", fs->byte_count);
+    if (fs->idle_timeout != OFP_FLOW_PERMANENT) {
+        ds_put_format(string, "idle_timeout=%"PRIu16", ", fs->idle_timeout);
+    }
+    if (fs->hard_timeout != OFP_FLOW_PERMANENT) {
+        ds_put_format(string, "hard_timeout=%"PRIu16", ", fs->hard_timeout);
+    }
+    if (fs->idle_age >= 0) {
+        ds_put_format(string, "idle_age=%d, ", fs->idle_age);
+    }
+    if (fs->hard_age >= 0 && fs->hard_age != fs->duration_sec) {
+        ds_put_format(string, "hard_age=%d, ", fs->hard_age);
+    }
+
+    cls_rule_format(&fs->rule, string);
+    if (string->string[string->length - 1] != ' ') {
+        ds_put_char(string, ' ');
+    }
+
+    ofp_print_actions(string, fs->actions, fs->n_actions);
+}
+
 static void
 ofp_print_flow_stats_reply(struct ds *string, const struct ofp_header *oh)
 {
@@ -1240,33 +1271,8 @@ ofp_print_flow_stats_reply(struct ds *string, const struct ofp_header *oh)
             }
             break;
         }
-
         ds_put_char(string, '\n');
-
-        ds_put_format(string, " cookie=0x%"PRIx64", duration=",
-                      ntohll(fs.cookie));
-        ofp_print_duration(string, fs.duration_sec, fs.duration_nsec);
-        ds_put_format(string, ", table=%"PRIu8", ", fs.table_id);
-        ds_put_format(string, "n_packets=%"PRIu64", ", fs.packet_count);
-        ds_put_format(string, "n_bytes=%"PRIu64", ", fs.byte_count);
-        if (fs.idle_timeout != OFP_FLOW_PERMANENT) {
-            ds_put_format(string, "idle_timeout=%"PRIu16", ", fs.idle_timeout);
-        }
-        if (fs.hard_timeout != OFP_FLOW_PERMANENT) {
-            ds_put_format(string, "hard_timeout=%"PRIu16", ", fs.hard_timeout);
-        }
-        if (fs.idle_age >= 0) {
-            ds_put_format(string, "idle_age=%d, ", fs.idle_age);
-        }
-        if (fs.hard_age >= 0 && fs.hard_age != fs.duration_sec) {
-            ds_put_format(string, "hard_age=%d, ", fs.hard_age);
-        }
-
-        cls_rule_format(&fs.rule, string);
-        if (string->string[string->length - 1] != ' ') {
-            ds_put_char(string, ' ');
-        }
-        ofp_print_actions(string, fs.actions, fs.n_actions);
+        ofp_print_flow_stats(string, &fs);
      }
 }
 
@@ -1602,15 +1608,10 @@ ofp_print_nxt_set_controller_id(struct ds *string,
     ds_put_format(string, " id=%"PRIu16, ntohs(nci->controller_id));
 }
 
-static void
-ofp_to_string__(const struct ofp_header *oh,
-                const struct ofputil_msg_type *type, struct ds *string,
-                int verbosity)
+void
+ofp_print_version(const struct ofp_header *oh,
+                  struct ds *string)
 {
-    enum ofputil_msg_code code;
-    const void *msg = oh;
-
-    ds_put_cstr(string, ofputil_msg_type_name(type));
     switch (oh->version) {
     case OFP10_VERSION:
         break;
@@ -1625,7 +1626,18 @@ ofp_to_string__(const struct ofp_header *oh,
         break;
     }
     ds_put_format(string, " (xid=0x%"PRIx32"):", ntohl(oh->xid));
+}
+
+static void
+ofp_to_string__(const struct ofp_header *oh,
+                const struct ofputil_msg_type *type, struct ds *string,
+                int verbosity)
+{
+    enum ofputil_msg_code code;
+    const void *msg = oh;
 
+    ds_put_cstr(string, ofputil_msg_type_name(type));
+    ofp_print_version(oh, string);
     code = ofputil_msg_type_code(type);
     switch (code) {
     case OFPUTIL_MSG_INVALID:
diff --git a/lib/ofp-print.h b/lib/ofp-print.h
index ae868a4..a58235c 100644
--- a/lib/ofp-print.h
+++ b/lib/ofp-print.h
@@ -26,6 +26,9 @@ struct ofp_flow_mod;
 struct ofp10_match;
 struct ds;
 union ofp_action;
+struct ofputil_flow_stats;
+struct ofp_header;
+struct ofputil_msg_type;
 
 #ifdef  __cplusplus
 extern "C" {
@@ -41,6 +44,9 @@ char *ofp_to_string(const void *, size_t, int verbosity);
 char *ofp10_match_to_string(const struct ofp10_match *, int verbosity);
 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 *);
+
 #ifdef  __cplusplus
 }
 #endif
diff --git a/tests/ovs-ofctl.at b/tests/ovs-ofctl.at
index 491e0ab..17ca43d 100644
--- a/tests/ovs-ofctl.at
+++ b/tests/ovs-ofctl.at
@@ -1332,3 +1332,63 @@ ofp_util|INFO|post: @&t@
 OVS_VSWITCHD_STOP
 AT_CLEANUP
 
+dnl Check that --sort and --rsort works with dump-flows
+dnl Default field is 'priority'. Flow entries are displayed based
+dnl on field to sort.
+AT_SETUP([ovs-ofctl [--sort|--rsort] dump-flows])
+OVS_VSWITCHD_START
+AT_KEYWORDS([dump-flows-sort])
+AT_DATA([allflows.txt], [[
+cookie=0x0, table=0, n_packets=0, n_bytes=0, priority=4,in_port=23213 actions=output:42
+cookie=0x0, table=0, n_packets=0, n_bytes=0, priority=5,in_port=1029 actions=output:43
+cookie=0x0, table=0, n_packets=0, n_bytes=0, priority=7,in_port=1029 actions=output:43
+cookie=0x0, table=0, n_packets=0, n_bytes=0, priority=3,in_port=1028 actions=output:44
+cookie=0x0, table=0, n_packets=0, n_bytes=0, priority=1,in_port=1026 actions=output:45
+cookie=0x0, table=0, n_packets=0, n_bytes=0, priority=6,in_port=1027 actions=output:64
+cookie=0x0, table=0, n_packets=0, n_bytes=0, priority=2,in_port=1025 actions=output:47
+]])
+
+AT_CHECK([ovs-ofctl add-flows br0 allflows.txt
+], [0], [ignore])
+AT_CHECK([ovs-ofctl --sort dump-flows br0 | ofctl_strip], [0], [dnl
+NXST_FLOW reply:
+ priority=1,in_port=1026 actions=output:45
+ priority=2,in_port=1025 actions=output:47
+ priority=3,in_port=1028 actions=output:44
+ priority=4,in_port=23213 actions=output:42
+ priority=5,in_port=1029 actions=output:43
+ priority=6,in_port=1027 actions=output:64
+ priority=7,in_port=1029 actions=output:43
+])
+AT_CHECK([ovs-ofctl --rsort dump-flows br0 | ofctl_strip], [0], [dnl
+NXST_FLOW reply:
+ priority=7,in_port=1029 actions=output:43
+ priority=6,in_port=1027 actions=output:64
+ priority=5,in_port=1029 actions=output:43
+ priority=4,in_port=23213 actions=output:42
+ priority=3,in_port=1028 actions=output:44
+ priority=2,in_port=1025 actions=output:47
+ priority=1,in_port=1026 actions=output:45
+])
+AT_CHECK([ovs-ofctl --sort=in_port dump-flows br0 | ofctl_strip], [0], [dnl
+NXST_FLOW reply:
+ priority=2,in_port=1025 actions=output:47
+ priority=1,in_port=1026 actions=output:45
+ priority=6,in_port=1027 actions=output:64
+ priority=3,in_port=1028 actions=output:44
+ priority=7,in_port=1029 actions=output:43
+ priority=5,in_port=1029 actions=output:43
+ priority=4,in_port=23213 actions=output:42
+])
+AT_CHECK([ovs-ofctl --rsort=in_port dump-flows br0 | ofctl_strip], [0], [dnl
+NXST_FLOW reply:
+ priority=4,in_port=23213 actions=output:42
+ priority=7,in_port=1029 actions=output:43
+ priority=5,in_port=1029 actions=output:43
+ priority=3,in_port=1028 actions=output:44
+ priority=6,in_port=1027 actions=output:64
+ priority=1,in_port=1026 actions=output:45
+ priority=2,in_port=1025 actions=output:47
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
diff --git a/utilities/ovs-ofctl.8.in b/utilities/ovs-ofctl.8.in
index 9c4ea0c..0a6c655 100644
--- a/utilities/ovs-ofctl.8.in
+++ b/utilities/ovs-ofctl.8.in
@@ -157,13 +157,21 @@ See the description of \fBip_frag\fR, below, for a way to match on
 whether a packet is a fragment and on its fragment offset.
 .
 .TP
-\fBdump\-flows \fIswitch \fR[\fIflows\fR]
+[\fB\-\-sort\fR[=field]\fR] [\fB\-\-rsort\fR[=field]\fR]\fB dump\-flows \fIswitch \fR[\fIflows\fR]
 Prints to the console all flow entries in \fIswitch\fR's
 tables that match \fIflows\fR.  If \fIflows\fR is omitted, all flows
 in the switch are retrieved.  See \fBFlow Syntax\fR, below, for the
 syntax of \fIflows\fR.  The output format is described in 
 \fBTable Entry Output\fR.
 .
+.IP
+Flow entries can be sorted by flow field in ascending or descending order.
+By default it will sort by \fBpriority\fR field if no argument is specified.
+If field values found equal in some entries then those entries will be
+displayed after sorting by \fBpriority\fR field in descending order.
+
+See \fBOPTIONS\fR below for more details.
+.
 .TP
 \fBdump\-aggregate \fIswitch \fR[\fIflows\fR]
 Prints to the console aggregate statistics for flows in 
@@ -1317,6 +1325,16 @@ Increases the verbosity of OpenFlow messages printed and logged by
 \fBovs\-ofctl\fR commands.  Specify this option more than once to
 increase verbosity further.
 .
+.IP \fB\-\-sort\fR[\fB=\fIfield\fR]
+Display output sorted by flow \fBfield\fR.
+It will display output in ascending order. Currently this option is only
+supported by \fBdump-flows\fR command.
+.
+.IP \fB\-\-rsort\fR[\fB=\fIfield\fR]
+Display output sorted by flow \fBfield\fR.
+It will display output in descending order. Currently this option is only
+supported by \fBdump-flows\fR command.
+.
 .ds DD \
 \fBovs\-ofctl\fR detaches only when executing the \fBmonitor\fR or \
 \fBsnoop\fR commands.
diff --git a/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c
index a2dfabf..ca6db15 100644
--- a/utilities/ovs-ofctl.c
+++ b/utilities/ovs-ofctl.c
@@ -55,6 +55,8 @@
 #include "util.h"
 #include "vconn.h"
 #include "vlog.h"
+#include "meta-flow.h"
+#include "sort.h"
 
 VLOG_DEFINE_THIS_MODULE(ofctl);
 
@@ -83,6 +85,22 @@ static int verbosity;
  * "snoop" command? */
 static bool timestamp;
 
+/* Different sorting orders. */
+enum sort_order
+{
+    ASC_SORT = 1,
+    DES_SORT
+};
+
+/* --sort|--rsort, store sorting order. */
+static enum sort_order selected_sort;
+
+/* --sort[=field]|--rsort[=field], flow field on which sorting is required. */
+static char *field_to_sort;
+
+/* Default sorting field if no argument to --sort|--rsort */
+static char *default_sort_field = "priority";
+
 static const struct command all_commands[];
 
 static void usage(void) NO_RETURN;
@@ -105,6 +123,8 @@ parse_options(int argc, char *argv[])
         OPT_STRICT = UCHAR_MAX + 1,
         OPT_READD,
         OPT_TIMESTAMP,
+        OPT_ASC_SORT,
+        OPT_DES_SORT,
         DAEMON_OPTION_ENUMS,
         VLOG_OPTION_ENUMS
     };
@@ -116,6 +136,8 @@ parse_options(int argc, char *argv[])
         {"packet-in-format", required_argument, NULL, 'P'},
         {"more", no_argument, NULL, 'm'},
         {"timestamp", no_argument, NULL, OPT_TIMESTAMP},
+        {"sort", optional_argument, NULL, OPT_ASC_SORT},
+        {"rsort", optional_argument, NULL, OPT_DES_SORT},
         {"help", no_argument, NULL, 'h'},
         {"version", no_argument, NULL, 'V'},
         DAEMON_LONG_OPTIONS,
@@ -183,6 +205,16 @@ parse_options(int argc, char *argv[])
             timestamp = true;
             break;
 
+        case OPT_ASC_SORT:
+           selected_sort = ASC_SORT;
+           field_to_sort = (optarg) ? optarg : default_sort_field;
+           break;
+
+        case OPT_DES_SORT:
+           selected_sort = DES_SORT;
+           field_to_sort = (optarg) ? optarg : default_sort_field;
+           break;
+
         DAEMON_OPTION_HANDLERS
         VLOG_OPTION_HANDLERS
         STREAM_SSL_OPTION_HANDLERS
@@ -194,6 +226,21 @@ parse_options(int argc, char *argv[])
             abort();
         }
     }
+
+    if (selected_sort) {
+        if (strcmp("dump-flows", argv[optind]) != 0) {
+            ovs_fatal(0, "--sort or --rsort is not supported by %s",
+                      argv[optind]);
+        }
+
+        if (strcmp(field_to_sort, default_sort_field) != 0) {
+            const struct mf_field *mf = mf_from_name(field_to_sort);
+            if (mf == NULL) {
+                ovs_fatal(0, "unknown sort field '%s'", field_to_sort);
+            }
+        }
+    }
+
     free(short_options);
 }
 
@@ -244,6 +291,8 @@ usage(void)
            "  -m, --more                  be more verbose printing OpenFlow\n"
            "  --timestamp                 (monitor, snoop) print timestamps\n"
            "  -t, --timeout=SECS          give up after SECS seconds\n"
+           "  --sort[=field]              sort in ascending order\n"
+           "  --rsort[=field]             sort in descending order\n"
            "  -h, --help                  display this help message\n"
            "  -V, --version               display version information\n");
     exit(EXIT_SUCCESS);
@@ -376,6 +425,146 @@ dump_trivial_transaction(const char *vconn_name, uint8_t request_type)
     dump_transaction(vconn_name, request);
 }
 
+struct dump_flows_stats {
+    struct ofputil_flow_stats **all_fs;    /* Flow entries to sort */
+    const char *field;                     /* Sorting field */
+};
+
+static int
+compare_flow_stats_by_priority(const void *a_, const void *b_)
+{
+    const struct ofputil_flow_stats *const *a = a_;
+    const struct ofputil_flow_stats *const *b = b_;
+    return ((*a)->rule.priority < (*b)->rule.priority
+            ? -1
+            : (*a)->rule.priority > (*b)->rule.priority);
+}
+
+static int
+compare_flow_stats_by_field(size_t a, size_t b, void *fs_dump_)
+{
+    const struct dump_flows_stats *fs_dump = fs_dump_;
+    const struct ofputil_flow_stats *fs_a = fs_dump->all_fs[a];
+    const struct ofputil_flow_stats *fs_b = fs_dump->all_fs[b];
+    const struct mf_field *mf = mf_from_name(fs_dump->field);
+    union mf_value value_a;
+    union mf_value value_b;
+    int res;
+
+    /*
+     * Flow entries which does meets mf prerequisites or
+     * not present in flow entry should be at the bottom
+     * of ascending sorted list.
+     */
+    if (selected_sort == ASC_SORT) {
+        char *cs_rule_str_a;
+        char *cs_rule_str_b;
+        char sort_field_tmp[strlen(fs_dump->field) + 1];
+
+        /*
+         * If a's mf prereqs is not ok and b's prereqs is ok
+         * then treat a is bigger then b. This is required to
+         * put unrelated entries at bottom of ascending sorted list.
+         */
+        if (!mf_are_prereqs_ok(mf, &((fs_a)->rule.flow))
+            && mf_are_prereqs_ok(mf, &((fs_b)->rule.flow))) {
+            return 1;
+        }
+
+        /*
+         * If a's mf prereqs is ok and b's prereqs is not ok
+         * then treat a is smaller then b. This is required to
+         * put unreleted entries at bottom of ascending sorted list.
+         */
+        if (mf_are_prereqs_ok(mf, &((fs_a)->rule.flow))
+            && !mf_are_prereqs_ok(mf, &((fs_b)->rule.flow))) {
+            return -1;
+        }
+
+        sprintf(sort_field_tmp, "%s=", fs_dump->field);
+        cs_rule_str_a = cls_rule_to_string(&(fs_a)->rule);
+        cs_rule_str_b = cls_rule_to_string(&(fs_b)->rule);
+
+        /*
+         * If sorting field is not present in cs_rule_str_a but
+         * present in cs_rule_str_b then treat cs_rule_str_a as
+         * bigger then cs_rule_str_b. This is required to
+         * put unrelated entries at bottom of ascending sorted list.
+         */
+        if ((strstr(cs_rule_str_a, sort_field_tmp) == NULL)
+            && strstr(cs_rule_str_b, sort_field_tmp) != NULL) {
+            return 1;
+        }
+
+        /*
+         * If sorting field is present in cs_rule_str_a but
+         * not present in cs_rule_str_b then treat cs_rule_str_a
+         * as smaller then cs_rule_str_b. This is required to
+         * put unrelated entries at bottom of ascending sorted list.
+         */
+        if ((strstr(cs_rule_str_a, sort_field_tmp) != NULL)
+            && strstr(cs_rule_str_b, sort_field_tmp) == NULL) {
+            return -1;
+        }
+
+        free(cs_rule_str_a);
+        free(cs_rule_str_b);
+    }
+
+    /* compare field values present in both entries */
+    memset(&value_a, '\0', sizeof(union mf_value));
+    memset(&value_b, '\0', sizeof(union mf_value));
+
+    mf_get_value(mf, &((fs_a)->rule.flow), &value_a);
+    mf_get_value(mf, &((fs_b)->rule.flow), &value_b);
+
+    if (mf_is_value_valid(mf, &value_a)
+        && mf_is_value_valid(mf, &value_b)) {
+        if (selected_sort == ASC_SORT) {
+            res = memcmp(&value_a, &value_b, mf->n_bytes);
+        } else {
+            res = memcmp(&value_b, &value_a, mf->n_bytes);
+        }
+    } else {
+        res = 0;
+    }
+
+    /*
+     * If field values are same in both entries or comparison
+     * result is zero then it should be sorted by priority in
+     * in descending order.
+     */
+    if (res == 0) {
+        return (fs_b->rule.priority < fs_a->rule.priority
+                ? -1
+                : fs_b->rule.priority > fs_a->rule.priority);
+    }
+    return res;
+}
+
+static void
+swap_flow_stats(size_t a, size_t b, void *fs_dump_)
+{
+    struct dump_flows_stats *fs_dump = fs_dump_;
+    struct ofputil_flow_stats *tmp;
+    tmp = fs_dump->all_fs[a];
+    fs_dump->all_fs[a] = fs_dump->all_fs[b];
+    fs_dump->all_fs[b] = tmp;
+}
+
+static void
+reverse_flow_stats(struct ofputil_flow_stats **all_fs,
+                   int fs_cnt)
+{
+    struct ofputil_flow_stats *tmp;
+    int i;
+    for (i = 0; i < (fs_cnt / 2); i++) {
+        tmp = all_fs[i];
+        all_fs[i] = all_fs[fs_cnt - i - 1];
+        all_fs[fs_cnt - i -1] = tmp;
+    }
+}
+
 static void
 dump_stats_transaction(const char *vconn_name, struct ofpbuf *request)
 {
@@ -383,6 +572,12 @@ dump_stats_transaction(const char *vconn_name, struct ofpbuf *request)
     ovs_be16 stats_type = ((struct ofp_stats_msg *) request->data)->type;
     struct vconn *vconn;
     bool done = false;
+    size_t i;
+    size_t fs_cnt = 0;
+    size_t reply_cnt = 0;
+    struct ds stats_str = DS_EMPTY_INITIALIZER;
+    struct ofputil_flow_stats **all_fs = NULL;
+    struct ofpbuf **all_reply = NULL;
 
     open_vconn(vconn_name, &vconn);
     send_openflow_buffer(vconn, request);
@@ -396,7 +591,19 @@ dump_stats_transaction(const char *vconn_name, struct ofpbuf *request)
             const struct ofp_stats_msg *osm = reply->data;
             const struct ofp_header *oh = reply->data;
 
-            ofp_print(stdout, reply->data, reply->size, verbosity + 1);
+            if (selected_sort && (!stats_str.string)) {
+                const struct ofputil_msg_type *type;
+                enum ofperr error;
+                error = ofputil_decode_msg_type(reply->data, &type);
+                if (!error) {
+                    ds_put_cstr(&stats_str, ofputil_msg_type_name(type));
+                    ofp_print_version(reply->data, &stats_str);
+                }
+            }
+
+            if (!selected_sort) {
+                ofp_print(stdout, reply->data, reply->size, verbosity + 1);
+            }
 
             if (oh->type == OFPT_ERROR) {
                 done = true;
@@ -408,11 +615,97 @@ dump_stats_transaction(const char *vconn_name, struct ofpbuf *request)
                           ofp_to_string(reply->data, reply->size,
                                         verbosity + 1));
             }
+
+            if (selected_sort) {
+                struct ofpbuf b;
+                ofpbuf_use_const(&b, reply->data,
+                                 ntohs(((struct ofp_header *)
+                                         reply->data)->length));
+
+                all_reply = xrealloc(all_reply,
+                                     (reply_cnt + 1) *
+                                     sizeof (struct ofpbuf *));
+
+                all_reply[reply_cnt] = reply;
+
+                for (;;) {
+                    int retval;
+                    all_fs = xrealloc(all_fs,
+                                      (fs_cnt + 1) *
+                                      sizeof (struct ofputil_flow_stats *));
+
+                    all_fs[fs_cnt] = xmalloc(sizeof(struct ofputil_flow_stats));
+
+                    retval = ofputil_decode_flow_stats_reply(all_fs[fs_cnt],
+                                                             &b, true);
+                    if (retval) {
+                        if (retval != EOF) {
+                            ovs_fatal(0,
+                                      "parse error while decoding flow stats");
+                        }
+                        free (all_fs[fs_cnt]);
+                        break;
+                    }
+                    fs_cnt++;
+                }
+            }
         } else {
             VLOG_DBG("received reply with xid %08"PRIx32" "
                      "!= expected %08"PRIx32, recv_xid, send_xid);
         }
-        ofpbuf_delete(reply);
+
+        if (!selected_sort) {
+            ofpbuf_delete(reply);
+        } else {
+            reply_cnt++;
+        }
+    }
+
+    if (selected_sort) {
+        struct dump_flows_stats fs_dump;
+        fs_dump.all_fs = all_fs;
+        fs_dump.field = field_to_sort;
+
+        if (strcmp(field_to_sort, default_sort_field) == 0) {
+            qsort(all_fs,
+                  fs_cnt,
+                  sizeof *all_fs,
+                  compare_flow_stats_by_priority);
+
+            if (selected_sort == DES_SORT) {
+                reverse_flow_stats(all_fs, fs_cnt);
+            }
+        } else {
+            sort(fs_cnt,
+                 compare_flow_stats_by_field,
+                 swap_flow_stats,
+                 &fs_dump);
+        }
+
+        fputs(ds_steal_cstr(&stats_str), stdout);
+
+        for (i = 0; i < fs_cnt; i++) {
+            char *formatted_entry;
+            struct ds string = DS_EMPTY_INITIALIZER;
+
+            ds_put_char(&string, '\n');
+            ofp_print_flow_stats(&string, all_fs[i]);
+            formatted_entry = ds_steal_cstr(&string);
+            fputs(formatted_entry, stdout);
+            free(formatted_entry);
+        }
+
+        fputs("\n", stdout);
+
+        for (i = 0; i < fs_cnt; i++) {
+            free(all_fs[i]);
+        }
+        free(all_fs);
+
+        for (i = 0; i < reply_cnt; i++) {
+            ofpbuf_delete(all_reply[i]);
+        }
+        free(all_reply);
     }
     vconn_close(vconn);
 }
-- 
1.7.2.5




More information about the dev mailing list