[ovs-dev] [sort 3/3] ovs-ofctl: Add --sort and --rsort options for "dump-flows" command.

Ben Pfaff blp at nicira.com
Tue Jul 3 19:48:41 UTC 2012


Signed-off-by: Arun Sharma <arun.sharma at calsoftinc.com>
[blp at nicira.com rewrote most of the code]
Signed-off-by: Ben Pfaff <blp at nicira.com>
---
 NEWS                     |    1 +
 lib/ofp-print.c          |   80 +++++++++++++---------
 lib/ofp-print.h          |    6 ++
 tests/ovs-ofctl.at       |   77 +++++++++++++++++++++
 utilities/ovs-ofctl.8.in |   33 +++++++++
 utilities/ovs-ofctl.c    |  168 ++++++++++++++++++++++++++++++++++++++++++++--
 6 files changed, 326 insertions(+), 39 deletions(-)

diff --git a/NEWS b/NEWS
index 1966ac2..cbd9784 100644
--- a/NEWS
+++ b/NEWS
@@ -5,6 +5,7 @@ post-v1.7.0
         - A new test utility that can create L3 tunnel between two Open
           vSwitches and detect connectivity issues.
     - ovs-ofctl:
+        - New --sort and --rsort options for "dump-flows" command.
         - "mod-port" command can now control all OpenFlow config flags.
     - OpenFlow:
       - Allow general bitwise masking for IPv4 and IPv6 addresses in
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..43e97c1 100644
--- a/tests/ovs-ofctl.at
+++ b/tests/ovs-ofctl.at
@@ -1332,3 +1332,80 @@ 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 dump-flows with sorting])
+OVS_VSWITCHD_START
+AT_KEYWORDS([dump-flows-sort])
+AT_DATA([allflows.txt], [[
+priority=4,in_port=23213 actions=output:42
+priority=5,in_port=1029 actions=output:43
+priority=7,in_port=1029 actions=output:43
+priority=3,in_port=1028 actions=output:44
+priority=1,in_port=1026 actions=output:45
+priority=6,in_port=1027 actions=output:64
+priority=2,in_port=1025 actions=output:47
+priority=8,tcp,tp_src=5 actions=drop
+priority=9,tcp,tp_src=6 actions=drop
+]])
+
+AT_CHECK([ovs-ofctl add-flows br0 allflows.txt
+], [0], [ignore])
+AT_CHECK([ovs-ofctl --sort dump-flows br0 | ofctl_strip], [0], [dnl
+ 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
+ priority=8,tcp,tp_src=5 actions=drop
+ priority=9,tcp,tp_src=6 actions=drop
+])
+AT_CHECK([ovs-ofctl --rsort dump-flows br0 | ofctl_strip], [0], [dnl
+ priority=9,tcp,tp_src=6 actions=drop
+ priority=8,tcp,tp_src=5 actions=drop
+ 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
+ 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
+ priority=9,tcp,tp_src=6 actions=drop
+ priority=8,tcp,tp_src=5 actions=drop
+])
+AT_CHECK([ovs-ofctl --rsort=in_port dump-flows br0 | ofctl_strip], [0], [dnl
+ 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
+ priority=9,tcp,tp_src=6 actions=drop
+ priority=8,tcp,tp_src=5 actions=drop
+])
+AT_CHECK([ovs-ofctl --sort=tcp_src dump-flows br0 | ofctl_strip], [0], [dnl
+ priority=8,tcp,tp_src=5 actions=drop
+ priority=9,tcp,tp_src=6 actions=drop
+ 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
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
diff --git a/utilities/ovs-ofctl.8.in b/utilities/ovs-ofctl.8.in
index 9c4ea0c..53d3848 100644
--- a/utilities/ovs-ofctl.8.in
+++ b/utilities/ovs-ofctl.8.in
@@ -164,6 +164,12 @@ 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
+By default, \fBovs\-ofctl\fR prints flow entries in the same order
+that the switch sends them, which is unlikely to be intuitive or
+consisten.  See the description of \fB\-\-sort\fR and \fB\-\-rsort\fR,
+under \fBOPTIONS\fR below, to influence the display order.
+.
 .TP
 \fBdump\-aggregate \fIswitch \fR[\fIflows\fR]
 Prints to the console aggregate statistics for flows in 
@@ -1317,6 +1323,33 @@ 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]
+.IQ \fB\-\-rsort\fR[\fB=\fIfield\fR]
+Display output sorted by flow \fIfield\fR in ascending
+(\fB\-\-sort\fR) or descending (\fB\-\-rsort\fR) order, where
+\fIfield\fR is any of the fields that are allowed for matching or
+\fBpriority\fR to sort by priority.  When \fIfield\fR is omitted, the
+output is sorted by priority.  Specify these options multiple times to
+sort by multiple fields.
+.IP
+Any given flow will not necessarily specify a value for a given
+field.  This requires special treatement:
+.RS
+.IP \(bu
+A flow that does not specify any part of a field that is used for sorting is
+sorted after all the flows that do specify the field.  For example,
+\fB\-\-sort=tcp_src\fR will sort all the flows that specify a TCP
+source port in ascending order, followed by the flows that do not
+specify a TCP source port at all.  
+.IP \(bu
+A flow that only specifies some bits in a field is sorted as if the
+wildcarded bits were zero.  For example, \fB\-\-sort=nw_src\fR would
+sort a flow that specifies \fBnw_src=192.168.0.0/24\fR the same as
+\fBnw_src=192.168.0.0\fR.
+.RE
+.IP
+These options currently affect only \fBdump\-flows\fR output.
+.
 .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 2f7a1bb..8d2ac0e 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,11 +85,23 @@ static int verbosity;
  * "snoop" command? */
 static bool timestamp;
 
+/* --sort, --rsort: Sort order. */
+enum sort_order { SORT_ASC, SORT_DESC };
+struct sort_criterion {
+    const struct mf_field *field; /* NULL means to sort by priority. */
+    enum sort_order order;
+};
+static struct sort_criterion *criteria;
+static size_t n_criteria, allocated_criteria;
+
 static const struct command all_commands[];
 
 static void usage(void) NO_RETURN;
 static void parse_options(int argc, char *argv[]);
 
+static bool recv_flow_stats_reply(struct vconn *, ovs_be32 send_xid,
+                                  struct ofpbuf **replyp,
+                                  struct ofputil_flow_stats *);
 int
 main(int argc, char *argv[])
 {
@@ -99,12 +113,35 @@ main(int argc, char *argv[])
 }
 
 static void
+add_sort_criterion(enum sort_order order, const char *field)
+{
+    struct sort_criterion *sc;
+
+    if (n_criteria >= allocated_criteria) {
+        criteria = x2nrealloc(criteria, &allocated_criteria, sizeof *criteria);
+    }
+
+    sc = &criteria[n_criteria++];
+    if (!field || !strcasecmp(field, "priority")) {
+        sc->field = NULL;
+    } else {
+        sc->field = mf_from_name(field);
+        if (!sc->field) {
+            ovs_fatal(0, "%s: unknown field name", field);
+        }
+    }
+    sc->order = order;
+}
+
+static void
 parse_options(int argc, char *argv[])
 {
     enum {
         OPT_STRICT = UCHAR_MAX + 1,
         OPT_READD,
         OPT_TIMESTAMP,
+        OPT_SORT,
+        OPT_RSORT,
         DAEMON_OPTION_ENUMS,
         VLOG_OPTION_ENUMS
     };
@@ -116,6 +153,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_SORT},
+        {"rsort", optional_argument, NULL, OPT_RSORT},
         {"help", no_argument, NULL, 'h'},
         {"version", no_argument, NULL, 'V'},
         DAEMON_LONG_OPTIONS,
@@ -183,6 +222,14 @@ parse_options(int argc, char *argv[])
             timestamp = true;
             break;
 
+        case OPT_SORT:
+            add_sort_criterion(SORT_ASC, optarg);
+            break;
+
+        case OPT_RSORT:
+            add_sort_criterion(SORT_DESC, optarg);
+            break;
+
         DAEMON_OPTION_HANDLERS
         VLOG_OPTION_HANDLERS
         STREAM_SSL_OPTION_HANDLERS
@@ -194,6 +241,12 @@ parse_options(int argc, char *argv[])
             abort();
         }
     }
+
+    if (n_criteria) {
+        /* Always do a final sort pass based on priority. */
+        add_sort_criterion(SORT_DESC, "priority");
+    }
+
     free(short_options);
 }
 
@@ -244,6 +297,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);
@@ -770,12 +825,12 @@ set_protocol_for_flow_dump(struct vconn *vconn,
     }
 }
 
-static void
-do_dump_flows__(int argc, char *argv[], bool aggregate)
+static struct vconn *
+prepare_dump_flows(int argc, char *argv[], bool aggregate,
+                   struct ofpbuf **requestp)
 {
     enum ofputil_protocol usable_protocols, protocol;
     struct ofputil_flow_stats_request fsr;
-    struct ofpbuf *request;
     struct vconn *vconn;
 
     parse_ofp_flow_stats_request_str(&fsr, aggregate, argc > 2 ? argv[2] : "");
@@ -783,15 +838,118 @@ do_dump_flows__(int argc, char *argv[], bool aggregate)
 
     protocol = open_vconn(argv[1], &vconn);
     protocol = set_protocol_for_flow_dump(vconn, protocol, usable_protocols);
-    request = ofputil_encode_flow_stats_request(&fsr, protocol);
+    *requestp = ofputil_encode_flow_stats_request(&fsr, protocol);
+    return vconn;
+}
+
+static void
+do_dump_flows__(int argc, char *argv[], bool aggregate)
+{
+    struct ofpbuf *request;
+    struct vconn *vconn;
+
+    vconn = prepare_dump_flows(argc, argv, aggregate, &request);
     dump_stats_transaction__(vconn, request);
     vconn_close(vconn);
 }
 
+static int
+compare_flows(const void *afs_, const void *bfs_)
+{
+    const struct ofputil_flow_stats *afs = afs_;
+    const struct ofputil_flow_stats *bfs = bfs_;
+    const struct cls_rule *a = &afs->rule;
+    const struct cls_rule *b = &bfs->rule;
+    const struct sort_criterion *sc;
+
+    for (sc = criteria; sc < &criteria[n_criteria]; sc++) {
+        const struct mf_field *f = sc->field;
+        int ret;
+
+        if (!f) {
+            ret = a->priority < b->priority ? -1 : a->priority > b->priority;
+        } else {
+            bool ina, inb;
+
+            ina = mf_are_prereqs_ok(f, &a->flow) && !mf_is_all_wild(f, &a->wc);
+            inb = mf_are_prereqs_ok(f, &b->flow) && !mf_is_all_wild(f, &b->wc);
+            if (ina != inb) {
+                /* Skip the test for sc->order, so that missing fields always
+                 * sort to the end whether we're sorting in ascending or
+                 * descending order. */
+                return ina ? -1 : 1;
+            } else {
+                union mf_value aval, bval;
+
+                mf_get_value(f, &a->flow, &aval);
+                mf_get_value(f, &b->flow, &bval);
+                ret = memcmp(&aval, &bval, f->n_bytes);
+            }
+        }
+
+        if (ret) {
+            return sc->order == SORT_ASC ? ret : -ret;
+        }
+    }
+
+    return 0;
+}
+
 static void
 do_dump_flows(int argc, char *argv[])
 {
-    return do_dump_flows__(argc, argv, false);
+    if (!n_criteria) {
+        return do_dump_flows__(argc, argv, false);
+    } else {
+        struct ofputil_flow_stats *fses;
+        size_t n_fses, allocated_fses;
+        struct ofpbuf *request;
+        struct ofpbuf *reply;
+        struct vconn *vconn;
+        ovs_be32 send_xid;
+        struct ds s;
+        size_t i;
+
+        vconn = prepare_dump_flows(argc, argv, false, &request);
+        send_xid = ((struct ofp_header *) request->data)->xid;
+        send_openflow_buffer(vconn, request);
+
+        fses = NULL;
+        n_fses = allocated_fses = 0;
+        reply = NULL;
+        for (;;) {
+            struct ofputil_flow_stats *fs;
+
+            if (n_fses >= allocated_fses) {
+                fses = x2nrealloc(fses, &allocated_fses, sizeof *fses);
+            }
+
+            fs = &fses[n_fses];
+            if (!recv_flow_stats_reply(vconn, send_xid, &reply, fs)) {
+                break;
+            }
+            fs->actions = xmemdup(fs->actions,
+                                  fs->n_actions * sizeof *fs->actions);
+            n_fses++;
+        }
+
+        qsort(fses, n_fses, sizeof *fses, compare_flows);
+
+        ds_init(&s);
+        for (i = 0; i < n_fses; i++) {
+            ds_clear(&s);
+            ofp_print_flow_stats(&s, &fses[i]);
+            puts(ds_cstr(&s));
+        }
+        ds_destroy(&s);
+
+        for (i = 0; i < n_fses; i++) {
+            free(fses[i].actions);
+        }
+        free(fses);
+
+        vconn_close(vconn);
+    }
 }
 
 static void
-- 
1.7.2.5




More information about the dev mailing list