I frequently view the contents of the Logical_Flow table while working on OVN. Add a patch 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 <rbry...@redhat.com> --- ovn/utilities/ovn-sbctl.8.in | 6 +++ ovn/utilities/ovn-sbctl.c | 94 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 100 insertions(+) Example output: $ ./ovn-2port-setup.sh + ovn-nbctl lswitch-add sw0 + ovn-nbctl lport-add sw0 sw0-port1 + ovn-nbctl lport-add sw0 sw0-port2 + ovn-nbctl lport-set-macs sw0-port1 00:00:00:00:00:01 + ovn-nbctl lport-set-macs sw0-port2 00:00:00:00:00:02 + ovn-nbctl lport-set-port-security sw0-port1 00:00:00:00:00:01 + ovn-nbctl lport-set-port-security sw0-port2 00:00:00:00:00:02 + ovs-vsctl add-port br-int lport1 -- set Interface lport1 external_ids:iface-id=sw0-port1 + ovs-vsctl add-port br-int lport2 -- set Interface lport2 external_ids:iface-id=sw0-port2 $ ovn-sbctl lflow-list Datapath: 03f8a791-b5bb-47b6-b373-09b75b8e26f3 Pipeline: ingress table_id=0, priority=100, match=(eth.src[40]), action=(drop;) table_id=0, priority=100, match=(vlan.present), action=(drop;) table_id=0, priority= 50, match=(inport == "sw0-port1" && eth.src == {00:00:00:00:00:01}), action=(next;) table_id=0, priority= 50, match=(inport == "sw0-port2" && eth.src == {00:00:00:00:00:02}), action=(next;) table_id=0, priority= 0, match=(1), action=(drop;) table_id=1, priority=100, match=(eth.dst[40]), action=(outport = "_MC_flood"; output;) table_id=1, priority= 50, match=(eth.dst == 00:00:00:00:00:01), action=(outport = "sw0-port1"; output;) table_id=1, priority= 50, match=(eth.dst == 00:00:00:00:00:02), action=(outport = "sw0-port2"; output;) Datapath: 03f8a791-b5bb-47b6-b373-09b75b8e26f3 Pipeline: egress table_id=0, priority= 0, match=(1), action=(next;) table_id=1, priority=100, match=(eth.dst[40]), action=(output;) table_id=1, priority= 50, match=(outport == "sw0-port1" && eth.dst == {00:00:00:00:00:01}), action=(output;) table_id=1, priority= 50, match=(outport == "sw0-port2" && eth.dst == {00:00:00:00:00:02}), action=(output;) diff --git a/ovn/utilities/ovn-sbctl.8.in b/ovn/utilities/ovn-sbctl.8.in index b5c796e..9f9168e 100644 --- a/ovn/utilities/ovn-sbctl.8.in +++ b/ovn/utilities/ovn-sbctl.8.in @@ -146,6 +146,12 @@ 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. +. .so lib/db-ctl-base.man .SH "EXIT STATUS" .IP "0" diff --git a/ovn/utilities/ovn-sbctl.c b/ovn/utilities/ovn-sbctl.c index cbde60a..0bcb795 100644 --- a/ovn/utilities/ovn-sbctl.c +++ b/ovn/utilities/ovn-sbctl.c @@ -300,6 +300,9 @@ 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\ +\n\ %s\ \n\ Options:\n\ @@ -470,6 +473,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[] = { @@ -584,6 +594,86 @@ cmd_lport_unbind(struct ctl_context *ctx) } } +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_); + +#define CMP(expr) \ + do { \ + int res; \ + res = (expr); \ + if (res) { \ + return res; \ + } \ + } while (0) + + CMP(memcmp(&lf1->logical_datapath->header_.uuid, + &lf2->logical_datapath->header_.uuid, + sizeof(lf1->logical_datapath->header_.uuid))); + CMP(strcmp(lf1->pipeline, lf2->pipeline) * -1); + 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) { + n_capacity *= 2; + lflows = xrealloc(lflows, sizeof *lflows * n_capacity); + } + 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, @@ -858,6 +948,10 @@ 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}, + /* SSL commands (To Be Added). */ {NULL, 0, 0, NULL, NULL, NULL, NULL, NULL, RO}, -- 2.4.3 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev