summaryrefslogtreecommitdiff
path: root/ovn
diff options
context:
space:
mode:
authorBen Pfaff <blp@ovn.org>2016-12-28 09:17:51 -0800
committerBen Pfaff <blp@ovn.org>2016-12-28 09:17:51 -0800
commitc80eac1f85df91babe5d4739810a80f7e748c3fe (patch)
treee8994e5edabbbbe59d5bda06a234b81b3231107e /ovn
parent92043ab8ffd449dfd50c3e716d6db06d04af70d7 (diff)
downloadopenvswitch-c80eac1f85df91babe5d4739810a80f7e748c3fe.tar.gz
ovn-controller: Tie OpenFlow and logical flows using OpenFlow cookie.
This makes it easy to find the logical flow that generated a particular OpenFlow flow, by running "ovn-sbctl dump-flows <cookie>". Later, this can be refined (and automated for "ofproto/trace"), but this is still a significant advance. Signed-off-by: Ben Pfaff <blp@ovn.org> Acked-by: Justin Pettit <jpettit@ovn.org>
Diffstat (limited to 'ovn')
-rw-r--r--ovn/controller/lflow.c8
-rw-r--r--ovn/controller/ofctrl.c12
-rw-r--r--ovn/controller/ofctrl.h4
-rw-r--r--ovn/controller/physical.c38
-rw-r--r--ovn/ovn-architecture.7.xml28
-rw-r--r--ovn/utilities/ovn-sbctl.8.in20
-rw-r--r--ovn/utilities/ovn-sbctl.c145
7 files changed, 201 insertions, 54 deletions
diff --git a/ovn/controller/lflow.c b/ovn/controller/lflow.c
index d913998a3..9f5341a73 100644
--- a/ovn/controller/lflow.c
+++ b/ovn/controller/lflow.c
@@ -265,8 +265,8 @@ consider_logical_flow(const struct lport_index *lports,
m->match.flow.conj_id += *conj_id_ofs_p;
}
if (!m->n) {
- ofctrl_add_flow(flow_table, ptable, lflow->priority, &m->match,
- &ofpacts);
+ ofctrl_add_flow(flow_table, ptable, lflow->priority,
+ lflow->header_.uuid.parts[0], &m->match, &ofpacts);
} else {
uint64_t conj_stubs[64 / 8];
struct ofpbuf conj;
@@ -281,7 +281,7 @@ consider_logical_flow(const struct lport_index *lports,
dst->clause = src->clause;
dst->n_clauses = src->n_clauses;
}
- ofctrl_add_flow(flow_table, ptable, lflow->priority, &m->match,
+ ofctrl_add_flow(flow_table, ptable, lflow->priority, 0, &m->match,
&conj);
ofpbuf_uninit(&conj);
}
@@ -350,7 +350,7 @@ consider_neighbor_flow(const struct lport_index *lports,
uint64_t stub[1024 / 8];
struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(stub);
put_load(mac.ea, sizeof mac.ea, MFF_ETH_DST, 0, 48, &ofpacts);
- ofctrl_add_flow(flow_table, OFTABLE_MAC_BINDING, 100, &match, &ofpacts);
+ ofctrl_add_flow(flow_table, OFTABLE_MAC_BINDING, 100, 0, &match, &ofpacts);
ofpbuf_uninit(&ofpacts);
}
diff --git a/ovn/controller/ofctrl.c b/ovn/controller/ofctrl.c
index 1d8fbf308..c79660bf4 100644
--- a/ovn/controller/ofctrl.c
+++ b/ovn/controller/ofctrl.c
@@ -57,6 +57,7 @@ struct ovn_flow {
/* Data. */
struct ofpact *ofpacts;
size_t ofpacts_len;
+ uint64_t cookie;
};
static uint32_t ovn_flow_hash(const struct ovn_flow *);
@@ -592,8 +593,8 @@ ofctrl_recv(const struct ofp_header *oh, enum ofptype type)
/* Flow table interfaces to the rest of ovn-controller. */
/* Adds a flow to 'desired_flows' with the specified 'match' and 'actions' to
- * the OpenFlow table numbered 'table_id' with the given 'priority'. The
- * caller retains ownership of 'match' and 'actions'.
+ * the OpenFlow table numbered 'table_id' with the given 'priority' and
+ * OpenFlow 'cookie'. The caller retains ownership of 'match' and 'actions'.
*
* This just assembles the desired flow table in memory. Nothing is actually
* sent to the switch until a later call to ofctrl_run().
@@ -601,8 +602,9 @@ ofctrl_recv(const struct ofp_header *oh, enum ofptype type)
* The caller should initialize its own hmap to hold the flows. */
void
ofctrl_add_flow(struct hmap *desired_flows,
- uint8_t table_id, uint16_t priority,
- const struct match *match, const struct ofpbuf *actions)
+ uint8_t table_id, uint16_t priority, uint64_t cookie,
+ const struct match *match,
+ const struct ofpbuf *actions)
{
struct ovn_flow *f = xmalloc(sizeof *f);
f->table_id = table_id;
@@ -611,6 +613,7 @@ ofctrl_add_flow(struct hmap *desired_flows,
f->ofpacts = xmemdup(actions->data, actions->size);
f->ofpacts_len = actions->size;
f->hmap_node.hash = ovn_flow_hash(f);
+ f->cookie = cookie;
if (ovn_flow_lookup(desired_flows, f)) {
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 5);
@@ -935,6 +938,7 @@ ofctrl_put(struct hmap *flow_table, struct shash *pending_ct_zones,
.table_id = d->table_id,
.ofpacts = d->ofpacts,
.ofpacts_len = d->ofpacts_len,
+ .new_cookie = htonll(d->cookie),
.command = OFPFC_ADD,
};
add_flow_mod(&fm, &msgs);
diff --git a/ovn/controller/ofctrl.h b/ovn/controller/ofctrl.h
index 5308b61b5..79f0cb780 100644
--- a/ovn/controller/ofctrl.h
+++ b/ovn/controller/ofctrl.h
@@ -45,8 +45,8 @@ void ofctrl_ct_flush_zone(uint16_t zone_id);
/* Flow table interfaces to the rest of ovn-controller. */
void ofctrl_add_flow(struct hmap *desired_flows, uint8_t table_id,
- uint16_t priority, const struct match *,
- const struct ofpbuf *ofpacts);
+ uint16_t priority, uint64_t cookie,
+ const struct match *, const struct ofpbuf *ofpacts);
void ofctrl_flow_table_clear(void);
diff --git a/ovn/controller/physical.c b/ovn/controller/physical.c
index 9d3741051..9cc7eb682 100644
--- a/ovn/controller/physical.c
+++ b/ovn/controller/physical.c
@@ -218,7 +218,7 @@ put_local_common_flows(uint32_t dp_key, uint32_t port_key,
/* Resubmit to table 34. */
put_resubmit(OFTABLE_CHECK_LOOPBACK, ofpacts_p);
- ofctrl_add_flow(flow_table, OFTABLE_LOCAL_OUTPUT, 100,
+ ofctrl_add_flow(flow_table, OFTABLE_LOCAL_OUTPUT, 100, 0,
&match, ofpacts_p);
/* Table 34, Priority 100.
@@ -233,7 +233,7 @@ put_local_common_flows(uint32_t dp_key, uint32_t port_key,
0, MLF_ALLOW_LOOPBACK);
match_set_reg(&match, MFF_LOG_INPORT - MFF_REG0, port_key);
match_set_reg(&match, MFF_LOG_OUTPORT - MFF_REG0, port_key);
- ofctrl_add_flow(flow_table, OFTABLE_CHECK_LOOPBACK, 100,
+ ofctrl_add_flow(flow_table, OFTABLE_CHECK_LOOPBACK, 100, 0,
&match, ofpacts_p);
/* Table 64, Priority 100.
@@ -257,7 +257,8 @@ put_local_common_flows(uint32_t dp_key, uint32_t port_key,
put_load(0, MFF_IN_PORT, 0, 16, ofpacts_p);
put_resubmit(OFTABLE_LOG_TO_PHY, ofpacts_p);
put_stack(MFF_IN_PORT, ofpact_put_STACK_POP(ofpacts_p));
- ofctrl_add_flow(flow_table, OFTABLE_SAVE_INPORT, 100, &match, ofpacts_p);
+ ofctrl_add_flow(flow_table, OFTABLE_SAVE_INPORT, 100, 0,
+ &match, ofpacts_p);
}
static void
@@ -346,7 +347,7 @@ consider_port_binding(enum mf_field_id mff_ovn_geneve,
ofpacts_p->header = clone;
ofpact_finish_CLONE(ofpacts_p, &clone);
- ofctrl_add_flow(flow_table, OFTABLE_LOG_TO_PHY, 100,
+ ofctrl_add_flow(flow_table, OFTABLE_LOG_TO_PHY, 100, 0,
&match, ofpacts_p);
return;
}
@@ -471,7 +472,7 @@ consider_port_binding(enum mf_field_id mff_ovn_geneve,
/* Resubmit to first logical ingress pipeline table. */
put_resubmit(OFTABLE_LOG_INGRESS_PIPELINE, ofpacts_p);
ofctrl_add_flow(flow_table, OFTABLE_PHY_TO_LOG,
- tag ? 150 : 100, &match, ofpacts_p);
+ tag ? 150 : 100, 0, &match, ofpacts_p);
if (!tag && (!strcmp(binding->type, "localnet")
|| !strcmp(binding->type, "l2gateway"))) {
@@ -481,7 +482,7 @@ consider_port_binding(enum mf_field_id mff_ovn_geneve,
* action. */
ofpbuf_pull(ofpacts_p, ofpacts_orig_size);
match_set_dl_tci_masked(&match, 0, htons(VLAN_CFI));
- ofctrl_add_flow(flow_table, 0, 100, &match, ofpacts_p);
+ ofctrl_add_flow(flow_table, 0, 100, 0, &match, ofpacts_p);
}
/* Table 65, Priority 100.
@@ -508,7 +509,7 @@ consider_port_binding(enum mf_field_id mff_ovn_geneve,
* switch will also contain the tag. */
ofpact_put_STRIP_VLAN(ofpacts_p);
}
- ofctrl_add_flow(flow_table, OFTABLE_LOG_TO_PHY, 100,
+ ofctrl_add_flow(flow_table, OFTABLE_LOG_TO_PHY, 100, 0,
&match, ofpacts_p);
} else if (!tun) {
/* Remote port connected by localnet port */
@@ -531,7 +532,7 @@ consider_port_binding(enum mf_field_id mff_ovn_geneve,
/* Resubmit to table 33. */
put_resubmit(OFTABLE_LOCAL_OUTPUT, ofpacts_p);
- ofctrl_add_flow(flow_table, OFTABLE_LOCAL_OUTPUT, 100,
+ ofctrl_add_flow(flow_table, OFTABLE_LOCAL_OUTPUT, 100, 0,
&match, ofpacts_p);
} else {
/* Remote port connected by tunnel */
@@ -555,7 +556,7 @@ consider_port_binding(enum mf_field_id mff_ovn_geneve,
/* Output to tunnel. */
ofpact_put_OUTPUT(ofpacts_p)->port = ofport;
- ofctrl_add_flow(flow_table, OFTABLE_REMOTE_OUTPUT, 100,
+ ofctrl_add_flow(flow_table, OFTABLE_REMOTE_OUTPUT, 100, 0,
&match, ofpacts_p);
}
}
@@ -643,7 +644,7 @@ consider_mc_group(enum mf_field_id mff_ovn_geneve,
* group as the logical output port. */
put_load(mc->tunnel_key, MFF_LOG_OUTPORT, 0, 32, ofpacts_p);
- ofctrl_add_flow(flow_table, OFTABLE_LOCAL_OUTPUT, 100,
+ ofctrl_add_flow(flow_table, OFTABLE_LOCAL_OUTPUT, 100, 0,
&match, ofpacts_p);
}
@@ -681,7 +682,7 @@ consider_mc_group(enum mf_field_id mff_ovn_geneve,
if (local_ports) {
put_resubmit(OFTABLE_LOCAL_OUTPUT, remote_ofpacts_p);
}
- ofctrl_add_flow(flow_table, OFTABLE_REMOTE_OUTPUT, 100,
+ ofctrl_add_flow(flow_table, OFTABLE_REMOTE_OUTPUT, 100, 0,
&match, remote_ofpacts_p);
}
}
@@ -882,7 +883,8 @@ physical_run(struct controller_ctx *ctx, enum mf_field_id mff_ovn_geneve,
put_resubmit(OFTABLE_LOCAL_OUTPUT, &ofpacts);
- ofctrl_add_flow(flow_table, OFTABLE_PHY_TO_LOG, 100, &match, &ofpacts);
+ ofctrl_add_flow(flow_table, OFTABLE_PHY_TO_LOG, 100, 0, &match,
+ &ofpacts);
}
/* Add flows for VXLAN encapsulations. Due to the limited amount of
@@ -914,7 +916,7 @@ physical_run(struct controller_ctx *ctx, enum mf_field_id mff_ovn_geneve,
put_load(1, MFF_LOG_FLAGS, MLF_RCV_FROM_VXLAN_BIT, 1, &ofpacts);
put_resubmit(OFTABLE_LOG_INGRESS_PIPELINE, &ofpacts);
- ofctrl_add_flow(flow_table, OFTABLE_PHY_TO_LOG, 100, &match,
+ ofctrl_add_flow(flow_table, OFTABLE_PHY_TO_LOG, 100, 0, &match,
&ofpacts);
}
}
@@ -935,7 +937,8 @@ physical_run(struct controller_ctx *ctx, enum mf_field_id mff_ovn_geneve,
/* Resubmit to table 33. */
put_resubmit(OFTABLE_LOCAL_OUTPUT, &ofpacts);
- ofctrl_add_flow(flow_table, OFTABLE_REMOTE_OUTPUT, 150, &match, &ofpacts);
+ ofctrl_add_flow(flow_table, OFTABLE_REMOTE_OUTPUT, 150, 0,
+ &match, &ofpacts);
/* Table 32, Priority 0.
* =======================
@@ -945,7 +948,7 @@ physical_run(struct controller_ctx *ctx, enum mf_field_id mff_ovn_geneve,
match_init_catchall(&match);
ofpbuf_clear(&ofpacts);
put_resubmit(OFTABLE_LOCAL_OUTPUT, &ofpacts);
- ofctrl_add_flow(flow_table, OFTABLE_REMOTE_OUTPUT, 0, &match, &ofpacts);
+ ofctrl_add_flow(flow_table, OFTABLE_REMOTE_OUTPUT, 0, 0, &match, &ofpacts);
/* Table 34, Priority 0.
* =======================
@@ -959,7 +962,8 @@ physical_run(struct controller_ctx *ctx, enum mf_field_id mff_ovn_geneve,
put_load(0, MFF_REG0 + i, 0, 32, &ofpacts);
}
put_resubmit(OFTABLE_LOG_EGRESS_PIPELINE, &ofpacts);
- ofctrl_add_flow(flow_table, OFTABLE_CHECK_LOOPBACK, 0, &match, &ofpacts);
+ ofctrl_add_flow(flow_table, OFTABLE_CHECK_LOOPBACK, 0, 0, &match,
+ &ofpacts);
/* Table 64, Priority 0.
* =======================
@@ -969,7 +973,7 @@ physical_run(struct controller_ctx *ctx, enum mf_field_id mff_ovn_geneve,
match_init_catchall(&match);
ofpbuf_clear(&ofpacts);
put_resubmit(OFTABLE_LOG_TO_PHY, &ofpacts);
- ofctrl_add_flow(flow_table, OFTABLE_SAVE_INPORT, 0, &match, &ofpacts);
+ ofctrl_add_flow(flow_table, OFTABLE_SAVE_INPORT, 0, 0, &match, &ofpacts);
ofpbuf_uninit(&ofpacts);
diff --git a/ovn/ovn-architecture.7.xml b/ovn/ovn-architecture.7.xml
index d96e4b141..b049e51f0 100644
--- a/ovn/ovn-architecture.7.xml
+++ b/ovn/ovn-architecture.7.xml
@@ -846,6 +846,34 @@
</p>
<p>
+ Each logical flow maps to one or more OpenFlow flows. An actual packet
+ ordinarily matches only one of these, although in some cases it can
+ match more than one of these flows (which is not a problem because all
+ of them have the same actions). <code>ovn-controller</code> uses the
+ first 32 bits of the logical flow's UUID as the cookie for its OpenFlow
+ flow or flows. (This is not necessarily unique, since the first 32
+ bits of a logical flow's UUID is not necessarily unique.)
+ </p>
+
+ <p>
+ Some logical flows can map to the Open vSwitch ``conjunctive match''
+ extension (see <code>ovs-ofctl</code>(8)). Flows with a
+ <code>conjunction</code> action use an OpenFlow cookie of 0, because
+ they can correspond to multiple logical flows. The OpenFlow flow for a
+ conjunctive match includes a match on <code>conj_id</code>.
+ </p>
+
+ <p>
+ Some logical flows may not be represented in the OpenFlow tables on a
+ given hypervisor, if they could not be used on that hypervisor. For
+ example, if no VIF in a logical switch resides on a given hypervisor,
+ and the logical switch is not otherwise reachable on that hypervisor
+ (e.g. over a series of hops through logical switches and routers
+ starting from a VIF on the hypervisor), then the logical flow may not
+ be represented there.
+ </p>
+
+ <p>
Most OVN actions have fairly obvious implementations in OpenFlow (with
OVS extensions), e.g. <code>next;</code> is implemented as
<code>resubmit</code>, <code><var>field</var> =
diff --git a/ovn/utilities/ovn-sbctl.8.in b/ovn/utilities/ovn-sbctl.8.in
index 783afc772..a87635de8 100644
--- a/ovn/utilities/ovn-sbctl.8.in
+++ b/ovn/utilities/ovn-sbctl.8.in
@@ -160,11 +160,25 @@ to unbind logical port that is not bound has no effect.
.
.SS "Logical Flow Commands"
.
-.IP "\fBlflow\-list\fR [\fIlogical-datapath\fR]"
+.IP "[\fB\-\-uuid\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.
+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
+datapaths have the same name).
+.IP
+If at least one \fIlflow\fR is given, only matching logical flows, if
+any, are listed. Each \fIlflow\fR may be specified as a UUID or the
+first few characters of a UUID, optionally prefixed by \fB0x\fR.
+(Because \fBovn\-controller\fR sets OpenFlow flow cookies to the first
+32 bits of the corresponding logical flow's UUID, this makes it easy
+to look up the logical flow that generated a particular OpenFlow
+flow.)
+.IP
+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 "\fBdump\-flows\fR [\fIlogical-datapath\fR]"
+.IP "[\fB\-\-uuid\fR] \fBdump\-flows\fR [\fIlogical-datapath\fR]"
Alias for \fBlflow\-list\fB.
.
.SS "Remote Connectivity Commands"
diff --git a/ovn/utilities/ovn-sbctl.c b/ovn/utilities/ovn-sbctl.c
index c190635b0..9e3016b65 100644
--- a/ovn/utilities/ovn-sbctl.c
+++ b/ovn/utilities/ovn-sbctl.c
@@ -304,8 +304,8 @@ Port binding commands:\n\
lsp-unbind PORT reset the port binding of logical port PORT\n\
\n\
Logical flow commands:\n\
- lflow-list [DATAPATH] List logical flows for all or a single datapath\n\
- dump-flows [DATAPATH] Alias for lflow-list\n\
+ lflow-list [DATAPATH] [LFLOW...] List logical flows for DATAPATH\n\
+ dump-flows [DATAPATH] [LFLOW...] Alias for lflow-list\n\
\n\
Connection commands:\n\
get-connection print the connections\n\
@@ -695,53 +695,148 @@ lflow_cmp(const void *lf1_, const void *lf2_)
return 0;
}
+static char *
+parse_partial_uuid(char *s)
+{
+ /* Accept a full or partial UUID. */
+ if (uuid_is_partial_string(s) == strlen(s)) {
+ return s;
+ }
+
+ /* Accept a full or partial UUID prefixed by 0x, since "ovs-ofctl
+ * dump-flows" prints cookies prefixed by 0x. */
+ if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')
+ && uuid_is_partial_string(s + 2) == strlen(s + 2)) {
+ return s + 2;
+ }
+
+ /* Not a (partial) UUID. */
+ return NULL;
+}
+
+static bool
+is_partial_uuid_match(const struct uuid *uuid, const char *match)
+{
+ char uuid_s[UUID_LEN + 1];
+ snprintf(uuid_s, sizeof uuid_s, UUID_FMT, UUID_ARGS(uuid));
+
+ return !strncmp(uuid_s, match, strlen(match));
+}
+
+static const struct sbrec_datapath_binding *
+lookup_datapath(struct ovsdb_idl *idl, const char *s)
+{
+ struct uuid uuid;
+ if (uuid_from_string(&uuid, s)) {
+ const struct sbrec_datapath_binding *datapath;
+ datapath = sbrec_datapath_binding_get_for_uuid(idl, &uuid);
+ if (datapath) {
+ return datapath;
+ }
+ }
+
+ const struct sbrec_datapath_binding *found = NULL;
+ const struct sbrec_datapath_binding *datapath;
+ SBREC_DATAPATH_BINDING_FOR_EACH (datapath, idl) {
+ const char *name = smap_get(&datapath->external_ids, "name");
+ if (name && !strcmp(name, s)) {
+ if (!found) {
+ found = datapath;
+ } else {
+ ctl_fatal("%s: multiple datapaths with this name", s);
+ }
+ }
+ }
+ return found;
+}
+
static void
cmd_lflow_list(struct ctl_context *ctx)
{
- const char *datapath = ctx->argc == 2 ? ctx->argv[1] : NULL;
- struct uuid datapath_uuid = { .parts = { 0, }};
- const struct sbrec_logical_flow **lflows;
- const struct sbrec_logical_flow *lflow;
- size_t n_flows = 0, n_capacity = 64;
+ const struct sbrec_datapath_binding *datapath = NULL;
+ if (ctx->argc > 1) {
+ datapath = lookup_datapath(ctx->idl, ctx->argv[1]);
+ if (datapath) {
+ ctx->argc--;
+ ctx->argv++;
+ }
+ }
- if (datapath && !uuid_from_string(&datapath_uuid, datapath)) {
- VLOG_ERR("Invalid format of datapath UUID");
- return;
+ for (size_t i = 1; i < ctx->argc; i++) {
+ char *s = parse_partial_uuid(ctx->argv[i]);
+ if (!s) {
+ ctl_fatal("%s is not a UUID or the beginning of a UUID",
+ ctx->argv[i]);
+ }
+ ctx->argv[i] = s;
}
- lflows = xmalloc(sizeof *lflows * n_capacity);
+ const struct sbrec_logical_flow **lflows = NULL;
+ size_t n_flows = 0;
+ size_t n_capacity = 0;
+ const struct sbrec_logical_flow *lflow;
SBREC_LOGICAL_FLOW_FOR_EACH (lflow, ctx->idl) {
+ if (datapath && lflow->logical_datapath != datapath) {
+ continue;
+ }
+
if (n_flows == n_capacity) {
lflows = x2nrealloc(lflows, &n_capacity, sizeof *lflows);
}
lflows[n_flows] = lflow;
n_flows++;
}
-
qsort(lflows, n_flows, sizeof *lflows, lflow_cmp);
- const char *cur_pipeline = "";
- size_t i;
- for (i = 0; i < n_flows; i++) {
+ bool print_uuid = shash_find(&ctx->options, "--uuid") != NULL;
+
+ const struct sbrec_logical_flow *prev = NULL;
+ for (size_t i = 0; i < n_flows; i++) {
lflow = lflows[i];
- if (datapath && !uuid_equals(&datapath_uuid,
- &lflow->logical_datapath->header_.uuid)) {
+
+ /* Figure out whether to print this particular flow. By default, we
+ * print all flows, but if any UUIDs were listed on the command line
+ * then we only print the matching ones. */
+ bool include;
+ if (ctx->argc > 1) {
+ include = false;
+ for (size_t j = 1; j < ctx->argc; j++) {
+ if (is_partial_uuid_match(&lflow->header_.uuid,
+ ctx->argv[j])) {
+ include = true;
+ break;
+ }
+ }
+ } else {
+ include = true;
+ }
+ if (!include) {
continue;
}
- if (strcmp(cur_pipeline, lflow->pipeline)) {
+
+ /* Print a header line for this datapath or pipeline, if we haven't
+ * already done so. */
+ if (!prev
+ || prev->logical_datapath != lflow->logical_datapath
+ || strcmp(prev->pipeline, lflow->pipeline)) {
printf("Datapath: \"%s\" ("UUID_FMT") Pipeline: %s\n",
smap_get_def(&lflow->logical_datapath->external_ids,
"name", ""),
UUID_ARGS(&lflow->logical_datapath->header_.uuid),
lflow->pipeline);
- cur_pipeline = lflow->pipeline;
}
- printf(" table=%-2" PRId64 "(%-19s), priority=%-5" PRId64
+ /* Print the flow. */
+ printf(" ");
+ if (print_uuid) {
+ printf("uuid=0x%08"PRIx32", ", lflow->header_.uuid.parts[0]);
+ }
+ printf("table=%-2"PRId64"(%-19s), priority=%-5"PRId64
", match=(%s), action=(%s)\n",
lflow->table_id,
smap_get_def(&lflow->external_ids, "stage-name", ""),
lflow->priority, lflow->match, lflow->actions);
+ prev = lflow;
}
free(lflows);
@@ -1243,10 +1338,12 @@ static const struct ctl_command_syntax sbctl_commands[] = {
"--if-exists", RW},
/* Logical flow commands */
- {"lflow-list", 0, 1, "[DATAPATH]", pre_get_info, cmd_lflow_list, NULL,
- "", RO},
- {"dump-flows", 0, 1, "[DATAPATH]", pre_get_info, cmd_lflow_list, NULL,
- "", RO}, /* Friendly alias for lflow-list */
+ {"lflow-list", 0, INT_MAX, "[DATAPATH] [LFLOW...]",
+ pre_get_info, cmd_lflow_list, NULL,
+ "--uuid", RO},
+ {"dump-flows", 0, INT_MAX, "[DATAPATH] [LFLOW...]",
+ pre_get_info, cmd_lflow_list, NULL,
+ "--uuid", RO}, /* Friendly alias for lflow-list */
/* Connection commands. */
{"get-connection", 0, 0, "", pre_connection, cmd_get_connection, NULL, "", RO},