[ovs-dev] [ovn-controller-vtep V4 5/6] ovn-controller-vtep: Add binding module.

Alex Wang alexw at nicira.com
Thu Jul 16 07:56:26 UTC 2015


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 a logical port in the vtep gateway chassis's
"vtep_logical_switches" map, sets the binding's chassis column to the
vtep gateway chassis.

Signed-off-by: Alex Wang <alexw at nicira.com>

---
V3->V4:
- rebase to master.

V2->V3:
- since ovn-sb schema changes (removal of Gateway table), the binding
  module code needs to be adapted.

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             |  194 +++++++++++++++++++++++++++++
 ovn/controller-vtep/binding.h             |   25 ++++
 ovn/controller-vtep/ovn-controller-vtep.c |    3 +
 tests/ovn-controller-vtep.at              |   48 +++++++
 5 files changed, 272 insertions(+)
 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..caf2a86
--- /dev/null
+++ b/ovn/controller-vtep/binding.c
@@ -0,0 +1,194 @@
+/* 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 gateway chassis's 'vtep_logical_switches'
+ * map sets the binding's chassis column to the vtep gateway chassis.
+ *
+ * Caution: each logical datapath can only have up to one logical port
+ *          attached to it from each vtep gateway.
+ *
+ */
+
+/* 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 attached_ldps;
+        struct sset gw_lports;
+        struct smap_node *iter;
+        const char *name;
+
+        /* 'attached_ldps' is used to guarantee that each logical datapath
+         * can only have up to one logical port attached from the same
+         * gateway chassis.
+         *
+         * If a lport is first added to a logical datapath, we add the
+         * logical datapath's uuid to 'attached_ldps' as string.  Then for
+         * each following lport, we always first check if the logical
+         * datapath has already been attached, and warn if it has.
+         * (since it is not allowed)!
+         *
+         */
+        sset_init(&attached_ldps);
+        sset_init(&gw_lports);
+        /* Collects all logical port names on the vtep gateway. */
+        SMAP_FOR_EACH (iter, &chassis_rec->vtep_logical_switches) {
+            sset_add(&gw_lports, iter->value);
+        }
+
+        SBREC_BINDING_FOR_EACH(binding_rec, ctx->ovnsb_idl) {
+            /* Finds a binding entry for the lport. */
+            if (sset_find_and_delete(&gw_lports, binding_rec->logical_port)) {
+                char *ldp_uuid;
+
+                /* Converts uuid to string. */
+                ldp_uuid = xasprintf(UUID_FMT,
+                                     UUID_ARGS(&binding_rec->logical_datapath));
+                /* Warns if the logical datapath has already
+                 * been attached. */
+                if (sset_find(&attached_ldps, ldp_uuid)) {
+                    VLOG_WARN("Logical datapath ("UUID_FMT") already has "
+                              "logical port from the same chassis "
+                              "(%s) attached to it, so clear the "
+                              "chassis column from binding (%s)",
+                              UUID_ARGS(&binding_rec->logical_datapath),
+                              chassis_rec->name,
+                              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 'attached_ldps'. */
+                    sset_add(&attached_ldps, ldp_uuid);
+                }
+                free(ldp_uuid);
+            } else if (binding_rec->chassis == chassis_rec) {
+                /* The logical port no longer exist, so clear
+                 * the binding->chassis. */
+                sbrec_binding_set_chassis(binding_rec, NULL);
+            }
+        }
+        SSET_FOR_EACH (name, &gw_lports) {
+            VLOG_DBG("No binding record for lport %s", name);
+        }
+        sset_destroy(&attached_ldps);
+        sset_destroy(&gw_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/tests/ovn-controller-vtep.at b/tests/ovn-controller-vtep.at
index 747e3ed..a0cae26 100644
--- a/tests/ovn-controller-vtep.at
+++ b/tests/ovn-controller-vtep.at
@@ -150,3 +150,51 @@ AT_CHECK([ovn-sbctl --columns=vtep_logical_switches list Chassis | cut -d ':' -f
 
 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 logical switch 'lswitch0' and vlan_bindings.
+AT_CHECK([vtep-ctl add-ls lswitch0 -- bind-ls br-vtep p0 100 lswitch0 -- bind-ls br-vtep p1 300 lswitch0])
+# adds lport in ovn-nb db.
+AT_CHECK_UNQUOTED([ovn-nbctl lport-add br-test br-vtep_lswitch0])
+OVS_WAIT_UNTIL([test -n "`ovn-sbctl list Binding  | grep br-vtep_lswitch0`"])
+# should see one binding.
+chassis_uuid=$(ovn-sbctl --columns=_uuid list Chassis br-vtep | cut -d ':' -f2 | tr -d ' ')
+AT_CHECK_UNQUOTED([ovn-sbctl --columns=chassis list Binding br-vtep_lswitch0 | cut -d ':' -f2 | tr -d ' '], [0], [dnl
+${chassis_uuid}
+])
+
+# adds another logical switch 'lswitch1' and vlan_bindings.
+AT_CHECK([vtep-ctl add-ls lswitch1 -- bind-ls br-vtep p0 200 lswitch1])
+# adds lport in ovn-nb db to the same logical switch.
+AT_CHECK_UNQUOTED([ovn-nbctl lport-add br-test br-vtep_lswitch1])
+OVS_WAIT_UNTIL([test -n "`ovn-sbctl list Binding | grep -- br-vtep_lswitch1`"])
+# should still see one binding, since it is not allowed to have more than
+# one logical port from same chassis attached to the same logical datapath
+# (logical switch in ovn-nb database).
+AT_CHECK_UNQUOTED([ovn-sbctl --columns=chassis list Binding | cut -d ':' -f2 | tr -d ' ' | sort -d], [0], [dnl
+
+[[]]
+${chassis_uuid}
+])
+# confirms the warning log.
+AT_CHECK([sed -n 's/^.*\(|WARN|.*\)$/\1/p' ovn-controller-vtep.log | sed 's/([[-_0-9a-z]][[-_0-9a-z]]*)/()/g' | uniq], [0], [dnl
+|WARN|Logical datapath () already has logical port from the same chassis () attached to it, so clear the chassis column from binding ()
+])
+
+# 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 Chassis | grep -- br-vtep_lswitch`"])
+# should see empty chassis column in both binding entries.
+AT_CHECK_UNQUOTED([ovn-sbctl --columns=chassis list Binding | cut -d ':' -f2 | tr -d ' ' | sort], [0], [dnl
+
+[[]]
+[[]]
+])
+
+OVN_CONTROLLER_VTEP_STOP(["/already has logical port from the same chassis/d"])
+AT_CLEANUP
-- 
1.7.9.5




More information about the dev mailing list