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

Ethan Jackson ethan at nicira.com
Thu Jul 12 19:46:37 UTC 2012


There's some trailing whitespace in this patch.

s/consisten/consistent/

Thanks for adding unit tests.  Does it make sense to add one that
sorts by multiple fields specified on the command line? Perhaps adding
a sort by in_port to the test which does tp_src would give us slightly
better coverage.

Is there any particular reason not to always sort based on priority
even if neither sort nor rsort are specified? I suppose it's slightly
less efficient, but I'm not sure that matters in particular.  I think
it would cleanup the code every so slightly because we wouldn't have
to special case n_criteria = 0.

Looks good thanks,
Ethan


On Tue, Jul 3, 2012 at 2:48 PM, Ben Pfaff <blp at nicira.com> wrote:
> 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
>
> _______________________________________________
> dev mailing list
> dev at openvswitch.org
> http://openvswitch.org/mailman/listinfo/dev



More information about the dev mailing list