With this patch ovn-controller-vtep start synchronizing local
MAC database to Mac_Binding OVN SB table. Which helps to resolve
couple of problems:

1. Connectivity between hosts plugged to different switches.
   Currently OVN does not create tunnel between hardware switches
   even when they have ports plugged to same network.
   With this patch ovn-controller-vtep checking for Mac_Binding
   OVN SB database and setup required tunnels between switches.

2. OVN rely on broadcasts when send data to remote VTEP MACs.
   Since now we propagate switch local MAC table to SB database,
   later this information may be used by ovn-controller instances
   to create direct rules for hosts discovered from remote VTEPs.

Signed-off-by: Vasyl Saienko <vsaie...@mirantis.com>
---
 controller-vtep/ovn-controller-vtep.c |   9 +
 controller-vtep/vtep.c                | 301 +++++++++++++++++++++++++-
 tests/ovn-controller-vtep.at          | 124 ++++++++++-
 3 files changed, 425 insertions(+), 9 deletions(-)

diff --git a/controller-vtep/ovn-controller-vtep.c 
b/controller-vtep/ovn-controller-vtep.c
index 698511482..7e64bacf5 100644
--- a/controller-vtep/ovn-controller-vtep.c
+++ b/controller-vtep/ovn-controller-vtep.c
@@ -172,6 +172,15 @@ main(int argc, char *argv[])
     ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_port_binding_col_type);
     ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_port_binding_col_up);
 
+    /* Listen for Mac_Binding changes, we use this table to create tunnels
+     * for macs learned from other VTEPs */
+    ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_mac_binding);
+    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_mac_binding_col_mac);
+    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_mac_binding_col_ip);
+    ovsdb_idl_add_column(ovnsb_idl_loop.idl,
+                         &sbrec_mac_binding_col_logical_port);
+    ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_mac_binding_col_datapath);
+
     ovsdb_idl_set_leader_only(ovnsb_idl_loop.idl, false);
     ovsdb_idl_get_initial_snapshot(ovnsb_idl_loop.idl);
 
diff --git a/controller-vtep/vtep.c b/controller-vtep/vtep.c
index 7f5e1d606..0975b11f4 100644
--- a/controller-vtep/vtep.c
+++ b/controller-vtep/vtep.c
@@ -42,6 +42,11 @@ struct mmr_hash_node_data {
     struct shash physical_locators;
 };
 
+struct lp_mac_ip_binding {
+    const char *lp;
+    struct shash mac_ip_lp;
+};
+
 /*
  * Scans through the Binding table in ovnsb, and updates the vtep logical
  * switch tunnel keys and the 'Ucast_Macs_Remote' table in the VTEP
@@ -90,7 +95,8 @@ create_pl(struct ovsdb_idl_txn *vtep_idl_txn, const char 
*chassis_ip)
         vteprec_physical_locator_insert(vtep_idl_txn);
 
     vteprec_physical_locator_set_dst_ip(new_pl, chassis_ip);
-    vteprec_physical_locator_set_encapsulation_type(new_pl, VTEP_ENCAP_TYPE);
+    vteprec_physical_locator_set_encapsulation_type(new_pl,
+                                                    VTEP_ENCAP_TYPE);
 
     return new_pl;
 }
@@ -267,9 +273,15 @@ vtep_lswitch_run(struct shash *vtep_pbs, struct sset 
*vtep_pswitches,
 /* Updates the vtep 'Ucast_Macs_Remote' and 'Mcast_Macs_Remote' tables based
  * on non-vtep port bindings. */
 static void
-vtep_macs_run(struct ovsdb_idl_txn *vtep_idl_txn, struct shash 
*ucast_macs_rmts,
-              struct shash *mcast_macs_rmts, struct shash *physical_locators,
-              struct shash *vtep_lswitches, struct shash *non_vtep_pbs)
+vtep_macs_run(struct ovsdb_idl_txn *vtep_idl_txn,
+              struct shash *ucast_macs_rmts,
+              struct shash *mcast_macs_rmts,
+              struct shash *physical_locators,
+              struct shash *vtep_lswitches,
+              struct shash *non_vtep_pbs,
+              struct shash *vtep_pbs,
+              struct shash *sbrec_lp_mac_binding,
+              struct sset *vtep_pswitches)
 {
     struct shash_node *node;
     struct hmap ls_map;
@@ -344,6 +356,14 @@ vtep_macs_run(struct ovsdb_idl_txn *vtep_idl_txn, struct 
shash *ucast_macs_rmts,
                 continue;
             }
             tnl_key = peer_pb->datapath->tunnel_key;
+        } else if (!strcmp(port_binding_rec->type, "external")) {
+            /* External ports sits actually behind remote VTEPs
+             * but port itself is bound to one of gateway nodes
+             * to provide DHCP/Metadata. Skip port_binding
+             * information for such ports, as its does not specify
+             * real node location. Use dynamically learned Mac_Binding
+             * records from remote VTEPs */
+            continue;
         } else {
             tnl_key = port_binding_rec->datapath->tunnel_key;
         }
@@ -445,6 +465,118 @@ vtep_macs_run(struct ovsdb_idl_txn *vtep_idl_txn, struct 
shash *ucast_macs_rmts,
         }
     }
 
+    /* Handle dynamically leart MACs from remote VTEPs registered in
+     * Mac_Binding table. */
+    SHASH_FOR_EACH (node, vtep_pbs) {
+        const struct sbrec_port_binding *port_binding_rec = node->data;
+        const struct sbrec_chassis *chassis_rec;
+        struct ls_hash_node *ls_node;
+        const char *chassis_ip;
+        int64_t tnl_key;
+
+        chassis_rec = port_binding_rec->chassis;
+        if (!chassis_rec) {
+            continue;
+        }
+
+        const char *pswitch_name = smap_get(&port_binding_rec->options,
+                                            "vtep-physical-switch");
+        /* Ignore macs learned by ourselfs */
+        if (sset_find(vtep_pswitches, pswitch_name)) {
+            continue;
+        }
+        tnl_key = port_binding_rec->datapath->tunnel_key;
+
+        HMAP_FOR_EACH_WITH_HASH (ls_node, hmap_node,
+                                 hash_uint64((uint64_t) tnl_key),
+                                 &ls_map) {
+            if (ls_node->vtep_ls->tunnel_key[0] == tnl_key) {
+                break;
+            }
+        }
+        /* If 'ls_node' is NULL, that means no vtep logical switch is
+         * attached to the corresponding ovn logical datapath, so pass.
+         */
+        if (!ls_node) {
+            continue;
+        }
+
+        chassis_ip = get_chassis_vtep_ip(chassis_rec);
+        /* Unreachable chassis, continue. */
+        if (!chassis_ip) {
+            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+            VLOG_INFO_RL(&rl, "VTEP tunnel encap on chassis (%s) not found",
+                         chassis_rec->name);
+            continue;
+        }
+
+        const struct vteprec_physical_locator *pl =
+            shash_find_data(physical_locators, chassis_ip);
+        if (!pl) {
+            pl = create_pl(vtep_idl_txn, chassis_ip);
+            shash_add(physical_locators, chassis_ip, pl);
+        }
+
+        const struct vteprec_physical_locator *ls_pl =
+            shash_find_data(&ls_node->physical_locators, chassis_ip);
+        if (!ls_pl) {
+            struct vtep_rec_physical_locator_list_entry *ploc_entry =
+                xmalloc(sizeof *ploc_entry);
+            ploc_entry->vteprec_ploc = pl;
+            ovs_list_push_back(&ls_node->locators_list,
+                               &ploc_entry->locators_node);
+            shash_add(&ls_node->physical_locators, chassis_ip, pl);
+        }
+
+
+        struct lp_mac_ip_binding *miplpb = shash_find_data(
+            sbrec_lp_mac_binding, port_binding_rec->logical_port);
+        struct shash_node *miplb_node;
+        if (!miplpb) {
+            continue;
+        }
+        SHASH_FOR_EACH (miplb_node, &miplpb->mac_ip_lp) {
+            const struct sbrec_mac_binding * mb = miplb_node->data;
+            /* Ignore MACs from other networks (datapathese) */
+            if (port_binding_rec->datapath->tunnel_key !=
+                    mb->datapath->tunnel_key) {
+                continue;
+            }
+            const struct vteprec_ucast_macs_remote *umr;
+            const struct sbrec_port_binding *conflict;
+
+            char *mac = mb->mac;
+
+            /* Checks for duplicate MAC in the same vtep logical switch. */
+            conflict = shash_find_data(&ls_node->added_macs, mac);
+            if (conflict) {
+                VLOG_WARN("MAC address (%s) has already been known to be "
+                          "on logical port (%s) in the same logical "
+                          "datapath, so just ignore this logical port (%s)",
+                          mac, conflict->logical_port,
+                          port_binding_rec->logical_port);
+                continue;
+            }
+            shash_add(&ls_node->added_macs, mac, port_binding_rec);
+
+            char *mac_ip_tnlkey = xasprintf("%s_%s_%"PRId64, mac, chassis_ip,
+                                            tnl_key);
+            umr = shash_find_data(ucast_macs_rmts, mac_ip_tnlkey);
+            /* If finds the 'umr' entry for the mac, ip, and tnl_key, deletes
+             * the entry from shash so that it is not garbage collected.
+             *
+             * If not found, creates a new 'umr' entry. */
+            if (umr && umr->logical_switch == ls_node->vtep_ls) {
+                shash_find_and_delete(ucast_macs_rmts, mac_ip_tnlkey);
+            } else {
+                const struct vteprec_ucast_macs_remote *new_umr;
+                new_umr = create_umr(vtep_idl_txn, mac, ls_node->vtep_ls);
+                vteprec_ucast_macs_remote_set_locator(new_umr, pl);
+            }
+            free(mac_ip_tnlkey);
+        }
+    }
+
     /* Removes all remaining 'umr's, since they do not exist anymore. */
     SHASH_FOR_EACH (node, ucast_macs_rmts) {
         vteprec_ucast_macs_remote_delete(node->data);
@@ -454,7 +586,7 @@ vtep_macs_run(struct ovsdb_idl_txn *vtep_idl_txn, struct 
shash *ucast_macs_rmts,
         struct vtep_rec_physical_locator_list_entry *ploc_entry;
         vtep_update_mmr(vtep_idl_txn, &iter->locators_list,
                         iter->vtep_ls, iter->mmr_ext);
-        LIST_FOR_EACH_POP(ploc_entry, locators_node,
+        LIST_FOR_EACH_POP (ploc_entry, locators_node,
                           &iter->locators_list) {
             free(ploc_entry);
         }
@@ -519,6 +651,123 @@ vtep_mcast_macs_cleanup(struct ovsdb_idl *vtep_idl)
 
     return true;
 }
+
+static const struct sbrec_port_binding *
+find_pbs_for_logical_switch(struct shash *vtep_pbs,
+                            struct sset *vtep_pswitches,
+                            char *ls_name){
+
+    struct shash_node *node;
+    SHASH_FOR_EACH (node, vtep_pbs) {
+        const struct sbrec_port_binding *port_binding_rec = node->data;
+        const char *pswitch_name = smap_get(&port_binding_rec->options,
+                                            "vtep-physical-switch");
+        const char *lswitch_name = smap_get(&port_binding_rec->options,
+                                            "vtep-logical-switch");
+        if (!port_binding_rec->chassis) {
+            continue;
+        }
+
+        /* If 'port_binding_rec->chassis' exists then 'pswitch_name'
+         * and 'lswitch_name' must also exist. */
+        if (!pswitch_name || !lswitch_name) {
+            /* This could only happen when someone directly modifies the
+             * database,  (e.g. using ovn-sbctl). */
+            VLOG_ERR("logical port (%s) with no 'options:vtep-physical-"
+                     "switch' or 'options:vtep-logical-switch' specified "
+                     "is bound to chassis (%s).",
+                     port_binding_rec->logical_port,
+                     port_binding_rec->chassis->name);
+            continue;
+        }
+        /* Make sure both logical_switch and physical_switch matches */
+        if (!strcmp(ls_name, lswitch_name)) {
+          if (sset_find(vtep_pswitches, port_binding_rec->chassis->name)) {
+            return port_binding_rec;
+          }
+        }
+    }
+    return NULL;
+}
+
+/* Propagate dynamically learned MACs on local VTEPs to OVN SB. Where
+ * later this information is used to create tunnels between neighbour
+ * VTEPs.
+*/
+static void
+vtep_local_macs(struct controller_vtep_ctx *ctx,
+                struct shash *vtep_pbs,
+                struct sset *vtep_pswitches,
+                struct shash *vtep_lswitches,
+                struct shash *sbrec_lp_mac_binding){
+
+    if (!ctx->ovnsb_idl_txn) {
+        return;
+    }
+
+    ovsdb_idl_txn_add_comment(ctx->ovnsb_idl_txn,
+                              "ovn-controller-vtep: updating mac_binding");
+
+    const struct vteprec_ucast_macs_local *vtep_uml;
+    const struct sbrec_port_binding *port_binding_rec;
+    const struct sbrec_mac_binding *mb;
+
+    /* Collect local unicast MACs */
+    VTEPREC_UCAST_MACS_LOCAL_FOR_EACH (vtep_uml, ctx->vtep_idl) {
+        port_binding_rec = find_pbs_for_logical_switch(
+            vtep_pbs, vtep_pswitches, vtep_uml->logical_switch->name);
+        if (!port_binding_rec) {
+            VLOG_ERR("Cannot find port_binding for dynamically learned MAC %s "
+                     "in logical_switch %s", vtep_uml->MAC,
+                     vtep_uml->logical_switch->name);
+            continue;
+        }
+
+        struct lp_mac_ip_binding *miplpb = shash_find_data(
+            sbrec_lp_mac_binding, port_binding_rec->logical_port);
+        mb = NULL;
+        if (miplpb) {
+            char *mac_ip_lp_key = xasprintf("%s_%s_%s", vtep_uml->MAC,
+                                            vtep_uml->ipaddr,
+                                            port_binding_rec->logical_port);
+            mb = shash_find_and_delete(&miplpb->mac_ip_lp, mac_ip_lp_key);
+            free(mac_ip_lp_key);
+        }
+
+        if (!mb) {
+            VLOG_DBG("Creating new mac_binding entry for mac %s",
+                     vtep_uml->MAC);
+            mb = sbrec_mac_binding_insert(ctx->ovnsb_idl_txn);
+            sbrec_mac_binding_set_mac(mb, vtep_uml->MAC);
+            sbrec_mac_binding_set_logical_port(
+                mb, port_binding_rec->logical_port);
+            sbrec_mac_binding_set_timestamp(mb, time_wall_msec());
+        }
+        sbrec_mac_binding_set_ip(mb, vtep_uml->ipaddr);
+        sbrec_mac_binding_set_datapath(mb, port_binding_rec->datapath);
+    }
+
+    struct shash_node *node;
+    struct shash_node *miplb_node;
+    SHASH_FOR_EACH (node, vtep_lswitches) {
+        port_binding_rec = find_pbs_for_logical_switch(vtep_pbs,
+                                                       vtep_pswitches,
+                                                       node->name);
+        if (!port_binding_rec) {
+            continue;
+        }
+        struct lp_mac_ip_binding *miplpb = shash_find_data(
+            sbrec_lp_mac_binding, port_binding_rec->logical_port);
+        if (!miplpb) {
+            continue;
+        }
+        SHASH_FOR_EACH (miplb_node, &miplpb->mac_ip_lp) {
+            mb = miplb_node->data;
+            VLOG_DBG("Removing mac_binding for stale VTEP mac %s", mb->mac);
+            sbrec_mac_binding_delete(mb);
+        }
+    }
+}
 
 /* Updates vtep logical switch tunnel keys. */
 void
@@ -593,7 +842,7 @@ vtep_run(struct controller_vtep_ctx *ctx)
     }
 
     /* Collects and classifies 'Port_Binding's. */
-    SBREC_PORT_BINDING_FOR_EACH(port_binding_rec, ctx->ovnsb_idl) {
+    SBREC_PORT_BINDING_FOR_EACH (port_binding_rec, ctx->ovnsb_idl) {
         struct shash *target =
             !strcmp(port_binding_rec->type, "vtep") ? &vtep_pbs
                                                     : &non_vtep_pbs;
@@ -605,6 +854,33 @@ vtep_run(struct controller_vtep_ctx *ctx)
         shash_add(target, port_binding_rec->logical_port, port_binding_rec);
     }
 
+    /* Construct logical_port to mac_binding */
+    const struct sbrec_mac_binding *mb;
+    struct shash sbrec_lp_mac_binding = SHASH_INITIALIZER(
+        &sbrec_lp_mac_binding);
+    SBREC_MAC_BINDING_FOR_EACH (mb, ctx->ovnsb_idl) {
+        if (!mb->logical_port) {
+            continue;
+        }
+        char *mac_ip_lp_key = xasprintf("%s_%s_%s", mb->mac, mb->ip,
+                                        mb->logical_port);
+
+        struct lp_mac_ip_binding *miplpb = shash_find_data(
+            &sbrec_lp_mac_binding, mb->logical_port);
+        if (!miplpb) {
+            struct lp_mac_ip_binding *sbrec_mac_ip_lp_binding = xmalloc(
+                sizeof *sbrec_mac_ip_lp_binding);
+            shash_init(&sbrec_mac_ip_lp_binding->mac_ip_lp);
+            sbrec_mac_ip_lp_binding->lp = mb->logical_port;
+            shash_add(&sbrec_mac_ip_lp_binding->mac_ip_lp, mac_ip_lp_key, mb);
+            shash_add(&sbrec_lp_mac_binding, mb->logical_port,
+                      sbrec_mac_ip_lp_binding);
+        } else {
+            shash_add(&miplpb->mac_ip_lp, mac_ip_lp_key, mb);
+        }
+        free(mac_ip_lp_key);
+    }
+
     ovsdb_idl_txn_add_comment(ctx->vtep_idl_txn,
                               "ovn-controller-vtep: update logical switch "
                               "tunnel keys and 'ucast_macs_remote's");
@@ -612,7 +888,10 @@ vtep_run(struct controller_vtep_ctx *ctx)
     vtep_lswitch_run(&vtep_pbs, &vtep_pswitches, &vtep_lswitches);
     vtep_macs_run(ctx->vtep_idl_txn, &ucast_macs_rmts,
                   &mcast_macs_rmts, &physical_locators,
-                  &vtep_lswitches, &non_vtep_pbs);
+                  &vtep_lswitches, &non_vtep_pbs, &vtep_pbs,
+                  &sbrec_lp_mac_binding, &vtep_pswitches);
+    vtep_local_macs(ctx, &vtep_pbs, &vtep_pswitches, &vtep_lswitches,
+                    &sbrec_lp_mac_binding);
 
     sset_destroy(&vtep_pswitches);
     shash_destroy(&vtep_lswitches);
@@ -628,6 +907,14 @@ vtep_run(struct controller_vtep_ctx *ctx)
     shash_destroy(&physical_locators);
     shash_destroy(&vtep_pbs);
     shash_destroy(&non_vtep_pbs);
+
+    struct shash_node *lp_mb_node;
+    SHASH_FOR_EACH (lp_mb_node, &sbrec_lp_mac_binding) {
+        struct lp_mac_ip_binding *miplpb = lp_mb_node->data;
+        shash_destroy(&miplpb->mac_ip_lp);
+        free(miplpb);
+    }
+    shash_destroy(&sbrec_lp_mac_binding);
 }
 
 /* Cleans up all related entries in vtep.  Returns true when done (i.e. there
diff --git a/tests/ovn-controller-vtep.at b/tests/ovn-controller-vtep.at
index d410ecd28..a0a4b89ff 100644
--- a/tests/ovn-controller-vtep.at
+++ b/tests/ovn-controller-vtep.at
@@ -1,9 +1,10 @@
 AT_BANNER([ovn_controller_vtep])
 
-# OVN_CONTROLLER_VTEP_START(SIM_NAME)
+# OVN_CONTROLLER_VTEP_START(SIM_NAME, TUNNEL_IP)
 #
 # $1 - optional simulator name. If none is given, runs ovn-controller-vtep, and
 #      vtep emulator in $ovs_dir.
+# $2 - optional tunnel ip. If none is given default 1.2.3.4 is used
 # Starts the test with a setup with vtep device.  Each test case must first
 # call this macro and ovn_start.
 #
@@ -14,7 +15,9 @@ m4_define([OVN_CONTROLLER_VTEP_START], [
    AT_KEYWORDS([ovn])
    # this will cause skip when 'make check' using Windows setup.
    sim="$1"
+   tunnel_ip="$2"
    vtep_chassis=${sim:-br-vtep}
+   tunnel_ip=${tunnel_ip:-1.2.3.4}
 
    test -n "$sim" && as "$sim"
    mkdir -p "$ovs_dir" || return 1
@@ -41,7 +44,7 @@ m4_define([OVN_CONTROLLER_VTEP_START], [
               -- add-port $vtep_chassis p1 -- set Interface p1 type=dummy 
ofport_request=2
 
    dnl Start ovs-vtep.
-   check vtep-ctl add-ps $vtep_chassis -- set Physical_Switch $vtep_chassis 
tunnel_ips=1.2.3.4
+   check vtep-ctl add-ps $vtep_chassis -- set Physical_Switch $vtep_chassis 
tunnel_ips=$tunnel_ip
    AT_CHECK([ovs-vtep --log-file="$ovs_dir"/ovs-vtep.log \
                       --pidfile="$ovs_dir"/ovs-vtep.pid \
                       --detach --no-chdir $vtep_chassis], [0], [], [stderr])
@@ -86,6 +89,9 @@ AT_CHECK([ovn-nbctl lsp-set-type $2 vtep])
 AT_CHECK([ovn-nbctl lsp-set-options $2 vtep-physical-switch=$3 
vtep-logical-switch=$4])
 ])
 
+# mac learning table is hardcoded in ovs-vtep emulator
+m4_define([mac_learning_table], [1])
+
 ##############################################
 
 # tests chassis related updates.
@@ -509,6 +515,120 @@ AT_CHECK([vtep-ctl --columns=MAC list Ucast_Macs_Remote | 
cut -d ':' -f2- | tr -
 OVN_CONTROLLER_VTEP_STOP([/has already been known to be on logical port/d])
 AT_CLEANUP
 
+# Tests vtep module 'Ucast_Macs_Local' MACs propagation
+AT_SETUP([ovn-controller-vtep - vtep-macs 3])
+ovn_start
+OVN_CONTROLLER_VTEP_START
+
+check ovn-nbctl ls-add br-test
+check ovn-nbctl ls-add br-test1
+
+# creates a simple logical network with the vtep device and a fake hv chassis
+# 'ch0'.
+AT_CHECK([ovn-nbctl --wait=sb sync])
+
+# creates the logical switch in vtep and adds the corresponding logical
+# port to 'br-test'.
+AT_CHECK([vtep-ctl add-ls lswitch0 -- bind-ls br-vtep p0 100 lswitch0])
+OVN_NB_ADD_VTEP_PORT([br-test], [br-vtep_lswitch0], [br-vtep], [lswitch0])
+OVS_WAIT_UNTIL([test -n "`ovn-sbctl list Port_Binding  | grep 
br-vtep_lswitch0`"])
+OVS_WAIT_UNTIL([test -n "`ovs-vsctl show | grep br-vtep_vtep_ls1`"])
+
+# Simulate peer on physical port to populate Ucast_Macs_Local table
+AT_CHECK([ovs-ofctl -O OpenFlow14 add-flow br-vtep_vtep_ls1 
"cookie=0x5000,table=mac_learning_table,priority=1000,dl_dst=aa:bb:cc:dd:ee:01 
actions=output:0100-p0-l"])
+
+# Wait Ucast_Macs_Local is updated with local MAC
+OVS_WAIT_UNTIL([test -n "`vtep-ctl list Ucast_Macs_Local | grep 
aa:bb:cc:dd:ee:01`"])
+
+# Check that OVNSB is updated with learned MAC
+OVS_WAIT_UNTIL([test -n "`ovn-sbctl find Mac_Binding mac='"aa:bb:cc:dd:ee:01"' 
|grep _uuid`"])
+
+# Plug p1 to lswitch1
+AT_CHECK([vtep-ctl add-ls lswitch1 -- bind-ls br-vtep p1 100 lswitch1])
+OVN_NB_ADD_VTEP_PORT([br-test1], [br-vtep_lswitch1], [br-vtep], [lswitch1])
+OVS_WAIT_UNTIL([test -n "`ovn-sbctl list Port_Binding  | grep 
br-vtep_lswitch1`"])
+OVS_WAIT_UNTIL([test -n "`ovs-vsctl show | grep br-vtep_vtep_ls2`"])
+
+OVS_WAIT_UNTIL([test -n "`ovs-vsctl show |grep 0100-p1-l`"])
+
+# Simulate peer on physical port to populate Ucast_Macs_Local table
+AT_CHECK([ovs-ofctl -O OpenFlow14 add-flow br-vtep_vtep_ls2 
"cookie=0x5000,table=mac_learning_table,priority=1000,dl_dst=aa:bb:cc:dd:ee:02 
actions=output:0100-p1-l"])
+
+# Wait Ucast_Macs_Local is updated with local MAC
+OVS_WAIT_UNTIL([test -n "`vtep-ctl list Ucast_Macs_Local | grep 
aa:bb:cc:dd:ee:02`"])
+
+# Remove MAC from Ucast_Macs_Local
+AT_CHECK([ovs-ofctl -O OpenFlow14 del-flows br-vtep_vtep_ls1 
"table=mac_learning_table,dl_dst=aa:bb:cc:dd:ee:01"])
+
+# Check that first MAC is removed from OVNSB, but second is still there
+OVS_WAIT_UNTIL([test -z "`ovn-sbctl find Mac_Binding 
mac='"aa:bb:cc:dd:ee:01"'`"])
+OVS_WAIT_UNTIL([test -n "`ovn-sbctl find Mac_Binding 
mac='"aa:bb:cc:dd:ee:02"'`"])
+
+# Remove second MAC from Ucast_Macs_Local
+AT_CHECK([ovs-ofctl -O OpenFlow14 del-flows br-vtep_vtep_ls2 
"table=mac_learning_table,dl_dst=aa:bb:cc:dd:ee:02"])
+
+# Check that second MAC is removed from OVNSB
+OVS_WAIT_UNTIL([test -z "`ovn-sbctl find Mac_Binding 
mac='"aa:bb:cc:dd:ee:01"'`"])
+OVS_WAIT_UNTIL([test -z "`ovn-sbctl find Mac_Binding 
mac='"aa:bb:cc:dd:ee:02"'`"])
+
+# Start second VTEP chassis
+OVN_CONTROLLER_VTEP_START(br-vtep1, 1.2.3.5)
+
+# creates the logical switch in vtep and adds the corresponding logical
+# # port to 'br-test'.
+AT_CHECK([vtep-ctl add-ls lswitch0 -- bind-ls br-vtep1 p0 100 lswitch0])
+OVN_NB_ADD_VTEP_PORT([br-test], [br-vtep1_lswitch0], [br-vtep1], [lswitch0])
+OVS_WAIT_UNTIL([test -n "`ovn-sbctl list Port_Binding  | grep 
br-vtep1_lswitch0`"])
+OVS_WAIT_UNTIL([test -n "`ovs-vsctl show | grep br-vtep1_vtep_ls1`"])
+
+# creates the logical switch in vtep and adds the corresponding logical
+# # # port to 'br-test'.
+AT_CHECK([vtep-ctl add-ls lswitch1 -- bind-ls br-vtep1 p1 100 lswitch1])
+OVN_NB_ADD_VTEP_PORT([br-test1], [br-vtep1_lswitch1], [br-vtep1], [lswitch1])
+OVS_WAIT_UNTIL([test -n "`ovn-sbctl list Port_Binding  | grep 
br-vtep1_lswitch1`"])
+OVS_WAIT_UNTIL([test -n "`ovs-vsctl show | grep br-vtep1_vtep_ls2`"])
+
+#sleep 3000
+
+# Wait bingins are activated
+OVS_WAIT_UNTIL([test -n "`ovs-vsctl show |grep 0100-p0-l`"])
+OVS_WAIT_UNTIL([test -n "`ovs-vsctl show |grep 0100-p1-l`"])
+
+# Simulate peer on physical port to populate Ucast_Macs_Local table on br-vtep1
+AT_CHECK([ovs-ofctl -O OpenFlow14 add-flow br-vtep1_vtep_ls1 
"cookie=0x5000,table=mac_learning_table,priority=1000,dl_dst=aa:bb:cc:dd:ff:01 
actions=output:0100-p0-l"])
+AT_CHECK([ovs-ofctl -O OpenFlow14 add-flow br-vtep1_vtep_ls2 
"cookie=0x5000,table=mac_learning_table,priority=1000,dl_dst=aa:bb:cc:dd:ff:02 
actions=output:0100-p1-l"])
+
+# Wait Ucast_Macs_Local is updated with local MAC
+OVS_WAIT_UNTIL([test -n "`vtep-ctl list Ucast_Macs_Local | grep 
aa:bb:cc:dd:ff:01`"])
+OVS_WAIT_UNTIL([test -n "`vtep-ctl list Ucast_Macs_Local | grep 
aa:bb:cc:dd:ff:02`"])
+
+# Switch context
+as
+
+# Make sure we see
+OVS_WAIT_UNTIL([test -n "`vtep-ctl list-remote-macs lswitch0 | grep 
aa:bb:cc:dd:ff:01`"])
+OVS_WAIT_UNTIL([test -z "`vtep-ctl list-remote-macs lswitch1 | grep 
aa:bb:cc:dd:ff:01`"])
+
+OVS_WAIT_UNTIL([test -n "`vtep-ctl list-remote-macs lswitch1 | grep 
aa:bb:cc:dd:ff:02`"])
+OVS_WAIT_UNTIL([test -z "`vtep-ctl list-remote-macs lswitch0 | grep 
aa:bb:cc:dd:ff:02`"])
+
+# Switch context to br-vtep1
+as br-vtep1
+
+# Remove local MACs
+AT_CHECK([ovs-ofctl -O OpenFlow14 del-flows br-vtep1_vtep_ls1 
"table=mac_learning_table,dl_dst=aa:bb:cc:dd:ff:01"])
+AT_CHECK([ovs-ofctl -O OpenFlow14 del-flows br-vtep1_vtep_ls2 
"table=mac_learning_table,dl_dst=aa:bb:cc:dd:ff:02"])
+
+# Switch context
+as
+
+# Ensure remote MACs gone
+OVS_WAIT_UNTIL([test -z "`vtep-ctl list-remote-macs lswitch0 | grep 
aa:bb:cc:dd:ff:01`"])
+OVS_WAIT_UNTIL([test -z "`vtep-ctl list-remote-macs lswitch1 | grep 
aa:bb:cc:dd:ff:02`"])
+
+OVN_CONTROLLER_VTEP_STOP
+AT_CLEANUP
+
 # Tests vtep module 'Mcast_Macs_Remote's.
 AT_SETUP([ovn-controller-vtep - vtep-Mcast_Macs_Remote])
 ovn_start
-- 
2.43.0

_______________________________________________
dev mailing list
d...@openvswitch.org
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to