[ovs-dev] [PATCH v3] ovn: Add lflow-list to ovn-sbctl.

Russell Bryant rbryant at redhat.com
Wed Aug 12 23:20:17 UTC 2015


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 <rbryant at redhat.com>
---
 ovn/utilities/ovn-sbctl.8.in |   9 ++++
 ovn/utilities/ovn-sbctl.c    | 116 +++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 125 insertions(+)


v2->v3
 - Address additional feedbcak from Alex
v1->v2
 - Address Alex's review feedback
 - Add a friendly "dump-flows" alias

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: 0f9a9e4e-0ef0-4afb-bed4-09887387fd10  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: 0f9a9e4e-0ef0-4afb-bed4-09887387fd10  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..7e68d68 100644
--- a/ovn/utilities/ovn-sbctl.8.in
+++ b/ovn/utilities/ovn-sbctl.8.in
@@ -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"
diff --git a/ovn/utilities/ovn-sbctl.c b/ovn/utilities/ovn-sbctl.c
index cbde60a..45b2afe 100644
--- a/ovn/utilities/ovn-sbctl.c
+++ b/ovn/utilities/ovn-sbctl.c
@@ -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\
@@ -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[] = {
@@ -584,6 +595,105 @@ 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,
@@ -858,6 +968,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},
-- 
2.4.3




More information about the dev mailing list