[ovs-dev] [PATCH v2 06/13] ovn-sbctl: Add --ovs option to "lflow-list", for listing OpenFlow flows.

Ben Pfaff blp at ovn.org
Wed May 3 15:45:53 UTC 2017


This is like the --ovs option to ovn-trace, but it applies to every flow
dumped, so it has different applications.

Signed-off-by: Ben Pfaff <blp at ovn.org>
---
 NEWS                            |  2 +
 include/openvswitch/ofp-print.h |  4 +-
 lib/db-ctl-base.c               | 19 ++++-----
 lib/db-ctl-base.h               | 11 ++++-
 lib/ofp-print.c                 |  2 +-
 ovn/utilities/ovn-sbctl.8.in    | 15 ++++++-
 ovn/utilities/ovn-sbctl.c       | 90 ++++++++++++++++++++++++++++++++++++++++-
 7 files changed, 124 insertions(+), 19 deletions(-)

diff --git a/NEWS b/NEWS
index 4d03ca529fa6..3033abd1cc98 100644
--- a/NEWS
+++ b/NEWS
@@ -25,6 +25,8 @@ Post-v2.7.0
      * ovn-trace now has basic support for tracing distributed firewalls.
      * In ovn-nbctl and ovn-sbctl, record UUIDs in commands may now be
        abbreviated to 4 hex digits.
+     * "ovn-sbctl lflow-list" can now print OpenFlow flows that correspond
+       to logical flows.
    - Add the command 'ovs-appctl stp/show' (see ovs-vswitchd(8)).
    - OpenFlow:
      * All features required by OpenFlow 1.4 are now implemented, so
diff --git a/include/openvswitch/ofp-print.h b/include/openvswitch/ofp-print.h
index 58fd4039d61f..863f7aaa2e52 100644
--- a/include/openvswitch/ofp-print.h
+++ b/include/openvswitch/ofp-print.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2011, 2012, 2015 Nicira, Inc.
+ * Copyright (c) 2008, 2009, 2011, 2012, 2015, 2017 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -43,7 +43,7 @@ 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_flow_stats(struct ds *, const struct ofputil_flow_stats *);
 void ofp_print_version(const struct ofp_header *, struct ds *);
 void ofp_print_table_features(
     struct ds *, const struct ofputil_table_features *features,
diff --git a/lib/db-ctl-base.c b/lib/db-ctl-base.c
index 52e06293ee21..78393895c91b 100644
--- a/lib/db-ctl-base.c
+++ b/lib/db-ctl-base.c
@@ -1637,11 +1637,11 @@ parse_command(int argc, char *argv[], struct shash *local_options,
         const char *s = strstr(p->options, node->name);
         int end = s ? s[strlen(node->name)] : EOF;
 
-        if (end != '=' && end != ',' && end != ' ' && end != '\0') {
+        if (!strchr("=,? ", end)) {
             ctl_fatal("'%s' command has no '%s' option",
                       argv[i], node->name);
         }
-        if ((end == '=') != (node->data != NULL)) {
+        if (end != '?' && (end == '=') != (node->data != NULL)) {
             if (end == '=') {
                 ctl_fatal("missing argument to '%s' option on '%s' "
                           "command", node->name, argv[i]);
@@ -1913,19 +1913,14 @@ ctl_add_cmd_options(struct option **options_p, size_t *n_options_p,
             s = xstrdup(p->options);
             for (name = strtok_r(s, ",", &save_ptr); name != NULL;
                  name = strtok_r(NULL, ",", &save_ptr)) {
-                char *equals;
-                int has_arg;
-
                 ovs_assert(name[0] == '-' && name[1] == '-' && name[2]);
                 name += 2;
 
-                equals = strchr(name, '=');
-                if (equals) {
-                    has_arg = required_argument;
-                    *equals = '\0';
-                } else {
-                    has_arg = no_argument;
-                }
+                size_t n = strcspn(name, "=?");
+                int has_arg = (name[n] == '\0' ? no_argument
+                               : name[n] == '=' ? required_argument
+                               : optional_argument);
+                name[n] = '\0';
 
                 o = find_option(name, *options_p, *n_options_p);
                 if (o) {
diff --git a/lib/db-ctl-base.h b/lib/db-ctl-base.h
index e4568a2cf2ce..81f0d0b27d3f 100644
--- a/lib/db-ctl-base.h
+++ b/lib/db-ctl-base.h
@@ -128,7 +128,16 @@ struct ctl_command_syntax {
     void (*postprocess)(struct ctl_context *ctx);
 
     /* A comma-separated list of supported options, e.g. "--a,--b", or the
-     * empty string if the command does not support any options. */
+     * empty string if the command does not support any options.
+     *
+     * Arguments are determined by appending special characters to option
+     * names:
+     *
+     *   - Append "=" (e.g. "--id=") for a required argument.
+     *
+     *   - Append "?" (e.g. "--ovs?") for an optional argument.
+     *
+     *   - Otherwise an option does not accept an argument. */
     const char *options;
 
     enum { RO, RW } mode;   /* Does this command modify the database? */
diff --git a/lib/ofp-print.c b/lib/ofp-print.c
index 10ac053ae54b..ab07434d024f 100644
--- a/lib/ofp-print.c
+++ b/lib/ofp-print.c
@@ -1659,7 +1659,7 @@ ofp_print_flow_stats_request(struct ds *string, const struct ofp_header *oh)
 }
 
 void
-ofp_print_flow_stats(struct ds *string, struct ofputil_flow_stats *fs)
+ofp_print_flow_stats(struct ds *string, const struct ofputil_flow_stats *fs)
 {
     ds_put_format(string, " %scookie=%s0x%"PRIx64", %sduration=%s",
                   colors.param, colors.end, ntohll(fs->cookie),
diff --git a/ovn/utilities/ovn-sbctl.8.in b/ovn/utilities/ovn-sbctl.8.in
index 83953cf3cd60..4f8017e7ae8b 100644
--- a/ovn/utilities/ovn-sbctl.8.in
+++ b/ovn/utilities/ovn-sbctl.8.in
@@ -160,7 +160,7 @@ to unbind logical port that is not bound has no effect.
 .
 .SS "Logical Flow Commands"
 .
-.IP "[\fB\-\-uuid\fR] \fBlflow\-list\fR [\fIlogical-datapath\fR] [\fIlflow\fR...]"
+.IP "[\fB\-\-uuid\fR] [\fB\-\-ovs\fR[\fB=\fIremote\fR]] [\fB\-\-stats\fR] \fBlflow\-list\fR [\fIlogical-datapath\fR] [\fIlflow\fR...]"
 List logical flows.  If \fIlogical-datapath\fR is specified, only list
 flows for that logical datapath.  The \fIlogical-datapath\fR may be
 given as a UUID or as a datapath name (reporting an error if multiple
@@ -177,6 +177,19 @@ flow.)
 If \fB\-\-uuid\fR is specified, the output includes the first 32 bits
 of each logical flow's UUID.  This makes it easier to find the
 OpenFlow flows that correspond to a given logical flow.
+.IP
+If \fB\-\-ovs\fR is included, \fBovn\-sbctl\fR attempts to obtain and
+display the OpenFlow flows that correspond to each OVN logical flow.
+To do so, \fBovn\-sbctl\fR connects to \fIremote\fR (by default,
+\fBunix:@RUNDIR@/br-int.mgmt\fR) over OpenFlow and retrieves the
+flows.  If \fIremote\fR is specified, it must be an active OpenFlow
+connection method described in \fBovs\-ofctl\fR(8).  Please see the
+discussion of the similar \fB\-\-ovs\fR option in \fBovn-trace\fR(8)
+for more information about the OpenFlow flow output.
+.IP
+By default, OpenFlow flow output includes only match and actions.  Add
+\fB\-\-stats\fR to include all OpenFlow information, such as packet
+and byte counters, duration, and timeouts.
 .
 .IP "[\fB\-\-uuid\fR] \fBdump\-flows\fR [\fIlogical-datapath\fR]"
 Alias for \fBlflow\-list\fB.
diff --git a/ovn/utilities/ovn-sbctl.c b/ovn/utilities/ovn-sbctl.c
index 69f461176572..97354df2da94 100644
--- a/ovn/utilities/ovn-sbctl.c
+++ b/ovn/utilities/ovn-sbctl.c
@@ -27,6 +27,7 @@
 #include <string.h>
 #include <unistd.h>
 
+#include "colors.h"
 #include "command-line.h"
 #include "compiler.h"
 #include "db-ctl-base.h"
@@ -34,7 +35,10 @@
 #include "fatal-signal.h"
 #include "openvswitch/dynamic-string.h"
 #include "openvswitch/json.h"
+#include "openvswitch/ofp-actions.h"
+#include "openvswitch/ofp-print.h"
 #include "openvswitch/shash.h"
+#include "openvswitch/vconn.h"
 #include "openvswitch/vlog.h"
 #include "ovn/lib/ovn-sb-idl.h"
 #include "ovn/lib/ovn-util.h"
@@ -736,6 +740,81 @@ is_partial_uuid_match(const struct uuid *uuid, const char *match)
     return !strncmp(s1, s2, strlen(s2));
 }
 
+static char *
+default_ovs(void)
+{
+    return xasprintf("unix:%s/br-int.mgmt", ovs_rundir());
+}
+
+static struct vconn *
+sbctl_open_vconn(struct shash *options)
+{
+    struct shash_node *ovs = shash_find(options, "--ovs");
+    if (!ovs) {
+        return NULL;
+    }
+
+    char *remote = ovs->data ? xstrdup(ovs->data) : default_ovs();
+    struct vconn *vconn;
+    int retval = vconn_open_block(remote, 1 << OFP13_VERSION, 0, &vconn);
+    if (retval) {
+        VLOG_WARN("%s: connection failed (%s)", remote, ovs_strerror(retval));
+    }
+    free(remote);
+    return vconn;
+}
+
+static void
+sbctl_dump_openflow(struct vconn *vconn, const struct uuid *uuid, bool stats)
+{
+    struct ofputil_flow_stats_request fsr = {
+        .cookie = htonll(uuid->parts[0]),
+        .cookie_mask = OVS_BE64_MAX,
+        .out_port = OFPP_ANY,
+        .out_group = OFPG_ANY,
+        .table_id = OFPTT_ALL,
+    };
+
+    struct ofputil_flow_stats *fses;
+    size_t n_fses;
+    int error = vconn_dump_flows(vconn, &fsr, OFPUTIL_P_OF13_OXM,
+                                 &fses, &n_fses);
+    if (error) {
+        VLOG_WARN("%s: error obtaining flow stats (%s)",
+                  vconn_get_name(vconn), ovs_strerror(error));
+        return;
+    }
+
+    if (n_fses) {
+        struct ds s = DS_EMPTY_INITIALIZER;
+        for (size_t i = 0; i < n_fses; i++) {
+            const struct ofputil_flow_stats *fs = &fses[i];
+
+            ds_clear(&s);
+            if (stats) {
+                ofp_print_flow_stats(&s, fs);
+            } else {
+                ds_put_format(&s, " %stable=%s%"PRIu8" ",
+                              colors.special, colors.end, fs->table_id);
+                match_format(&fs->match, &s, OFP_DEFAULT_PRIORITY);
+                if (ds_last(&s) != ' ') {
+                    ds_put_char(&s, ' ');
+                }
+
+                ds_put_format(&s, "%sactions=%s", colors.actions, colors.end);
+                ofpacts_format(fs->ofpacts, fs->ofpacts_len, &s);
+            }
+            printf("   %s\n", ds_cstr(&s));
+        }
+        ds_destroy(&s);
+    }
+
+    for (size_t i = 0; i < n_fses; i++) {
+        free(CONST_CAST(struct ofpact *, fses[i].ofpacts));
+    }
+    free(fses);
+}
+
 static void
 cmd_lflow_list(struct ctl_context *ctx)
 {
@@ -759,6 +838,9 @@ cmd_lflow_list(struct ctl_context *ctx)
         ctx->argv[i] = s;
     }
 
+    struct vconn *vconn = sbctl_open_vconn(&ctx->options);
+    bool stats = shash_find(&ctx->options, "--stats") != NULL;
+
     const struct sbrec_logical_flow **lflows = NULL;
     size_t n_flows = 0;
     size_t n_capacity = 0;
@@ -824,9 +906,13 @@ cmd_lflow_list(struct ctl_context *ctx)
                lflow->table_id,
                smap_get_def(&lflow->external_ids, "stage-name", ""),
                lflow->priority, lflow->match, lflow->actions);
+        if (vconn) {
+            sbctl_dump_openflow(vconn, &lflow->header_.uuid, stats);
+        }
         prev = lflow;
     }
 
+    vconn_close(vconn);
     free(lflows);
 }
 
@@ -1298,10 +1384,10 @@ static const struct ctl_command_syntax sbctl_commands[] = {
     /* Logical flow commands */
     {"lflow-list", 0, INT_MAX, "[DATAPATH] [LFLOW...]",
      pre_get_info, cmd_lflow_list, NULL,
-     "--uuid", RO},
+     "--uuid,--ovs?,--stats", RO},
     {"dump-flows", 0, INT_MAX, "[DATAPATH] [LFLOW...]",
      pre_get_info, cmd_lflow_list, NULL,
-     "--uuid", RO}, /* Friendly alias for lflow-list */
+     "--uuid,--ovs?,--stats", RO}, /* Friendly alias for lflow-list */
 
     /* Connection commands. */
     {"get-connection", 0, 0, "", pre_connection, cmd_get_connection, NULL, "", RO},
-- 
2.10.2



More information about the dev mailing list