[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