summaryrefslogtreecommitdiff
path: root/ovn
diff options
context:
space:
mode:
authorBen Pfaff <blp@ovn.org>2016-12-28 09:31:42 -0800
committerBen Pfaff <blp@ovn.org>2016-12-28 09:31:42 -0800
commitd444a914fdbd4a5e1e41b81a3853deebb0da0db3 (patch)
tree9c68f541c69d4672e67abcc41ea1be6b4fd4673e /ovn
parentc80eac1f85df91babe5d4739810a80f7e748c3fe (diff)
downloadopenvswitch-d444a914fdbd4a5e1e41b81a3853deebb0da0db3.tar.gz
ovn-trace: New --ovs option to also print OpenFlow flows.
Sometimes seeing the OpenFlow flows that back a given logical flow can provide additional insight. This commit adds a new --ovs option to ovn-trace that makes it connect to Open vSwitch over OpenFlow and retrieve and print the OpenFlow flows behind each logical flow encountered during a trace. Signed-off-by: Ben Pfaff <blp@ovn.org> Acked-by: Justin Pettit <jpettit@ovn.org>
Diffstat (limited to 'ovn')
-rw-r--r--ovn/utilities/ovn-trace.8.xml54
-rw-r--r--ovn/utilities/ovn-trace.c91
2 files changed, 141 insertions, 4 deletions
diff --git a/ovn/utilities/ovn-trace.8.xml b/ovn/utilities/ovn-trace.8.xml
index 92dcb17bf..5d41f4695 100644
--- a/ovn/utilities/ovn-trace.8.xml
+++ b/ovn/utilities/ovn-trace.8.xml
@@ -255,6 +255,58 @@
<dd>
Selects all three forms of output.
</dd>
+
+ <dt><code>--ovs</code>[<code>=</code><var>remote</var>]</dt>
+ <dd>
+ <p>
+ Makes <code>ovn-trace</code> attempt to obtain and display the OpenFlow
+ flows that correspond to each OVN logical flow. To do so,
+ <code>ovn-trace</code> connects to <var>remote</var> (by default,
+ <code>unix:@RUNDIR@/br-int.mgmt</code>) over OpenFlow and retrieves the
+ flows. If <var>remote</var> is specified, it must be an active
+ OpenFlow connection method described in <code>ovs-ofctl</code>(8).
+ </p>
+
+ <p>
+ To make the best use of the output, it is important to understand the
+ relationship between logical flows and OpenFlow flows.
+ <code>ovn-architecture</code>(7), under <em>Architectural Physical Life
+ Cycle of a Packet</em>, describes this relationship. Keep in mind the
+ following points:
+ </p>
+
+ <ul>
+ <li>
+ <code>ovn-trace</code> currently shows all the OpenFlow flows to
+ which a logical flow corresponds, even though an actual packet
+ ordinarily matches only one of these.
+ </li>
+
+ <li>
+ Some logical flows can map to the Open vSwitch ``conjunctive match''
+ extension (see <code>ovs-ofctl</code>(8)). Currently
+ <code>ovn-trace</code> cannot display the flows with
+ <code>conjunction</code> actions that effectively produce the
+ <code>conj_id</code> match.
+ </li>
+
+ <li>
+ Some logical flows may not be represented in the OpenFlow tables on a
+ given hypervisor, if they could not be used on that hypervisor.
+ </li>
+
+ <li>
+ Some OpenFlow flows do not correspond to logical flows, such as
+ OpenFlow flows that map between physical and logical ports. These
+ flows will never show up in a trace.
+ </li>
+
+ <li>
+ When <code>ovn-trace</code> omits uninteresting logical flows from
+ output, it does not look up the corresponding OpenFlow flows.
+ </li>
+ </ul>
+ </dd>
</dl>
<h2>Daemon Options</h2>
@@ -266,7 +318,7 @@
<h2>PKI Options</h2>
<p>
PKI configuration is required to use SSL for the connection to the
- database.
+ database (and the switch, if <code>--ovs</code> is specified).
</p>
<xi:include href="lib/ssl.xml" xmlns:xi="http://www.w3.org/2003/XInclude"/>
diff --git a/ovn/utilities/ovn-trace.c b/ovn/utilities/ovn-trace.c
index 6d8e514a1..718929e47 100644
--- a/ovn/utilities/ovn-trace.c
+++ b/ovn/utilities/ovn-trace.c
@@ -27,6 +27,8 @@
#include "nx-match.h"
#include "openvswitch/dynamic-string.h"
#include "openvswitch/ofp-actions.h"
+#include "openvswitch/ofp-print.h"
+#include "openvswitch/vconn.h"
#include "openvswitch/vlog.h"
#include "ovn/actions.h"
#include "ovn/expr.h"
@@ -63,6 +65,10 @@ static bool summary;
/* --minimal: Show a trace with only minimal information. */
static bool minimal;
+/* --ovs: OVS instance to contact to get OpenFlow flows. */
+static const char *ovs;
+static struct vconn *vconn;
+
OVS_NO_RETURN static void usage(void);
static void parse_options(int argc, char *argv[]);
static char *trace(const char *datapath, const char *flow);
@@ -143,6 +149,12 @@ main(int argc, char *argv[])
}
}
+static char *
+default_ovs(void)
+{
+ return xasprintf("unix:%s/br-int.mgmt", ovs_rundir());
+}
+
static void
parse_options(int argc, char *argv[])
{
@@ -153,6 +165,7 @@ parse_options(int argc, char *argv[])
OPT_SUMMARY,
OPT_MINIMAL,
OPT_ALL,
+ OPT_OVS,
DAEMON_OPTION_ENUMS,
SSL_OPTION_ENUMS,
VLOG_OPTION_ENUMS
@@ -164,6 +177,7 @@ parse_options(int argc, char *argv[])
{"summary", no_argument, NULL, OPT_SUMMARY},
{"minimal", no_argument, NULL, OPT_MINIMAL},
{"all", no_argument, NULL, OPT_ALL},
+ {"ovs", optional_argument, NULL, OPT_OVS},
{"help", no_argument, NULL, 'h'},
{"version", no_argument, NULL, 'V'},
DAEMON_LONG_OPTIONS,
@@ -207,6 +221,10 @@ parse_options(int argc, char *argv[])
detailed = summary = minimal = true;
break;
+ case OPT_OVS:
+ ovs = optarg ? optarg : default_ovs();
+ break;
+
case 'h':
usage();
@@ -245,7 +263,7 @@ usage(void)
usage: %s [OPTIONS] DATAPATH MICROFLOW\n\
%s [OPTIONS] --detach\n\
\n\
-Option format options:\n\
+Output format options:\n\
--detailed table-by-table \"backtrace\" (default)\n\
--summary less detailed, more parseable\n\
--minimal minimum to explain externally visible behavior\n\
@@ -257,11 +275,14 @@ Option format options:\n\
Other options:\n\
--db=DATABASE connect to DATABASE\n\
(default: %s)\n\
+ --ovs[=REMOTE] obtain corresponding OpenFlow flows from REMOTE\n\
+ (default: %s)\n\
--unixctl=SOCKET set control socket name\n\
-h, --help display this help message\n\
-V, --version display version information\n",
- default_sb_db());
+ default_sb_db(), default_ovs());
stream_usage("database", true, true, false);
+ vconn_usage(true, false, false);
exit(EXIT_SUCCESS);
}
@@ -303,6 +324,7 @@ struct ovntrace_mcgroup {
enum ovntrace_pipeline { P_INGRESS, P_EGRESS };
struct ovntrace_flow {
+ struct uuid uuid;
enum ovntrace_pipeline pipeline;
int table_id;
char *stage_name;
@@ -644,6 +666,7 @@ read_flows(void)
}
struct ovntrace_flow *flow = xzalloc(sizeof *flow);
+ flow->uuid = sblf->header_.uuid;
flow->pipeline = (!strcmp(sblf->pipeline, "ingress")
? P_INGRESS
: P_EGRESS);
@@ -1394,6 +1417,54 @@ may_omit_stage(const struct ovntrace_flow *f, uint8_t table_id)
}
static void
+trace_openflow(const struct ovntrace_flow *f, struct ovs_list *super)
+{
+ struct ofputil_flow_stats_request fsr = {
+ .cookie = htonll(f->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) {
+ ovntrace_node_append(super, OVNTRACE_NODE_ERROR,
+ "*** error obtaining flow stats (%s)",
+ ovs_strerror(error));
+ VLOG_WARN("%s: error obtaining flow stats (%s)",
+ ovs, ovs_strerror(error));
+ return;
+ }
+
+ if (n_fses) {
+ struct ds s = DS_EMPTY_INITIALIZER;
+ for (size_t i = 0; i < n_fses; i++) {
+ ds_clear(&s);
+ ofp_print_flow_stats(&s, &fses[i]);
+
+ /* ofp_print_flow_stats() indents its output with a space.
+ * Omit it. */
+ const char *p = ds_cstr(&s);
+ p += strspn(p, " ");
+ ovntrace_node_append(super, OVNTRACE_NODE_ACTION, "%s", p);
+ }
+ ds_destroy(&s);
+ } else {
+ ovntrace_node_append(super, OVNTRACE_NODE_ERROR,
+ "*** no OpenFlow flows");
+ }
+
+ for (size_t i = 0; i < n_fses; i++) {
+ free(CONST_CAST(struct ofpact *, fses[i].ofpacts));
+ }
+ free(fses);
+}
+
+static void
trace__(const struct ovntrace_datapath *dp, struct flow *uflow,
uint8_t table_id, enum ovntrace_pipeline pipeline,
struct ovs_list *super)
@@ -1417,7 +1488,8 @@ trace__(const struct ovntrace_datapath *dp, struct flow *uflow,
} else if (f->source) {
ds_put_format(&s, "(%s): ", f->source);
}
- ds_put_format(&s, "%s, priority %d", f->match_s, f->priority);
+ ds_put_format(&s, "%s, priority %d, uuid %08x",
+ f->match_s, f->priority, f->uuid.parts[0]);
} else {
char *stage_name = ovntrace_stage_name(dp, table_id, pipeline);
ds_put_format(&s, "%s%sno match (implicit drop)",
@@ -1430,6 +1502,9 @@ trace__(const struct ovntrace_datapath *dp, struct flow *uflow,
ds_destroy(&s);
if (f) {
+ if (vconn) {
+ trace_openflow(f, &node->subs);
+ }
trace_actions(f->ovnacts, f->ovnacts_len, dp, uflow, table_id,
pipeline, &node->subs);
}
@@ -1465,6 +1540,13 @@ trace(const char *dp_s, const char *flow_s)
flow_format(&output, &uflow);
ds_put_char(&output, '\n');
+ if (ovs) {
+ int retval = vconn_open_block(ovs, 1 << OFP13_VERSION, 0, &vconn);
+ if (retval) {
+ VLOG_WARN("%s: connection failed (%s)", ovs, ovs_strerror(retval));
+ }
+ }
+
struct ovs_list root = OVS_LIST_INITIALIZER(&root);
struct ovntrace_node *node = ovntrace_node_append(
&root, OVNTRACE_NODE_PIPELINE, "ingress(dp=\"%s\", inport=\"%s\")",
@@ -1496,6 +1578,9 @@ trace(const char *dp_s, const char *flow_s)
ovntrace_node_prune_hard(&root);
ovntrace_node_print_summary(&output, &root, 0);
}
+
+ vconn_close(vconn);
+
return ds_steal_cstr(&output);
}