This commit adds the gateway module to ovn-controller-vtep.  The
module will register the physical switches to ovnsb as chassis and
constantly update the gateway_ports entries in Chassis table and
the vlan_map in Gateway table.

Limitation:

- Do not support reading multiple tunnel ips of physical switch.

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

---
PATCH->V2:
- split into separate commit.
- can register all physical switches controlled by vtep database.
---
 ovn/controller-vtep/automake.mk                    |    2 +
 ovn/controller-vtep/gateway.c                      |  405 ++++++++++++++++++++
 .../{ovn-controller-vtep.h => gateway.h}           |   15 +-
 ovn/controller-vtep/ovn-controller-vtep.c          |    3 +
 ovn/controller-vtep/ovn-controller-vtep.h          |   18 +
 tests/ovn-controller-vtep.at                       |  162 ++++++++
 6 files changed, 596 insertions(+), 9 deletions(-)
 create mode 100644 ovn/controller-vtep/gateway.c
 copy ovn/controller-vtep/{ovn-controller-vtep.h => gateway.h} (72%)

diff --git a/ovn/controller-vtep/automake.mk b/ovn/controller-vtep/automake.mk
index 7adda15..514cafa 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/gateway.c \
+       ovn/controller-vtep/gateway.h \
        ovn/controller-vtep/ovn-controller-vtep.c \
        ovn/controller-vtep/ovn-controller-vtep.h
 ovn_controller_vtep_ovn_controller_vtep_LDADD = ovn/lib/libovn.la 
lib/libopenvswitch.la vtep/libvtep.la
diff --git a/ovn/controller-vtep/gateway.c b/ovn/controller-vtep/gateway.c
new file mode 100644
index 0000000..ac5ea9d
--- /dev/null
+++ b/ovn/controller-vtep/gateway.c
@@ -0,0 +1,405 @@
+/* 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 "gateway.h"
+
+#include "lib/hash.h"
+#include "lib/hmap.h"
+#include "lib/poll-loop.h"
+#include "lib/sset.h"
+#include "lib/util.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(gateway);
+
+/*
+ * Registers the physical switches in vtep to ovnsb as chassis, creates
+ * gateway_ports and Gateway table entry based on vtep physical port
+ * binding.
+ *
+ * The logical port name in vlan_map is defined as the combination of
+ * "{physical_switch_name}_{physical_port_name}_{vlan_number}".  This
+ * is to guarantee the uniqueness of the logical port.
+ *
+ */
+
+/* Global revalidation sequence number, incremented at each call to
+ * 'revalidate_gateway()'. */
+static uint64_t gw_reval_seq;
+
+/* Represents a chassis added by the gateway module.  The 'reval_seq'
+ * is increment each time a chassis is revalidated.  Chassis whose 'reval_seq'
+ * not equal to 'gw_reval_seq' will be removed. */
+struct gw_chassis {
+    struct hmap_node hmap_node; /* In 'gw_chassis'. */
+    char *name;                 /* Name of the Chassis. */
+    uint64_t reval_seq;         /* Chassis revalidation sequence number. */
+};
+
+/* Contains all chassis created by the gateway module. */
+static struct hmap gw_chassis_map = HMAP_INITIALIZER(&gw_chassis_map);
+
+/* Searchs 'gw_chassis_map' for chassis 'name' and returns the pointer.
+ * Returns NULL, if the chassis is not found. */
+static struct gw_chassis *
+get_gw_chassis(const char *name)
+{
+    struct gw_chassis *gw_chassis;
+
+    HMAP_FOR_EACH_WITH_HASH (gw_chassis, hmap_node, hash_string(name, 0),
+                             &gw_chassis_map) {
+        if (!strcmp(gw_chassis->name, name)) {
+            return gw_chassis;
+        }
+    }
+
+    return NULL;
+}
+
+/* Creates and returns a new instance of 'struct sbrec_chassis'. */
+static const struct sbrec_chassis *
+create_chassis_rec(struct ovsdb_idl_txn *txn, const char *name,
+                   const char *encap_ip)
+{
+    const struct sbrec_chassis *chassis_rec;
+    struct sbrec_encap *encap_rec;
+
+    chassis_rec = sbrec_chassis_insert(txn);
+    sbrec_chassis_set_name(chassis_rec, name);
+    encap_rec = sbrec_encap_insert(txn);
+    sbrec_encap_set_type(encap_rec, OVN_SB_ENCAP_TYPE);
+    sbrec_encap_set_ip(encap_rec, encap_ip);
+    sbrec_chassis_set_encaps(chassis_rec, &encap_rec, 1);
+
+    return chassis_rec;
+}
+
+/* Revalidates chassis in ovnsb against vtep.  Creates chassis for new
+ * vtep physical switch.  And removes chassis which no longer have physical
+ * switch in vtep.
+ *
+ * xxx: Support multiple tunnel encaps.
+ *
+ * */
+static void
+revalidate_gateway(struct controller_vtep_ctx *ctx)
+{
+    const struct vteprec_physical_switch *pswitch;
+    struct ovsdb_idl_txn *txn;
+    struct gw_chassis *iter, *next;
+    int retval;
+
+    /* Increments the global revalidation sequence number. */
+    gw_reval_seq++;
+
+    txn = ovsdb_idl_txn_create(ctx->ovnsb_idl);
+    ovsdb_idl_txn_add_comment(txn, "ovn-controller-vtep: updating vtep 
chassis");
+
+    VTEPREC_PHYSICAL_SWITCH_FOR_EACH (pswitch, ctx->vtep_idl) {
+        const struct sbrec_chassis *chassis_rec;
+        struct gw_chassis *gw_chassis;
+        const char *encap_ip;
+
+        encap_ip = pswitch->n_tunnel_ips ? pswitch->tunnel_ips[0] : "";
+        gw_chassis = get_gw_chassis(pswitch->name);
+        chassis_rec = get_chassis_by_name(ctx->ovnsb_idl, pswitch->name);
+        if (gw_chassis && !chassis_rec) {
+            VLOG_WARN("Chassis for VTEP physical switch (%s) disappears, "
+                      "maybe deleted by ovn-sbctl, adding it back",
+                      pswitch->name);
+            create_chassis_rec(txn, pswitch->name, encap_ip);
+        } else if (!gw_chassis && chassis_rec) {
+            VLOG_WARN("Chassis for new VTEP physical switch (%s) has already "
+                      "been added, maybe by ovn-sbctl", pswitch->name);
+            if (strcmp(chassis_rec->encaps[0]->type, OVN_SB_ENCAP_TYPE)
+                && strcmp(chassis_rec->encaps[0]->ip, encap_ip)) {
+                VLOG_WARN("Chassis config changing on startup, make sure "
+                          "multiple chassis are not configured : %s/%s->%s/%s",
+                          chassis_rec->encaps[0]->type,
+                          chassis_rec->encaps[0]->ip,
+                          OVN_SB_ENCAP_TYPE, encap_ip);
+                VLOG_WARN("Skip adding chassis for physical switch (%s)",
+                          pswitch->name);
+                continue;
+            }
+            gw_chassis = xmalloc(sizeof *gw_chassis);
+            gw_chassis->name = xstrdup(pswitch->name);
+            hmap_insert(&gw_chassis_map, &gw_chassis->hmap_node,
+                        hash_string(gw_chassis->name, 0));
+        } else if (gw_chassis && chassis_rec) {
+            /* Updates chassis's encap if anything changed. */
+            if (strcmp(chassis_rec->encaps[0]->type, OVN_SB_ENCAP_TYPE)) {
+                VLOG_WARN("Chassis for VTEP physical switch (%s) can only have 
"
+                          "encap type \"%s\"", pswitch->name, 
OVN_SB_ENCAP_TYPE);
+                sbrec_encap_set_type(chassis_rec->encaps[0], 
OVN_SB_ENCAP_TYPE);
+            }
+            if (strcmp(chassis_rec->encaps[0]->ip, encap_ip)) {
+                sbrec_encap_set_ip(chassis_rec->encaps[0], encap_ip);
+            }
+        } else {
+            /* Creates a new chassis for the VTEP physical switch and a new
+             * gw_chassis record. */
+            create_chassis_rec(txn, pswitch->name, encap_ip);
+            gw_chassis = xmalloc(sizeof *gw_chassis);
+            gw_chassis->name = xstrdup(pswitch->name);
+            hmap_insert(&gw_chassis_map, &gw_chassis->hmap_node,
+                        hash_string(gw_chassis->name, 0));
+        }
+        /* Updates the 'gw_chassis's revalidation seq number to prevent
+         * it from being garbage collected. */
+        gw_chassis->reval_seq = gw_reval_seq;
+    }
+
+    /* For 'gw_chassis' in 'gw_chassis_map' whose reval_seq is not
+     * 'gw_reval_seq', it means the corresponding physical switch no
+     * longer exist.  So, garbage collects them. */
+    HMAP_FOR_EACH_SAFE (iter, next, hmap_node, &gw_chassis_map) {
+        if (iter->reval_seq != gw_reval_seq) {
+            const struct sbrec_chassis *chassis_rec;
+
+            chassis_rec = get_chassis_by_name(ctx->ovnsb_idl, iter->name);
+            if (chassis_rec) {
+                sbrec_chassis_delete(chassis_rec);
+            }
+            hmap_remove(&gw_chassis_map, &iter->hmap_node);
+            free(iter->name);
+            free(iter);
+        }
+    }
+
+    retval = ovsdb_idl_txn_commit_block(txn);
+    if (retval != TXN_SUCCESS && retval != TXN_UNCHANGED) {
+        VLOG_INFO("Problem registering chassis: %s",
+                  ovsdb_idl_txn_status_to_string(retval));
+        poll_immediate_wake();
+    }
+    ovsdb_idl_txn_destroy(txn);
+}
+
+/* Updates the gateway_ports map in chassis table based on physical
+ * port configuration of each VTEP physical switch. */
+static void
+update_physical_ports(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 physcial ports");
+
+    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 sset sset = SSET_INITIALIZER(&sset);
+        struct sbrec_gateway **gws;
+        char **pp_names;
+        bool changed = false;
+        int idx = 0;
+        int i;
+
+        /* Collects all physical ports from physical switch. */
+        for (i = 0; i < pswitch->n_ports; i++) {
+            sset_add(&sset, pswitch->ports[i]->name);
+        }
+
+        gws = xmalloc(sizeof *gws * pswitch->n_ports);
+        pp_names = xmalloc(sizeof *pp_names * pswitch->n_ports);
+        /* Keeps existing rows. */
+        for (i = 0; i < chassis_rec->n_gateway_ports; i++) {
+            if (sset_find_and_delete(&sset, 
chassis_rec->key_gateway_ports[i])) {
+                pp_names[idx] = xstrdup(chassis_rec->key_gateway_ports[i]);
+                gws[idx] = chassis_rec->value_gateway_ports[i];
+                idx++;
+            } else {
+                /* Finds a deleted entry, reports changed. */
+                changed = true;
+            }
+        }
+
+        /* Deletes non-existing rows and adds new rows.  */
+        if (changed || !sset_is_empty(&sset)) {
+            const char *iter;
+
+            /* Adds new rows. */
+            SSET_FOR_EACH (iter, &sset) {
+                pp_names[idx] = xstrdup(iter);
+                gws[idx] = sbrec_gateway_insert(txn);
+                idx++;
+            }
+            sbrec_chassis_set_gateway_ports(chassis_rec,
+                                            (const char **)pp_names,
+                                            gws, pswitch->n_ports);
+        }
+
+        for (i = 0; i < idx; i++) {
+            free(pp_names[i]);
+        }
+        free(pp_names);
+        free(gws);
+        sset_destroy(&sset);
+    }
+
+    retval = ovsdb_idl_txn_commit_block(txn);
+    if (retval != TXN_SUCCESS && retval != TXN_UNCHANGED) {
+        VLOG_INFO("Problem registering chassis: %s",
+                  ovsdb_idl_txn_status_to_string(retval));
+        poll_immediate_wake();
+    }
+    ovsdb_idl_txn_destroy(txn);
+}
+
+/* Updates the vlan_map in gateway table based on vlan bindings
+ * of physical ports.  The logical port name in vlan_map is
+ * defined as the combination of
+ * "{physical_switch_name}_{physical_port_name}_{vlan_number}".
+ */
+static void
+update_vlan_map(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-gw: updating vlan map");
+
+    struct pp_hash_node {
+        struct hmap_node hmap_node;  /* Inside 'physical_ports'. */
+        struct vteprec_physical_port *pp;
+    };
+
+    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 hmap physical_ports = HMAP_INITIALIZER(&physical_ports);
+        int i;
+
+        /* Collects 'physical_port's from pswitch, to avoid multiple
+         * linear searches. */
+        for (i = 0; i < pswitch->n_ports; i++) {
+            struct pp_hash_node *pp = xmalloc(sizeof *pp);
+
+            pp->pp = pswitch->ports[i];
+            hmap_insert(&physical_ports, &pp->hmap_node,
+                        hash_string(pp->pp->name, 0));
+        }
+
+        /* Checks each row in Gateway table. */
+        for (i = 0; i < chassis_rec->n_gateway_ports; i++) {
+            struct sbrec_gateway *gw = chassis_rec->value_gateway_ports[i];
+            char *port_name = chassis_rec->key_gateway_ports[i];
+            struct pp_hash_node *pp = NULL;
+            struct pp_hash_node *iter;
+
+            HMAP_FOR_EACH_WITH_HASH (iter, hmap_node,
+                                     hash_string(port_name, 0),
+                                     &physical_ports) {
+                if (!strcmp(iter->pp->name, port_name)) {
+                    pp = iter;
+                    break;
+                }
+            }
+            /* 'pp' must exist, since the update_physical_ports() already
+             * cleans up all non-exist ports. */
+            ovs_assert(pp);
+
+            /* If length or content is different, we need to recreate
+             * the 'vlan_map' in 'gw'. */
+            if (gw->n_vlan_map != pp->pp->n_vlan_bindings
+                || memcmp(gw->key_vlan_map, pp->pp->key_vlan_bindings,
+                          gw->n_vlan_map)) {
+                int64_t *vlans = xmalloc(sizeof *vlans * 
pp->pp->n_vlan_bindings);
+                char **lports = xmalloc(sizeof *lports * 
pp->pp->n_vlan_bindings);
+                int j;
+
+                for (j = 0; j < pp->pp->n_vlan_bindings; j++) {
+                    vlans[j] = pp->pp->key_vlan_bindings[j];
+                    lports[j] = xasprintf("%s_%s_%"PRId64, pswitch->name,
+                                          pp->pp->name, vlans[j]);
+                }
+                sbrec_gateway_set_vlan_map(gw, vlans, (const char **)lports,
+                                           pp->pp->n_vlan_bindings);
+
+                for (j = 0; j < pp->pp->n_vlan_bindings; j++) {
+                    free(lports[j]);
+                }
+                free(lports);
+                free(vlans);
+            }
+            hmap_remove(&physical_ports, &pp->hmap_node);
+            free(pp);
+        }
+        /* hmap must be empty, otherwise the update_physical_port() is
+         * buggy. */
+        ovs_assert(hmap_is_empty(&physical_ports));
+        hmap_destroy(&physical_ports);
+    }
+
+    retval = ovsdb_idl_txn_commit_block(txn);
+    if (retval != TXN_SUCCESS && retval != TXN_UNCHANGED) {
+        VLOG_INFO("Problem registering chassis: %s",
+                  ovsdb_idl_txn_status_to_string(retval));
+        poll_immediate_wake();
+    }
+    ovsdb_idl_txn_destroy(txn);
+}
+
+
+void
+gateway_run(struct controller_vtep_ctx *ctx)
+{
+    revalidate_gateway(ctx);
+    update_physical_ports(ctx);
+    update_vlan_map(ctx);
+}
+
+/* Destroys the chassis table entries of the vtep physical switches. */
+void
+gateway_destroy(struct controller_vtep_ctx *ctx)
+{
+    int retval = TXN_TRY_AGAIN;
+
+    ovs_assert(ctx->ovnsb_idl);
+    while (retval != TXN_SUCCESS && retval != TXN_UNCHANGED) {
+        const struct vteprec_physical_switch *pswitch;
+        struct ovsdb_idl_txn *txn = ovsdb_idl_txn_create(ctx->ovnsb_idl);
+
+        ovsdb_idl_txn_add_comment(txn,
+                                  "ovn-controller-vtep: "
+                                  "unregistering vtep chassis");
+        VTEPREC_PHYSICAL_SWITCH_FOR_EACH (pswitch, ctx->vtep_idl) {
+            const struct sbrec_chassis *chassis_rec;
+
+            chassis_rec = get_chassis_by_name(ctx->ovnsb_idl, pswitch->name);
+            if (!chassis_rec) {
+                continue;
+            }
+            sbrec_chassis_delete(chassis_rec);
+        }
+        retval = ovsdb_idl_txn_commit_block(txn);
+        if (retval == TXN_ERROR) {
+            VLOG_INFO("Problem unregistering chassis: %s",
+                      ovsdb_idl_txn_status_to_string(retval));
+        }
+        ovsdb_idl_txn_destroy(txn);
+    }
+}
diff --git a/ovn/controller-vtep/ovn-controller-vtep.h 
b/ovn/controller-vtep/gateway.h
similarity index 72%
copy from ovn/controller-vtep/ovn-controller-vtep.h
copy to ovn/controller-vtep/gateway.h
index 121a9e3..edc895e 100644
--- a/ovn/controller-vtep/ovn-controller-vtep.h
+++ b/ovn/controller-vtep/gateway.h
@@ -13,15 +13,12 @@
  * limitations under the License.
  */
 
+#ifndef OVN_GATEWAY_H
+#define OVN_GATEWAY_H 1
 
-#ifndef OVN_CONTROLLER_VTEP_H
-#define OVN_CONTROLLER_VTEP_H 1
+struct controller_vtep_ctx;
 
-#include "ovn/lib/ovn-sb-idl.h"
+void gateway_run(struct controller_vtep_ctx *);
+void gateway_destroy(struct controller_vtep_ctx *);
 
-struct controller_vtep_ctx {
-    struct ovsdb_idl *ovnsb_idl;
-    struct ovsdb_idl *vtep_idl;
-};
-
-#endif /* ovn/ovn-controller-vtep.h */
+#endif /* ovn/controller-gw/gateway.h */
diff --git a/ovn/controller-vtep/ovn-controller-vtep.c 
b/ovn/controller-vtep/ovn-controller-vtep.c
index 50d727f..dbc754b 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 "gateway.h"
 #include "ovn-controller-vtep.h"
 
 VLOG_DEFINE_THIS_MODULE(ovn_vtep);
@@ -121,6 +122,7 @@ main(int argc, char *argv[])
             break;
         }
 
+        gateway_run(&ctx);
         unixctl_server_run(unixctl);
 
         unixctl_server_wait(unixctl);
@@ -134,6 +136,7 @@ main(int argc, char *argv[])
     }
 
     unixctl_server_destroy(unixctl);
+    gateway_destroy(&ctx);
 
     ovsdb_idl_destroy(ctx.vtep_idl);
     ovsdb_idl_destroy(ctx.ovnsb_idl);
diff --git a/ovn/controller-vtep/ovn-controller-vtep.h 
b/ovn/controller-vtep/ovn-controller-vtep.h
index 121a9e3..f00d3c4 100644
--- a/ovn/controller-vtep/ovn-controller-vtep.h
+++ b/ovn/controller-vtep/ovn-controller-vtep.h
@@ -24,4 +24,22 @@ struct controller_vtep_ctx {
     struct ovsdb_idl *vtep_idl;
 };
 
+/* VTEP needs what VTEP needs. */
+#define OVN_SB_ENCAP_TYPE "vxlan"
+#define VTEP_ENCAP_TYPE "vxlan_over_ipv4"
+
+static inline const struct sbrec_chassis *
+get_chassis_by_name(struct ovsdb_idl *ovnsb_idl, char *chassis_id)
+{
+    const struct sbrec_chassis *chassis_rec;
+
+    SBREC_CHASSIS_FOR_EACH(chassis_rec, ovnsb_idl) {
+        if (!strcmp(chassis_rec->name, chassis_id)) {
+            break;
+        }
+    }
+
+    return chassis_rec;
+}
+
 #endif /* ovn/ovn-controller-vtep.h */
diff --git a/tests/ovn-controller-vtep.at b/tests/ovn-controller-vtep.at
index bcae89c..63775a2 100644
--- a/tests/ovn-controller-vtep.at
+++ b/tests/ovn-controller-vtep.at
@@ -1 +1,163 @@
 AT_BANNER([ovn_controller_vtep])
+
+# OVN_CONTROLLER_VTEP_START
+#
+# Starts the test with a setup with vtep device.
+#
+# Uses vtep-ovs to simulate the vtep switch 'br-vtep' with two physical ports
+# 'p0', 'p1'.
+#
+# Configures ovn-nb with a logical switch 'br-test'.
+#
+#
+m4_define([OVN_CONTROLLER_VTEP_START],
+  [OVS_RUNDIR=`pwd`; export OVS_RUNDIR
+   OVS_LOGDIR=`pwd`; export OVS_LOGDIR
+   OVS_DBDIR=`pwd`; export OVS_DBDIR
+   OVS_SYSCONFDIR=`pwd`; export OVS_SYSCONFDIR
+
+   dnl Create databases (ovn-nb, ovn-sb, vtep).
+   AT_CHECK([ovsdb-tool create vswitchd.db 
$abs_top_srcdir/vswitchd/vswitch.ovsschema])
+   for daemon in ovn-nb ovn-sb vtep; do
+      AT_CHECK([ovsdb-tool create $daemon.db 
$abs_top_srcdir/${daemon%%-*}/${daemon}.ovsschema])
+   done
+
+   dnl Start ovsdb-server.
+   AT_CHECK([ovsdb-server --detach --no-chdir --pidfile --log-file 
--remote=punix:$OVS_RUNDIR/db.sock vswitchd.db vtep.db ovn-nb.db ovn-sb.db], 
[0], [], [stderr])
+    ON_EXIT_UNQUOTED([kill `cat ovsdb-server.pid`])
+   AT_CHECK([[sed < stderr '
+/vlog|INFO|opened log file/d
+/ovsdb_server|INFO|ovsdb-server (Open vSwitch)/d']])
+   AT_CAPTURE_FILE([ovsdb-server.log])
+
+   dnl Start ovs-vswitchd.
+   AT_CHECK([ovs-vswitchd --enable-dummy --disable-system --detach --no-chdir 
--pidfile --log-file -vvconn -vofproto_dpif], [0], [], [stderr])
+   AT_CAPTURE_FILE([ovs-vswitchd.log])
+   ON_EXIT_UNQUOTED([kill `cat ovs-vswitchd.pid`])
+   AT_CHECK([[sed < stderr '
+/ovs_numa|INFO|Discovered /d
+/vlog|INFO|opened log file/d
+/vswitchd|INFO|ovs-vswitchd (Open vSwitch)/d
+/reconnect|INFO|/d
+/ofproto|INFO|using datapath ID/d
+/ofproto|INFO|datapath ID changed to fedcba9876543210/d']])
+   AT_CHECK([ovs-vsctl -- add-br br-vtep \
+              -- set bridge br-vtep datapath-type=dummy 
other-config:datapath-id=fedcba9876543210 other-config:hwaddr=aa:55:aa:55:00:00 
protocols=[[OpenFlow10,OpenFlow11,OpenFlow12,OpenFlow13,OpenFlow14,OpenFlow15]] 
fail-mode=secure \
+              -- add-port br-vtep p0 -- set Interface p0 type=dummy 
ofport_request=1 \
+              -- add-port br-vtep p1 -- set Interface p1 type=dummy 
ofport_request=2])
+
+   dnl Start ovs-vtep.
+   AT_CHECK([vtep-ctl add-ps br-vtep -- set Physical_Switch br-vtep 
tunnel_ips=1.2.3.4])
+   AT_CHECK([ovs-vtep --log-file=ovs-vtep.log --pidfile=ovs-vtep.pid --detach 
br-vtep \], [0], [], [stderr])
+   ON_EXIT_UNQUOTED([kill `cat ovs-vtep.pid`])
+   AT_CHECK([[sed < stderr '
+/vlog|INFO|opened log file/d']])
+   # waits until ovs-vtep starts up.
+   OVS_WAIT_UNTIL([test -n "`vtep-ctl show | grep Physical_Port`"])
+
+   dnl Start ovn-northd.
+   AT_CHECK([ovn-nbctl lswitch-add br-test])
+   AT_CHECK([ovn-northd --detach --pidfile --log-file 
--ovnnb-db=unix:$OVS_RUNDIR/db.sock --ovnsb-db=unix:$OVS_RUNDIR/db.sock], [0], 
[], [stderr])
+   ON_EXIT_UNQUOTED([kill `cat ovn-northd.pid`])
+   AT_CHECK([[sed < stderr '
+/vlog|INFO|opened log file/d']])
+   AT_CAPTURE_FILE([ovn-northd.log])
+
+   dnl Start ovn-controllger-vtep.
+   AT_CHECK([ovn-controller-vtep --detach --pidfile --log-file 
--vtep-db=unix:$OVS_RUNDIR/db.sock --ovnsb-db=unix:$OVS_RUNDIR/db.sock], [0], 
[], [stderr])
+   AT_CAPTURE_FILE([ovn-controller-vtep.log])
+   ON_EXIT_UNQUOTED([kill `cat ovn-controller-vtep.pid`])
+   AT_CHECK([[sed < stderr '
+/vlog|INFO|opened log file/d
+/reconnect|INFO|/d']])
+])
+
+# OVN_CONTROLLER_VTEP_STOP
+#
+# So many exits... Yeah, we started a lot daemons~
+#
+m4_define([OVN_CONTROLLER_VTEP_STOP],
+  [AT_CHECK([check_logs $1])
+   AT_CHECK([ovs-appctl -t ovs-vtep exit])
+   AT_CHECK([ovs-appctl -t ovn-northd exit])
+   AT_CHECK([ovs-appctl -t ovn-controller-vtep exit])
+   AT_CHECK([ovs-appctl -t ovsdb-server exit])
+   AT_CHECK([ovs-appctl -t ovs-vswitchd exit])])
+
+
+
+# tests chassis related updates.
+AT_SETUP([ovn-controller-vtep - test chassis])
+OVN_CONTROLLER_VTEP_START
+
+# verifies the initial ovn-sb db configuration.
+AT_CHECK([ovn-sbctl show], [0], [dnl
+Chassis br-vtep
+    Encap vxlan
+        ip: "1.2.3.4"
+])
+
+# deletes the chassis via ovn-sbctl and check that it is readded back
+# with the log.
+AT_CHECK([ovn-sbctl del-chassis br-vtep])
+OVS_WAIT_UNTIL([test -n "`grep WARN ovn-controller-vtep.log`"])
+AT_CHECK([sed -n 's/^.*\(|WARN|.*\)$/\1/p' ovn-controller-vtep.log], [0], [dnl
+|WARN|Chassis for VTEP physical switch (br-vtep) disappears, maybe deleted by 
ovn-sbctl, adding it back
+])
+
+# changes the tunnel_ip on physical switch, watches the update of chassis's
+# encap.
+AT_CHECK([vtep-ctl set Physical_Switch br-vtep tunnel_ips=1.2.3.5])
+OVS_WAIT_UNTIL([test -n "`ovn-sbctl show | grep 1\.2\.3\.5`"])
+AT_CHECK([ovn-sbctl --columns=ip list Encap | cut -d ':' -f2 | tr -d ' '], 
[0], [dnl
+"1.2.3.5"
+])
+
+# checks for gateway_ports.
+AT_CHECK([ovn-sbctl --columns=gateway_ports list Chassis br-vtep | cut -d ':' 
-f2 | sed 's/=[[-0-9a-f]][[-0-9a-f]]*/=<GW_row>/g' | tr -d ' '], [0], [dnl
+{"p0"=<GW_row>,"p1"=<GW_row>}
+])
+
+# adds a new physical port p2.
+AT_CHECK([ovs-vsctl add-port br-vtep p2 -- set Interface p2 type=dummy 
ofport_request=3])
+AT_CHECK([vtep-ctl add-port br-vtep p2])
+OVS_WAIT_UNTIL([test -n "`ovn-sbctl list Chassis br-vtep | grep p2`"])
+AT_CHECK([ovn-sbctl --columns=gateway_ports list Chassis br-vtep | cut -d ':' 
-f2 | sed 's/=[[-0-9a-f]][[-0-9a-f]]*/=<GW_row>/g' | tr -d ' '], [0], [dnl
+{"p0"=<GW_row>,"p1"=<GW_row>,"p2"=<GW_row>}
+])
+
+# deletes port p2 and adds port p3.
+AT_CHECK([ovs-vsctl del-port p2])
+AT_CHECK([vtep-ctl del-port br-vtep p2])
+AT_CHECK([ovs-vsctl add-port br-vtep p3 -- set Interface p3 type=dummy 
ofport_request=4])
+AT_CHECK([vtep-ctl add-port br-vtep p3])
+OVS_WAIT_UNTIL([test -z "`ovn-sbctl list Chassis br-vtep | grep p2`"])
+OVS_WAIT_UNTIL([test -n "`ovn-sbctl list Chassis br-vtep | grep p3`"])
+AT_CHECK([ovn-sbctl --columns=gateway_ports list Chassis br-vtep | cut -d ':' 
-f2 | sed 's/=[[-0-9a-f]][[-0-9a-f]]*/=<GW_row>/g' | tr -d ' '], [0], [dnl
+{"p0"=<GW_row>,"p1"=<GW_row>,"p3"=<GW_row>}
+])
+
+# adds vlan_bindings to physical ports.
+AT_CHECK([vtep-ctl add-ls lswitch -- bind-ls br-vtep p0 100 lswitch -- bind-ls 
br-vtep p0 200 lswitch -- bind-ls br-vtep p1 300 lswitch])
+OVS_WAIT_UNTIL([test -n "`ovn-sbctl list Gateway | grep -- br-vtep_`"])
+AT_CHECK([ovn-sbctl --columns=vlan_map list Gateway | cut -d ':' -f2 | tr -d ' 
' | sort], [0], [dnl
+
+
+{100="br-vtep_p0_100",200="br-vtep_p0_200"}
+{300="br-vtep_p1_300"}
+{}
+])
+
+# adds one more vlan_binding to p1.
+AT_CHECK([vtep-ctl bind-ls br-vtep p1 400 lswitch])
+OVS_WAIT_UNTIL([test -n "`ovn-sbctl list Gateway | grep -- br-vtep_p1_400`"])
+AT_CHECK([ovn-sbctl --columns=vlan_map list gateway | cut -d ':' -f2 | tr -d ' 
' | sort], [0], [dnl
+
+
+{100="br-vtep_p0_100",200="br-vtep_p0_200"}
+{300="br-vtep_p1_300",400="br-vtep_p1_400"}
+{}
+])
+
+OVN_CONTROLLER_VTEP_STOP(["/Chassis for VTEP physical switch (br-vtep) 
disappears/d"])
+AT_CLEANUP
-- 
1.7.9.5

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

Reply via email to