* 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
/*
- * 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.
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,
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]);
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) {
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? */
}
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),
.
.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
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.
#include <string.h>
#include <unistd.h>
+#include "colors.h"
#include "command-line.h"
#include "compiler.h"
#include "db-ctl-base.h"
#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"
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)
{
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;
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);
}
/* 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},