Skip to content

Commit

Permalink
ovn: Add lflow-list to ovn-sbctl.
Browse files Browse the repository at this point in the history
I frequently view the contents of the Logical_Flow table while working
on OVN.  Add a command that can output the contents of this table in a
sorted way that makes it easier to read through.  It's sorted by
logical datapath, pipeline, table id, priority, and match.

Signed-off-by: Russell Bryant <[email protected]>
Acked-by: Alex Wang <[email protected]>
  • Loading branch information
russellb authored and yew011 committed Aug 12, 2015
1 parent b30001c commit dc70b67
Show file tree
Hide file tree
Showing 2 changed files with 126 additions and 0 deletions.
9 changes: 9 additions & 0 deletions ovn/utilities/ovn-sbctl.8.in
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,15 @@ Without \fB\-\-if\-exists\fR, attempting to unbind a logical port
that is not bound is an error. With \fB\-\-if\-exists\fR, attempting
to unbind logical port that is not bound has no effect.
.
.SS "Logical Flow Commands"
.
.IP "\fBlflow\-list\fR [\fIlogical\-datapath\fR]"
List logical flows. If \fIlogical\-datapath\fR is specified, only list flows for
that logical datapath.
.
.IP "\fBdump\-flows\fR [\fIlogical\-datapath\fR]"
Alias for \fBlflow\-list\fB.
.
.so lib/db-ctl-base.man
.SH "EXIT STATUS"
.IP "0"
Expand Down
117 changes: 117 additions & 0 deletions ovn/utilities/ovn-sbctl.c
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,10 @@ Port binding commands:\n\
lport-bind LPORT CHASSIS bind logical port LPORT to CHASSIS\n\
lport-unbind LPORT reset the port binding of logical port LPORT\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\
\n\
%s\
\n\
Options:\n\
Expand Down Expand Up @@ -470,6 +474,13 @@ pre_get_info(struct ctl_context *ctx)

ovsdb_idl_add_column(ctx->idl, &sbrec_port_binding_col_logical_port);
ovsdb_idl_add_column(ctx->idl, &sbrec_port_binding_col_chassis);

ovsdb_idl_add_column(ctx->idl, &sbrec_logical_flow_col_logical_datapath);
ovsdb_idl_add_column(ctx->idl, &sbrec_logical_flow_col_pipeline);
ovsdb_idl_add_column(ctx->idl, &sbrec_logical_flow_col_actions);
ovsdb_idl_add_column(ctx->idl, &sbrec_logical_flow_col_priority);
ovsdb_idl_add_column(ctx->idl, &sbrec_logical_flow_col_table_id);
ovsdb_idl_add_column(ctx->idl, &sbrec_logical_flow_col_match);
}

static struct cmd_show_table cmd_show_tables[] = {
Expand Down Expand Up @@ -584,6 +595,106 @@ cmd_lport_unbind(struct ctl_context *ctx)
}
}

enum {
PL_INGRESS,
PL_EGRESS,
};

/* Help ensure we catch any future pipeline values */
static int
pipeline_encode(const char *pl)
{
if (!strcmp(pl, "ingress")) {
return PL_INGRESS;
} else if (!strcmp(pl, "egress")) {
return PL_EGRESS;
}

OVS_NOT_REACHED();
}

static int
lflow_cmp(const void *lf1_, const void *lf2_)
{
const struct sbrec_logical_flow *lf1, *lf2;

lf1 = *((struct sbrec_logical_flow **) lf1_);
lf2 = *((struct sbrec_logical_flow **) lf2_);

int pl1 = pipeline_encode(lf1->pipeline);
int pl2 = pipeline_encode(lf2->pipeline);

#define CMP(expr) \
do { \
int res; \
res = (expr); \
if (res) { \
return res; \
} \
} while (0)

CMP(uuid_compare_3way(&lf1->logical_datapath->header_.uuid,
&lf2->logical_datapath->header_.uuid));
CMP(pl1 - pl2);
CMP(lf1->table_id > lf2->table_id ? 1 :
(lf1->table_id < lf2->table_id ? -1 : 0));
CMP(lf1->priority > lf2->priority ? -1 :
(lf1->priority < lf2->priority ? 1 : 0));
CMP(strcmp(lf1->match, lf2->match));

#undef CMP

return 0;
}

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;
}

lflows = xmalloc(sizeof *lflows * n_capacity);
SBREC_LOGICAL_FLOW_FOR_EACH (lflow, ctx->idl) {
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++) {
lflow = lflows[i];
if (datapath && !uuid_equals(&datapath_uuid,
&lflow->logical_datapath->header_.uuid)) {
continue;
}
if (strcmp(cur_pipeline, lflow->pipeline)) {
printf("Datapath: " UUID_FMT " Pipeline: %s\n",
UUID_ARGS(&lflow->logical_datapath->header_.uuid),
lflow->pipeline);
cur_pipeline = lflow->pipeline;
}
printf(" table_id=%" PRId64 ", priority=%3" PRId64 ", "
"match=(%s), action=(%s)\n",
lflow->table_id, lflow->priority,
lflow->match, lflow->actions);
}

free(lflows);
}


static const struct ctl_table_class tables[] = {
{&sbrec_table_chassis,
Expand Down Expand Up @@ -858,6 +969,12 @@ static const struct ctl_command_syntax sbctl_commands[] = {
{"lport-unbind", 1, 1, "LPORT", pre_get_info, cmd_lport_unbind, NULL,
"--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 */

/* SSL commands (To Be Added). */

{NULL, 0, 0, NULL, NULL, NULL, NULL, NULL, RO},
Expand Down

0 comments on commit dc70b67

Please sign in to comment.