mirror of
https://github.com/openvswitch/ovs
synced 2025-09-05 00:35:33 +00:00
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>
This commit is contained in:
22
lib/uuid.c
22
lib/uuid.c
@@ -1,4 +1,4 @@
|
||||
/* Copyright (c) 2008, 2009, 2010, 2011, 2013 Nicira, Inc.
|
||||
/* Copyright (c) 2008, 2009, 2010, 2011, 2013, 2016 Nicira, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -210,6 +210,26 @@ error:
|
||||
uuid_zero(uuid);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Returns the number of characters at the beginning of 's' that are valid for
|
||||
* a UUID. For example, the "123" at the beginning of "123xyzzy" could begin a
|
||||
* UUID, so uuid_is_partial_string() would return 3; for "xyzzy", this function
|
||||
* would return 0, since "x" can't start a UUID. */
|
||||
int
|
||||
uuid_is_partial_string(const char *s)
|
||||
{
|
||||
static const char tmpl[UUID_LEN] = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx";
|
||||
size_t i;
|
||||
for (i = 0; i < UUID_LEN; i++) {
|
||||
if (tmpl[i] == 'x'
|
||||
? hexit_value(s[i]) < 0
|
||||
: s[i] != '-') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
sha1_update_int(struct sha1_ctx *sha1_ctx, uintmax_t x)
|
||||
|
@@ -1,4 +1,4 @@
|
||||
/* Copyright (c) 2008, 2009, 2010 Nicira, Inc.
|
||||
/* Copyright (c) 2008, 2009, 2010, 2016 Nicira, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -61,6 +61,7 @@ bool uuid_is_zero(const struct uuid *);
|
||||
int uuid_compare_3way(const struct uuid *, const struct uuid *);
|
||||
bool uuid_from_string(struct uuid *, const char *);
|
||||
bool uuid_from_string_prefix(struct uuid *, const char *);
|
||||
int uuid_is_partial_string(const char *);
|
||||
void uuid_set_bits_v4(struct uuid *);
|
||||
|
||||
#endif /* uuid.h */
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
|
@@ -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);
|
||||
|
@@ -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);
|
||||
|
||||
|
@@ -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);
|
||||
|
||||
|
@@ -845,6 +845,34 @@
|
||||
through 31).
|
||||
</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
|
||||
|
@@ -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"
|
||||
|
@@ -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;
|
||||
|
||||
if (datapath && !uuid_from_string(&datapath_uuid, datapath)) {
|
||||
VLOG_ERR("Invalid format of datapath UUID");
|
||||
return;
|
||||
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++;
|
||||
}
|
||||
}
|
||||
|
||||
lflows = xmalloc(sizeof *lflows * n_capacity);
|
||||
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;
|
||||
}
|
||||
|
||||
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},
|
||||
|
Reference in New Issue
Block a user