This commit adds the binding module to ovn-controller-vtep.  The
module will scan through the Binding table in ovnsb.  If there is
a binding for the logical port on vtep gw, sets the binding's chassis
column to the corresponding vtep gw's chassis.

Note, each logical datapath can only have up to one logical port from
each 'vlan_map' (in Gateway table) attached to it.

Signed-off-by: Alex Wang <al...@nicira.com>

---
PATCH->V2:
- split into separate commit.
- disallow and warn if more than one logical port from one 'vlan_map'
  are attached to the same logical datapath.
---
 ovn/controller-vtep/automake.mk           |    2 +
 ovn/controller-vtep/binding.c             |  199 +++++++++++++++++++++++++++++
 ovn/controller-vtep/binding.h             |   25 ++++
 ovn/controller-vtep/ovn-controller-vtep.c |    3 +
 ovn/ovn-sb.xml                            |   12 +-
 tests/ovn-controller-vtep.at              |   47 +++++++
 6 files changed, 286 insertions(+), 2 deletions(-)
 create mode 100644 ovn/controller-vtep/binding.c
 create mode 100644 ovn/controller-vtep/binding.h

diff --git a/ovn/controller-vtep/automake.mk b/ovn/controller-vtep/automake.mk
index 514cafa..33f063f 100644
--- a/ovn/controller-vtep/automake.mk
+++ b/ovn/controller-vtep/automake.mk
@@ -1,5 +1,7 @@
 bin_PROGRAMS += ovn/controller-vtep/ovn-controller-vtep
 ovn_controller_vtep_ovn_controller_vtep_SOURCES = \
+       ovn/controller-vtep/binding.c \
+       ovn/controller-vtep/binding.h \
        ovn/controller-vtep/gateway.c \
        ovn/controller-vtep/gateway.h \
        ovn/controller-vtep/ovn-controller-vtep.c \
diff --git a/ovn/controller-vtep/binding.c b/ovn/controller-vtep/binding.c
new file mode 100644
index 0000000..71b087e
--- /dev/null
+++ b/ovn/controller-vtep/binding.c
@@ -0,0 +1,199 @@
+/* Copyright (c) 2015 Nicira, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <config.h>
+#include "binding.h"
+
+#include "lib/hash.h"
+#include "lib/sset.h"
+#include "lib/util.h"
+#include "lib/uuid.h"
+#include "openvswitch/vlog.h"
+#include "ovn/lib/ovn-sb-idl.h"
+#include "vtep/vtep-idl.h"
+#include "ovn-controller-vtep.h"
+
+VLOG_DEFINE_THIS_MODULE(binding);
+
+/*
+ * This module scans through the Binding table in ovnsb.  If there is a
+ * row for the logical port on vtep gw, sets the binding's chassis column
+ * to the corresponding vtep gw's chassis.
+ *
+ */
+
+/* Checks and updates bindings for each physical switch in VTEP. */
+void
+binding_run(struct controller_vtep_ctx *ctx)
+{
+    const struct vteprec_physical_switch *pswitch;
+    struct ovsdb_idl_txn *txn;
+    int retval;
+
+    txn = ovsdb_idl_txn_create(ctx->ovnsb_idl);
+    ovsdb_idl_txn_add_comment(txn,
+                              "ovn-controller-vtep: updating bindings");
+
+    VTEPREC_PHYSICAL_SWITCH_FOR_EACH (pswitch, ctx->vtep_idl) {
+        const struct sbrec_chassis *chassis_rec
+            = get_chassis_by_name(ctx->ovnsb_idl, pswitch->name);
+        const struct sbrec_binding *binding_rec;
+        struct sset ldps_in_gw;
+        struct sset lports;
+        const char *name;
+        int i;
+
+        /* 'ldps_in_gw' is used to guarantee that each logical datapath
+         * can only have up to one logical port from each 'vlan_map'.
+         *
+         * If a lport in 'vlan_map' is first added to a logical datapath,
+         * we add a string which consists of
+         * 'pswitch_name+port_name+logical_datapath_uuid'.  Then for each
+         * lport, we always first check if there is already a lport in
+         * the same 'vlan_map' attached to the same logical datapath,
+         * which is not allowed!
+         * */
+        sset_init(&ldps_in_gw);
+        sset_init(&lports);
+        /* Collects all logical ports on the vtep gateway. */
+        for (i = 0; i < chassis_rec->n_gateway_ports; i++) {
+            const struct sbrec_gateway *gw_rec =
+                chassis_rec->value_gateway_ports[i];
+            int j;
+
+            for (j = 0; j < gw_rec->n_vlan_map; j++) {
+                sset_add(&lports, gw_rec->value_vlan_map[j]);
+            }
+        }
+
+        SBREC_BINDING_FOR_EACH(binding_rec, ctx->ovnsb_idl) {
+            if (sset_find_and_delete(&lports, binding_rec->logical_port)) {
+                char *lp_ldp;
+                int chunk;
+
+                /* Gets the length of '{pswitch_name}_{port_name}' according 
to lport format.
+                 * lport is formated as 'pswitch_port_vlanNum'. */
+                chunk = strrchr(binding_rec->logical_port, '_')
+                    - binding_rec->logical_port;
+                /* Constructs string "pswitch_port_logical_datapath". */
+                lp_ldp = xasprintf("%.*s_"UUID_FMT,
+                                   chunk, binding_rec->logical_port,
+                                   UUID_ARGS(&binding_rec->logical_datapath));
+                if (sset_find(&ldps_in_gw, lp_ldp)) {
+                    VLOG_WARN("Logical datapath ("UUID_FMT") already has "
+                              "logical port from the chassis_port "
+                              "(%.*s) attached to it, so clear the "
+                              "chassis column from binding (%s)",
+                              UUID_ARGS(&binding_rec->logical_datapath),
+                              chunk, binding_rec->logical_port,
+                              binding_rec->logical_port);
+                    sbrec_binding_set_chassis(binding_rec, NULL);
+                } else {
+                    if (binding_rec->chassis != chassis_rec) {
+                        if (binding_rec->chassis) {
+                            VLOG_DBG("Changing chassis for lport (%s) from "
+                                     "(%s) to (%s)",
+                                     binding_rec->logical_port,
+                                     binding_rec->chassis->name,
+                                     chassis_rec->name);
+                        }
+                        sbrec_binding_set_chassis(binding_rec, chassis_rec);
+                    }
+                    /* Records the attachment in 'ldps_in_gw'. */
+                    sset_add(&ldps_in_gw, lp_ldp);
+                }
+                free(lp_ldp);
+            } else if (binding_rec->chassis == chassis_rec) {
+                /* The logical port is removed from vtep gateway, so clear
+                 * the binding->chassis. */
+                sbrec_binding_set_chassis(binding_rec, NULL);
+            }
+        }
+        SSET_FOR_EACH (name, &lports) {
+            VLOG_DBG("No binding record for lport %s", name);
+        }
+        sset_destroy(&ldps_in_gw);
+        sset_destroy(&lports);
+    }
+
+    retval = ovsdb_idl_txn_commit_block(txn);
+    if (retval == TXN_ERROR) {
+        VLOG_INFO("Problem committing binding information: %s",
+                  ovsdb_idl_txn_status_to_string(retval));
+    }
+    ovsdb_idl_txn_destroy(txn);
+}
+
+/* Removes the chassis reference for each binding to the vtep gateway. */
+void
+binding_destroy(struct controller_vtep_ctx *ctx)
+{
+    struct hmap bd_map = HMAP_INITIALIZER(&bd_map);
+    const struct sbrec_binding *binding_rec;
+    int retval = TXN_TRY_AGAIN;
+
+    struct binding_hash_node {
+        struct hmap_node hmap_node;  /* Inside 'bd_map'. */
+        const struct sbrec_binding *binding;
+    };
+
+    /* Collects all bindings with chassis. */
+    SBREC_BINDING_FOR_EACH(binding_rec, ctx->ovnsb_idl) {
+        if (binding_rec->chassis) {
+            struct binding_hash_node *bd = xmalloc(sizeof *bd);
+
+            bd->binding = binding_rec;
+            hmap_insert(&bd_map, &bd->hmap_node,
+                        hash_string(binding_rec->chassis->name, 0));
+        }
+    }
+
+    while (retval != TXN_SUCCESS && retval != TXN_UNCHANGED) {
+        const struct vteprec_physical_switch *pswitch;
+        struct ovsdb_idl_txn *txn;
+
+        txn = ovsdb_idl_txn_create(ctx->ovnsb_idl);
+        ovsdb_idl_txn_add_comment(txn, "ovn-controller-vtep: removing 
bindings");
+
+        VTEPREC_PHYSICAL_SWITCH_FOR_EACH (pswitch, ctx->vtep_idl) {
+            const struct sbrec_chassis *chassis_rec
+                = get_chassis_by_name(ctx->ovnsb_idl, pswitch->name);
+            struct binding_hash_node *bd;
+
+            HMAP_FOR_EACH_WITH_HASH (bd, hmap_node,
+                                     hash_string(chassis_rec->name, 0),
+                                     &bd_map) {
+                if (!strcmp(bd->binding->chassis->name, chassis_rec->name)) {
+                    sbrec_binding_set_chassis(bd->binding, NULL);
+                }
+            }
+        }
+
+        retval = ovsdb_idl_txn_commit_block(txn);
+        if (retval == TXN_ERROR) {
+            VLOG_DBG("Problem removing binding: %s",
+                      ovsdb_idl_txn_status_to_string(retval));
+        }
+        ovsdb_idl_txn_destroy(txn);
+    }
+
+    struct binding_hash_node *iter, *next;
+
+    HMAP_FOR_EACH_SAFE (iter, next, hmap_node, &bd_map) {
+        hmap_remove(&bd_map, &iter->hmap_node);
+        free(iter);
+    }
+    hmap_destroy(&bd_map);
+}
diff --git a/ovn/controller-vtep/binding.h b/ovn/controller-vtep/binding.h
new file mode 100644
index 0000000..156465d
--- /dev/null
+++ b/ovn/controller-vtep/binding.h
@@ -0,0 +1,25 @@
+/* Copyright (c) 2015 Nicira, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#ifndef OVN_BINDING_H
+#define OVN_BINDING_H 1
+
+struct controller_vtep_ctx;
+
+void binding_run(struct controller_vtep_ctx *);
+void binding_destroy(struct controller_vtep_ctx *);
+
+#endif /* ovn/controller-gw/binding.h */
diff --git a/ovn/controller-vtep/ovn-controller-vtep.c 
b/ovn/controller-vtep/ovn-controller-vtep.c
index dbc754b..712116a 100644
--- a/ovn/controller-vtep/ovn-controller-vtep.c
+++ b/ovn/controller-vtep/ovn-controller-vtep.c
@@ -38,6 +38,7 @@
 #include "unixctl.h"
 #include "util.h"
 
+#include "binding.h"
 #include "gateway.h"
 #include "ovn-controller-vtep.h"
 
@@ -123,6 +124,7 @@ main(int argc, char *argv[])
         }
 
         gateway_run(&ctx);
+        binding_run(&ctx);
         unixctl_server_run(unixctl);
 
         unixctl_server_wait(unixctl);
@@ -137,6 +139,7 @@ main(int argc, char *argv[])
 
     unixctl_server_destroy(unixctl);
     gateway_destroy(&ctx);
+    binding_destroy(&ctx);
 
     ovsdb_idl_destroy(ctx.vtep_idl);
     ovsdb_idl_destroy(ctx.ovnsb_idl);
diff --git a/ovn/ovn-sb.xml b/ovn/ovn-sb.xml
index 6d74c9f..7ddf944 100644
--- a/ovn/ovn-sb.xml
+++ b/ovn/ovn-sb.xml
@@ -193,8 +193,16 @@
     </p>
 
     <column name="vlan_map">
-      Maps from a VLAN ID to a logical port name.  Thus, each named logical
-      port corresponds to one VLAN on the gateway port.
+      <p>
+        Maps from a VLAN ID to a logical port name.  Thus, each named logical
+        port corresponds to one VLAN on the gateway port.  The logical port 
name
+        is the combination of "physical_switch_name+physical_port+vlan_number".
+      </p>
+
+      <p>
+        Each logical datapath can only have up to one logical port from each
+        <ref column="vlan_map" table="Gateway"/> attached to it.
+      </p>
     </column>
   </table>
 
diff --git a/tests/ovn-controller-vtep.at b/tests/ovn-controller-vtep.at
index 63775a2..9b3ab72 100644
--- a/tests/ovn-controller-vtep.at
+++ b/tests/ovn-controller-vtep.at
@@ -161,3 +161,50 @@ AT_CHECK([ovn-sbctl --columns=vlan_map list gateway | cut 
-d ':' -f2 | tr -d ' '
 
 OVN_CONTROLLER_VTEP_STOP(["/Chassis for VTEP physical switch (br-vtep) 
disappears/d"])
 AT_CLEANUP
+
+
+# Tests binding updates.
+AT_SETUP([ovn-controller-vtep - test binding])
+OVN_CONTROLLER_VTEP_START
+
+# adds vlan_bindings to physical ports.
+AT_CHECK([vtep-ctl add-ls lswitch -- bind-ls br-vtep p0 100 lswitch -- bind-ls 
br-vtep p1 300 lswitch])
+# adds lport in ovn-nb db.
+for lport_name in br-vtep_p0_100 br-vtep_p1_300; do
+    AT_CHECK_UNQUOTED([ovn-nbctl lport-add br-test ${lport_name}])
+done
+OVS_WAIT_UNTIL([test -n "`ovn-sbctl list Binding  | grep br-vtep_p1_300`"])
+
+chassis_uuid=$(ovn-sbctl --columns=_uuid list Chassis br-vtep | cut -d ':' -f2 
| tr -d ' ')
+for lport_name in br-vtep_p0_100 br-vtep_p1_300; do
+    AT_CHECK_UNQUOTED([ovn-sbctl --columns=chassis list Binding ${lport_name} 
| cut -d ':' -f2 | tr -d ' '], [0], [dnl
+${chassis_uuid}
+])
+done
+
+# adds br-vtep_p0_200 to lswitch, which is disallowed, since br-vtep_p0_100 
has already attached to lswitch.
+AT_CHECK([vtep-ctl bind-ls br-vtep p0 200 lswitch])
+AT_CHECK([ovn-nbctl lport-add br-test br-vtep_p0_200])
+OVS_WAIT_UNTIL([test -n "`ovn-sbctl list Binding  | grep br-vtep_p0_200`"])
+AT_CHECK([sed -n 's/^.*\(|WARN|.*\)$/\1/p' ovn-controller-vtep.log | sed 
's/([[-_0-9a-z]][[-_0-9a-z]]*)/()/g'], [0], [dnl
+|WARN|Logical datapath () already has logical port from the chassis_port () 
attached to it, so clear the chassis column from binding ()
+])
+# chassis column should be empty, since disallow this.
+cleared_lport=$(sed -n 's/^.*|WARN|.*(\(.*\))$/\1/p' ovn-controller-vtep.log)
+AT_CHECK_UNQUOTED([ovn-sbctl --columns=chassis list Binding ${cleared_lport} | 
cut -d ':' -f2 | tr -d ' '], [0], [dnl
+[[]]
+])
+
+# deletes physical ports from vtep.
+AT_CHECK([ovs-vsctl del-port p0 -- del-port p1])
+AT_CHECK([vtep-ctl del-port br-vtep p0 -- del-port br-vtep p1])
+OVS_WAIT_UNTIL([test -z "`ovn-sbctl list Gateway | grep uuid`"])
+
+for lport_name in br-vtep_p0_100 br-vtep_p0_200 br-vtep_p1_300; do
+    AT_CHECK_UNQUOTED([ovn-sbctl --columns=chassis list Binding ${lport_name} 
| cut -d ':' -f2 | tr -d ' '], [0], [dnl
+[[]]
+])
+done
+
+OVN_CONTROLLER_VTEP_STOP(["/already has logical port from the chassis_port/d"])
+AT_CLEANUP
-- 
1.7.9.5

_______________________________________________
dev mailing list
dev@openvswitch.org
http://openvswitch.org/mailman/listinfo/dev

Reply via email to