2
0
mirror of https://github.com/openvswitch/ovs synced 2025-08-22 01:51:26 +00:00

ovs-ofctl: Add --sort and --rsort options for "dump-flows" command.

Feature #8754.
Signed-off-by: Arun Sharma <arun.sharma@calsoftinc.com>
[blp@nicira.com rewrote most of the code]
Signed-off-by: Ben Pfaff <blp@nicira.com>
This commit is contained in:
Ben Pfaff 2012-07-12 13:32:47 -07:00
parent 4ce9c31573
commit bdcc592595
6 changed files with 346 additions and 45 deletions

1
NEWS
View File

@ -8,6 +8,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

View File

@ -972,6 +972,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, ' ');
}
ofpacts_format(fs->ofpacts, fs->ofpacts_len, string);
}
static void
ofp_print_flow_stats_reply(struct ds *string, const struct ofp_header *oh)
{
@ -991,35 +1022,9 @@ 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, ' ');
}
ofpacts_format(fs.ofpacts, fs.ofpacts_len, string);
}
ofpbuf_uninit(&ofpacts);
ofp_print_flow_stats(string, &fs);
}
}
static void
@ -1354,15 +1359,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;
@ -1377,7 +1377,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:

View File

@ -22,9 +22,11 @@
#include <stdint.h>
#include <stdio.h>
struct ofp_flow_mod;
struct ofp10_match;
struct ds;
struct ofp10_match;
struct ofp_flow_mod;
struct ofp_header;
struct ofputil_flow_stats;
#ifdef __cplusplus
extern "C" {
@ -39,6 +41,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

View File

@ -1357,3 +1357,92 @@ 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([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
])
AT_CHECK(
[ovs-ofctl --sort=in_port --sort=tcp_src dump-flows br0 | ofctl_strip], [0],
[ 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=8,tcp,tp_src=5 actions=drop
priority=9,tcp,tp_src=6 actions=drop
])
OVS_VSWITCHD_STOP
AT_CLEANUP

View File

@ -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
consistent. 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.

View File

@ -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,24 @@ 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 *,
struct ofpbuf *ofpacts);
int
main(int argc, char *argv[])
{
@ -98,6 +113,27 @@ main(int argc, char *argv[])
return 0;
}
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[])
{
@ -105,6 +141,8 @@ parse_options(int argc, char *argv[])
OPT_STRICT = UCHAR_MAX + 1,
OPT_READD,
OPT_TIMESTAMP,
OPT_SORT,
OPT_RSORT,
DAEMON_OPTION_ENUMS,
VLOG_OPTION_ENUMS
};
@ -116,6 +154,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 +223,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 +242,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 +298,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 +826,12 @@ set_protocol_for_flow_dump(struct vconn *vconn,
}
}
static void
ofctl_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 +839,121 @@ ofctl_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
ofctl_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
ofctl_dump_flows(int argc, char *argv[])
{
return ofctl_dump_flows__(argc, argv, false);
if (!n_criteria) {
return ofctl_dump_flows__(argc, argv, false);
} else {
struct ofputil_flow_stats *fses;
size_t n_fses, allocated_fses;
struct ofpbuf *request;
struct ofpbuf ofpacts;
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;
ofpbuf_init(&ofpacts, 0);
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,
&ofpacts)) {
break;
}
fs->ofpacts = xmemdup(fs->ofpacts, fs->ofpacts_len);
n_fses++;
}
ofpbuf_uninit(&ofpacts);
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].ofpacts);
}
free(fses);
vconn_close(vconn);
}
}
static void