[ovs-dev] [PATCH ovn v9 1/8] ovn-controller: Store the local port bindings in the runtime data I-P state.

numans at ovn.org numans at ovn.org
Thu May 28 11:04:06 UTC 2020


From: Numan Siddique <numans at ovn.org>

This patch adds a new data structure - 'struct local_binding' which represents
a probable local port binding. A new object of this structure is creared for
each interface present in the integration bridge (br-int) with the
external_ids:iface-id set. This struct has 2 main fields
 - 'struct ovsrec_interface *' and 'struct sbrec_port_binding *'. These fields
are updated during port claim and release.

A shash of the local bindings is maintained with the 'iface-id' as the hash key
in the runtime data of the incremental processing engine.

This patch helps in the upcoming patch to add incremental processing support
for OVS interface and SB port binding changes.

This patch also refactors the port binding code by adding a port binding function
for each port binding type.

Acked-by: Mark Michelson <mmichels at redhat.com>
Signed-off-by: Numan Siddique <numans at ovn.org>
---
 controller/binding.c        | 1020 ++++++++++++++++++++++++-----------
 controller/binding.h        |   14 +-
 controller/ovn-controller.c |   48 +-
 tests/ovn.at                |   36 ++
 4 files changed, 756 insertions(+), 362 deletions(-)

diff --git a/controller/binding.c b/controller/binding.c
index 79dc046d9..6a13d1a0e 100644
--- a/controller/binding.c
+++ b/controller/binding.c
@@ -69,47 +69,6 @@ binding_register_ovs_idl(struct ovsdb_idl *ovs_idl)
     ovsdb_idl_add_column(ovs_idl, &ovsrec_qos_col_type);
 }
 
-static void
-get_local_iface_ids(const struct ovsrec_bridge *br_int,
-                    struct shash *lport_to_iface,
-                    struct sset *local_lports,
-                    struct sset *egress_ifaces)
-{
-    int i;
-
-    for (i = 0; i < br_int->n_ports; i++) {
-        const struct ovsrec_port *port_rec = br_int->ports[i];
-        const char *iface_id;
-        int j;
-
-        if (!strcmp(port_rec->name, br_int->name)) {
-            continue;
-        }
-
-        for (j = 0; j < port_rec->n_interfaces; j++) {
-            const struct ovsrec_interface *iface_rec;
-
-            iface_rec = port_rec->interfaces[j];
-            iface_id = smap_get(&iface_rec->external_ids, "iface-id");
-            int64_t ofport = iface_rec->n_ofport ? *iface_rec->ofport : 0;
-
-            if (iface_id && ofport > 0) {
-                shash_add(lport_to_iface, iface_id, iface_rec);
-                sset_add(local_lports, iface_id);
-            }
-
-            /* Check if this is a tunnel interface. */
-            if (smap_get(&iface_rec->options, "remote_ip")) {
-                const char *tunnel_iface
-                    = smap_get(&iface_rec->status, "tunnel_egress_iface");
-                if (tunnel_iface) {
-                    sset_add(egress_ifaces, tunnel_iface);
-                }
-            }
-        }
-    }
-}
-
 static void
 add_local_datapath__(struct ovsdb_idl_index *sbrec_datapath_binding_by_key,
                      struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
@@ -403,13 +362,12 @@ destroy_qos_map(struct hmap *qos_map)
 
 static void
 update_local_lport_ids(struct sset *local_lport_ids,
-                       const struct sbrec_port_binding *binding_rec)
+                       const struct sbrec_port_binding *pb)
 {
-        char buf[16];
-        snprintf(buf, sizeof(buf), "%"PRId64"_%"PRId64,
-                 binding_rec->datapath->tunnel_key,
-                 binding_rec->tunnel_key);
-        sset_add(local_lport_ids, buf);
+    char buf[16];
+    snprintf(buf, sizeof(buf), "%"PRId64"_%"PRId64,
+             pb->datapath->tunnel_key, pb->tunnel_key);
+    sset_add(local_lport_ids, buf);
 }
 
 /*
@@ -448,219 +406,6 @@ sbrec_get_port_encap(const struct sbrec_chassis *chassis_rec,
     return best_encap;
 }
 
-static bool
-is_our_chassis(const struct sbrec_chassis *chassis_rec,
-               const struct sbrec_port_binding *binding_rec,
-               const struct sset *active_tunnels,
-               const struct ovsrec_interface *iface_rec,
-               const struct sset *local_lports)
-{
-    /* Ports of type "virtual" should never be explicitly bound to an OVS
-     * port in the integration bridge. If that's the case, ignore the binding
-     * and log a warning.
-     */
-    if (iface_rec && !strcmp(binding_rec->type, "virtual")) {
-        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
-        VLOG_WARN_RL(&rl,
-                     "Virtual port %s should not be bound to OVS port %s",
-                     binding_rec->logical_port, iface_rec->name);
-        return false;
-    }
-
-    bool our_chassis = false;
-    if (iface_rec
-        || (binding_rec->parent_port && binding_rec->parent_port[0] &&
-            sset_contains(local_lports, binding_rec->parent_port))) {
-        /* This port is in our chassis unless it is a localport. */
-        our_chassis = strcmp(binding_rec->type, "localport");
-    } else if (!strcmp(binding_rec->type, "l2gateway")) {
-        const char *chassis_id = smap_get(&binding_rec->options,
-                                          "l2gateway-chassis");
-        our_chassis = chassis_id && !strcmp(chassis_id, chassis_rec->name);
-    } else if (!strcmp(binding_rec->type, "chassisredirect") ||
-               !strcmp(binding_rec->type, "external")) {
-        our_chassis = ha_chassis_group_contains(binding_rec->ha_chassis_group,
-                                                chassis_rec) &&
-                      ha_chassis_group_is_active(binding_rec->ha_chassis_group,
-                                                 active_tunnels, chassis_rec);
-    } else if (!strcmp(binding_rec->type, "l3gateway")) {
-        const char *chassis_id = smap_get(&binding_rec->options,
-                                          "l3gateway-chassis");
-        our_chassis = chassis_id && !strcmp(chassis_id, chassis_rec->name);
-    }
-
-    return our_chassis;
-}
-
-/* Updates 'binding_rec' and if the port binding is local also updates the
- * local datapaths and ports.
- * Updates and returns the array of local virtual ports that will require
- * additional processing.
- */
-static const struct sbrec_port_binding **
-consider_local_datapath(const struct sbrec_port_binding *binding_rec,
-                        const struct ovsrec_interface *iface_rec,
-                        struct binding_ctx_in *b_ctx_in,
-                        struct binding_ctx_out *b_ctx_out,
-                        struct hmap *qos_map,
-                        const struct sbrec_port_binding **vpbs,
-                        size_t *n_vpbs, size_t *n_allocated_vpbs)
-{
-    bool our_chassis = is_our_chassis(b_ctx_in->chassis_rec, binding_rec,
-                                      b_ctx_in->active_tunnels, iface_rec,
-                                      b_ctx_out->local_lports);
-    if (iface_rec
-        || (binding_rec->parent_port && binding_rec->parent_port[0] &&
-            sset_contains(b_ctx_out->local_lports,
-                          binding_rec->parent_port))) {
-        if (binding_rec->parent_port && binding_rec->parent_port[0]) {
-            /* Add child logical port to the set of all local ports. */
-            sset_add(b_ctx_out->local_lports, binding_rec->logical_port);
-        }
-        add_local_datapath(b_ctx_in->sbrec_datapath_binding_by_key,
-                           b_ctx_in->sbrec_port_binding_by_datapath,
-                           b_ctx_in->sbrec_port_binding_by_name,
-                           binding_rec->datapath, false,
-                           b_ctx_out->local_datapaths);
-        if (iface_rec && qos_map && b_ctx_in->ovs_idl_txn) {
-            get_qos_params(binding_rec, qos_map);
-        }
-    } else if (!strcmp(binding_rec->type, "l2gateway")) {
-        if (our_chassis) {
-            sset_add(b_ctx_out->local_lports, binding_rec->logical_port);
-            add_local_datapath(b_ctx_in->sbrec_datapath_binding_by_key,
-                               b_ctx_in->sbrec_port_binding_by_datapath,
-                               b_ctx_in->sbrec_port_binding_by_name,
-                               binding_rec->datapath, false,
-                               b_ctx_out->local_datapaths);
-        }
-    } else if (!strcmp(binding_rec->type, "chassisredirect")) {
-        if (ha_chassis_group_contains(binding_rec->ha_chassis_group,
-                                      b_ctx_in->chassis_rec)) {
-            add_local_datapath(b_ctx_in->sbrec_datapath_binding_by_key,
-                               b_ctx_in->sbrec_port_binding_by_datapath,
-                               b_ctx_in->sbrec_port_binding_by_name,
-                               binding_rec->datapath, false,
-                               b_ctx_out->local_datapaths);
-        }
-    } else if (!strcmp(binding_rec->type, "l3gateway")) {
-        if (our_chassis) {
-            add_local_datapath(b_ctx_in->sbrec_datapath_binding_by_key,
-                               b_ctx_in->sbrec_port_binding_by_datapath,
-                               b_ctx_in->sbrec_port_binding_by_name,
-                               binding_rec->datapath, true,
-                               b_ctx_out->local_datapaths);
-        }
-    } else if (!strcmp(binding_rec->type, "localnet")) {
-        /* Add all localnet ports to local_lports so that we allocate ct zones
-         * for them. */
-        sset_add(b_ctx_out->local_lports, binding_rec->logical_port);
-        if (qos_map && b_ctx_in->ovs_idl_txn) {
-            get_qos_params(binding_rec, qos_map);
-        }
-    } else if (!strcmp(binding_rec->type, "external")) {
-        if (ha_chassis_group_contains(binding_rec->ha_chassis_group,
-                                      b_ctx_in->chassis_rec)) {
-            add_local_datapath(b_ctx_in->sbrec_datapath_binding_by_key,
-                               b_ctx_in->sbrec_port_binding_by_datapath,
-                               b_ctx_in->sbrec_port_binding_by_name,
-                               binding_rec->datapath, false,
-                               b_ctx_out->local_datapaths);
-        }
-    }
-
-    if (our_chassis
-        || !strcmp(binding_rec->type, "patch")
-        || !strcmp(binding_rec->type, "localport")
-        || !strcmp(binding_rec->type, "vtep")
-        || !strcmp(binding_rec->type, "localnet")) {
-        update_local_lport_ids(b_ctx_out->local_lport_ids, binding_rec);
-    }
-
-    ovs_assert(b_ctx_in->ovnsb_idl_txn);
-    const char *vif_chassis = smap_get(&binding_rec->options,
-                                           "requested-chassis");
-    bool can_bind = !vif_chassis || !vif_chassis[0]
-                    || !strcmp(vif_chassis, b_ctx_in->chassis_rec->name)
-                    || !strcmp(vif_chassis, b_ctx_in->chassis_rec->hostname);
-
-    if (can_bind && our_chassis) {
-        if (binding_rec->chassis != b_ctx_in->chassis_rec) {
-            if (binding_rec->chassis) {
-                VLOG_INFO("Changing chassis for lport %s from %s to %s.",
-                          binding_rec->logical_port,
-                          binding_rec->chassis->name,
-                          b_ctx_in->chassis_rec->name);
-            } else {
-                VLOG_INFO("Claiming lport %s for this chassis.",
-                          binding_rec->logical_port);
-            }
-            for (int i = 0; i < binding_rec->n_mac; i++) {
-                VLOG_INFO("%s: Claiming %s",
-                          binding_rec->logical_port, binding_rec->mac[i]);
-            }
-            sbrec_port_binding_set_chassis(binding_rec, b_ctx_in->chassis_rec);
-        }
-        /* Check if the port encap binding, if any, has changed */
-        struct sbrec_encap *encap_rec =
-            sbrec_get_port_encap(b_ctx_in->chassis_rec, iface_rec);
-        if (encap_rec && binding_rec->encap != encap_rec) {
-            sbrec_port_binding_set_encap(binding_rec, encap_rec);
-        }
-    } else if (binding_rec->chassis == b_ctx_in->chassis_rec) {
-        if (!strcmp(binding_rec->type, "virtual")) {
-            if (*n_vpbs == *n_allocated_vpbs) {
-                vpbs = x2nrealloc(vpbs, n_allocated_vpbs, sizeof *vpbs);
-            }
-            vpbs[(*n_vpbs)] = binding_rec;
-            (*n_vpbs)++;
-        } else {
-            VLOG_INFO("Releasing lport %s from this chassis.",
-                      binding_rec->logical_port);
-            if (binding_rec->encap) {
-                sbrec_port_binding_set_encap(binding_rec, NULL);
-            }
-            sbrec_port_binding_set_chassis(binding_rec, NULL);
-        }
-    } else if (our_chassis) {
-        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
-        VLOG_INFO_RL(&rl,
-                     "Not claiming lport %s, chassis %s "
-                     "requested-chassis %s",
-                     binding_rec->logical_port, b_ctx_in->chassis_rec->name,
-                     vif_chassis);
-    }
-
-    return vpbs;
-}
-
-static void
-consider_local_virtual_port(struct ovsdb_idl_index *sbrec_port_binding_by_name,
-                            const struct sbrec_chassis *chassis_rec,
-                            const struct sbrec_port_binding *binding_rec)
-{
-    if (binding_rec->virtual_parent) {
-        const struct sbrec_port_binding *parent =
-            lport_lookup_by_name(sbrec_port_binding_by_name,
-                                 binding_rec->virtual_parent);
-        if (parent && parent->chassis == chassis_rec) {
-            return;
-        }
-    }
-
-    /* pinctrl module takes care of binding the ports of type 'virtual'.
-     * Release such ports if their virtual parents are no longer claimed by
-     * this chassis.
-     */
-    VLOG_INFO("Releasing lport %s from this chassis.",
-              binding_rec->logical_port);
-    if (binding_rec->encap) {
-        sbrec_port_binding_set_encap(binding_rec, NULL);
-    }
-    sbrec_port_binding_set_chassis(binding_rec, NULL);
-    sbrec_port_binding_set_virtual_parent(binding_rec, NULL);
-}
-
 static void
 add_localnet_egress_interface_mappings(
         const struct sbrec_port_binding *port_binding,
@@ -736,6 +481,585 @@ consider_localnet_port(const struct sbrec_port_binding *binding_rec,
     ld->localnet_port = binding_rec;
 }
 
+/* Local bindings. binding.c module binds the logical port (represented by
+ * Port_Binding rows) and sets the 'chassis' column when it sees the
+ * OVS interface row (of type "" or "internal") with the
+ * external_ids:iface-id=<logical_port name> set.
+ *
+ * This module also manages the other port_bindings.
+ *
+ * To better manage the local bindings with the associated OVS interfaces,
+ * 'struct local_binding' is used. A shash of these local bindings is
+ * maintained with the 'external_ids:iface-id' as the key to the shash.
+ *
+ * struct local_binding has 3 main fields:
+ *    - type
+ *    - OVS interface row object
+ *    - Port_Binding row object
+ *
+ * An instance of 'struct local_binding' can be one of 3 types.
+ *
+ *  BT_VIF:     Represent a local binding for an OVS interface of
+ *              type "" or "internal" with the external_ids:iface-id
+ *              set.
+ *
+ *              This can be a
+ *                 * probable local binding - external_ids:iface-id is
+ *                   set, but the corresponding Port_Binding row is not
+ *                   created or is not visible to the local ovn-controller
+ *                   instance.
+ *
+ *                 * a local binding - external_ids:iface-id is set and
+ *                   which is already bound to the corresponding Port_Binding
+ *                   row.
+ *
+ *              It maintains a list of children
+ *              (of type BT_CONTAINER/BT_VIRTUAL) if any.
+ *
+ *  BT_CONTAINER:   Represents a local binding which has a parent of type
+ *                  BT_VIF. Its Port_Binding row's 'parent' column is set to
+ *                  its parent's Port_Binding. It shares the OVS interface row
+ *                  with the parent.
+ *
+ *  BT_VIRTUAL: Represents a local binding which has a parent of type BT_VIF.
+ *              Its Port_Binding type is "virtual" and it shares the OVS
+ *              interface row with the parent.
+ *              Port_Binding of type "virtual" is claimed by pinctrl module
+ *              when it sees the ARP packet from the parent's VIF.
+ *
+ */
+enum local_binding_type {
+    BT_VIF,
+    BT_CONTAINER,
+    BT_VIRTUAL
+};
+
+struct local_binding {
+    char *name;
+    enum local_binding_type type;
+    const struct ovsrec_interface *iface;
+    const struct sbrec_port_binding *pb;
+
+    /* shash of 'struct local_binding' representing children. */
+    struct shash children;
+};
+
+static struct local_binding *
+local_binding_create(const char *name, const struct ovsrec_interface *iface,
+                     const struct sbrec_port_binding *pb,
+                     enum local_binding_type type)
+{
+    struct local_binding *lbinding = xzalloc(sizeof *lbinding);
+    lbinding->name = xstrdup(name);
+    lbinding->type = type;
+    lbinding->pb = pb;
+    lbinding->iface = iface;
+    shash_init(&lbinding->children);
+    return lbinding;
+}
+
+static void
+local_binding_add(struct shash *local_bindings, struct local_binding *lbinding)
+{
+    shash_add(local_bindings, lbinding->name, lbinding);
+}
+
+static struct local_binding *
+local_binding_find(struct shash *local_bindings, const char *name)
+{
+    return shash_find_data(local_bindings, name);
+}
+
+static void
+local_binding_destroy(struct local_binding *lbinding)
+{
+    local_bindings_destroy(&lbinding->children);
+
+    free(lbinding->name);
+    free(lbinding);
+}
+
+void
+local_bindings_init(struct shash *local_bindings)
+{
+    shash_init(local_bindings);
+}
+
+void
+local_bindings_destroy(struct shash *local_bindings)
+{
+    struct shash_node *node, *next;
+    SHASH_FOR_EACH_SAFE (node, next, local_bindings) {
+        struct local_binding *lbinding = node->data;
+        local_binding_destroy(lbinding);
+        shash_delete(local_bindings, node);
+    }
+
+    shash_destroy(local_bindings);
+}
+
+static void
+local_binding_add_child(struct local_binding *lbinding,
+                        struct local_binding *child)
+{
+    local_binding_add(&lbinding->children, child);
+}
+
+static struct local_binding *
+local_binding_find_child(struct local_binding *lbinding,
+                         const char *child_name)
+{
+    return local_binding_find(&lbinding->children, child_name);
+}
+
+static bool
+is_lport_vif(const struct sbrec_port_binding *pb)
+{
+    return !pb->type[0];
+}
+
+static bool
+is_lport_container(const struct sbrec_port_binding *pb)
+{
+    return is_lport_vif(pb) && pb->parent_port && pb->parent_port[0];
+}
+
+enum en_lport_type {
+    LP_UNKNOWN,
+    LP_VIF,
+    LP_PATCH,
+    LP_L3GATEWAY,
+    LP_LOCALNET,
+    LP_LOCALPORT,
+    LP_L2GATEWAY,
+    LP_VTEP,
+    LP_CHASSISREDIRECT,
+    LP_VIRTUAL,
+    LP_EXTERNAL,
+    LP_REMOTE
+};
+
+static enum en_lport_type
+get_lport_type(const struct sbrec_port_binding *pb)
+{
+    if (is_lport_vif(pb)) {
+        return LP_VIF;
+    } else if (!strcmp(pb->type, "patch")) {
+        return LP_PATCH;
+    } else if (!strcmp(pb->type, "chassisredirect")) {
+        return LP_CHASSISREDIRECT;
+    } else if (!strcmp(pb->type, "l3gateway")) {
+        return LP_L3GATEWAY;
+    } else if (!strcmp(pb->type, "localnet")) {
+        return LP_LOCALNET;
+    } else if (!strcmp(pb->type, "localport")) {
+        return LP_LOCALPORT;
+    } else if (!strcmp(pb->type, "l2gateway")) {
+        return LP_L2GATEWAY;
+    } else if (!strcmp(pb->type, "virtual")) {
+        return LP_VIRTUAL;
+    } else if (!strcmp(pb->type, "external")) {
+        return LP_EXTERNAL;
+    } else if (!strcmp(pb->type, "remote")) {
+        return LP_REMOTE;
+    } else if (!strcmp(pb->type, "vtep")) {
+        return LP_VTEP;
+    }
+
+    return LP_UNKNOWN;
+}
+
+static void
+claim_lport(const struct sbrec_port_binding *pb,
+            const struct sbrec_chassis *chassis_rec,
+            const struct ovsrec_interface *iface_rec)
+{
+    if (pb->chassis != chassis_rec) {
+        if (pb->chassis) {
+            VLOG_INFO("Changing chassis for lport %s from %s to %s.",
+                    pb->logical_port, pb->chassis->name,
+                    chassis_rec->name);
+        } else {
+            VLOG_INFO("Claiming lport %s for this chassis.", pb->logical_port);
+        }
+        for (int i = 0; i < pb->n_mac; i++) {
+            VLOG_INFO("%s: Claiming %s", pb->logical_port, pb->mac[i]);
+        }
+
+        sbrec_port_binding_set_chassis(pb, chassis_rec);
+    }
+
+    /* Check if the port encap binding, if any, has changed */
+    struct sbrec_encap *encap_rec =
+        sbrec_get_port_encap(chassis_rec, iface_rec);
+    if (encap_rec && pb->encap != encap_rec) {
+        sbrec_port_binding_set_encap(pb, encap_rec);
+    }
+}
+
+static void
+release_lport(const struct sbrec_port_binding *pb)
+{
+    VLOG_INFO("Releasing lport %s from this chassis.", pb->logical_port);
+    if (pb->encap) {
+        sbrec_port_binding_set_encap(pb, NULL);
+    }
+    sbrec_port_binding_set_chassis(pb, NULL);
+
+    if (pb->virtual_parent) {
+        sbrec_port_binding_set_virtual_parent(pb, NULL);
+    }
+}
+
+static bool
+is_lbinding_set(struct local_binding *lbinding)
+{
+    return lbinding && lbinding->pb && lbinding->iface;
+}
+
+static bool
+is_lbinding_this_chassis(struct local_binding *lbinding,
+                         const struct sbrec_chassis *chassis)
+{
+    return lbinding && lbinding->pb && lbinding->pb->chassis == chassis;
+}
+
+static bool
+can_bind_on_this_chassis(const struct sbrec_chassis *chassis_rec,
+                         const char *requested_chassis)
+{
+    return !requested_chassis || !requested_chassis[0]
+           || !strcmp(requested_chassis, chassis_rec->name)
+           || !strcmp(requested_chassis, chassis_rec->hostname);
+}
+
+static void
+consider_vif_lport_(const struct sbrec_port_binding *pb,
+                    bool can_bind, const char *vif_chassis,
+                    struct binding_ctx_in *b_ctx_in,
+                    struct binding_ctx_out *b_ctx_out,
+                    struct local_binding *lbinding,
+                    struct hmap *qos_map)
+{
+    bool lbinding_set = is_lbinding_set(lbinding);
+    if (lbinding_set) {
+        if (can_bind) {
+            /* We can claim the lport. */
+            claim_lport(pb, b_ctx_in->chassis_rec, lbinding->iface);
+
+            add_local_datapath(b_ctx_in->sbrec_datapath_binding_by_key,
+                               b_ctx_in->sbrec_port_binding_by_datapath,
+                               b_ctx_in->sbrec_port_binding_by_name,
+                               pb->datapath, false,
+                               b_ctx_out->local_datapaths);
+            update_local_lport_ids(b_ctx_out->local_lport_ids, pb);
+            if (lbinding->iface && qos_map && b_ctx_in->ovs_idl_txn) {
+                get_qos_params(pb, qos_map);
+            }
+        } else {
+            /* We could, but can't claim the lport. */
+            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
+                VLOG_INFO_RL(&rl,
+                             "Not claiming lport %s, chassis %s "
+                             "requested-chassis %s",
+                             pb->logical_port,
+                             b_ctx_in->chassis_rec->name,
+                             vif_chassis);
+        }
+    }
+
+    if (pb->chassis == b_ctx_in->chassis_rec) {
+        /* Release the lport if there is no lbinding. */
+        if (!lbinding_set || !can_bind) {
+            release_lport(pb);
+        }
+    }
+
+}
+
+static void
+consider_vif_lport(const struct sbrec_port_binding *pb,
+                   struct binding_ctx_in *b_ctx_in,
+                   struct binding_ctx_out *b_ctx_out,
+                   struct local_binding *lbinding,
+                   struct hmap *qos_map)
+{
+    const char *vif_chassis = smap_get(&pb->options, "requested-chassis");
+    bool can_bind = can_bind_on_this_chassis(b_ctx_in->chassis_rec,
+                                             vif_chassis);
+
+    if (!lbinding) {
+        lbinding = local_binding_find(b_ctx_out->local_bindings,
+                                      pb->logical_port);
+    }
+
+    if (lbinding) {
+        lbinding->pb = pb;
+    }
+
+    consider_vif_lport_(pb, can_bind, vif_chassis, b_ctx_in,
+                        b_ctx_out, lbinding, qos_map);
+}
+
+static void
+consider_container_lport(const struct sbrec_port_binding *pb,
+                         struct binding_ctx_in *b_ctx_in,
+                         struct binding_ctx_out *b_ctx_out,
+                         struct hmap *qos_map)
+{
+    struct local_binding *parent_lbinding;
+    parent_lbinding = local_binding_find(b_ctx_out->local_bindings,
+                                         pb->parent_port);
+
+    if (parent_lbinding && !parent_lbinding->pb) {
+        parent_lbinding->pb = lport_lookup_by_name(
+            b_ctx_in->sbrec_port_binding_by_name, pb->parent_port);
+
+        if (parent_lbinding->pb) {
+            /* Its possible that the parent lport is not considered yet.
+             * So call consider_vif_lport() to process it first. */
+            consider_vif_lport(parent_lbinding->pb, b_ctx_in, b_ctx_out,
+                               parent_lbinding, qos_map);
+        }
+    }
+
+    if (!parent_lbinding || !parent_lbinding->pb) {
+        /* Call release_lport, to release the container lport, if
+         * it was bound earlier. */
+        if (pb->chassis == b_ctx_in->chassis_rec) {
+            release_lport(pb);
+        }
+        return;
+    }
+
+    struct local_binding *container_lbinding =
+        local_binding_find_child(parent_lbinding, pb->logical_port);
+    ovs_assert(!container_lbinding);
+
+    container_lbinding = local_binding_create(pb->logical_port,
+                                              parent_lbinding->iface,
+                                              pb, BT_CONTAINER);
+    local_binding_add_child(parent_lbinding, container_lbinding);
+
+    const char *vif_chassis = smap_get(&parent_lbinding->pb->options,
+                                       "requested-chassis");
+    bool can_bind = can_bind_on_this_chassis(b_ctx_in->chassis_rec,
+                                             vif_chassis);
+
+    consider_vif_lport_(pb, can_bind, vif_chassis, b_ctx_in, b_ctx_out,
+                        container_lbinding, qos_map);
+}
+
+static void
+consider_virtual_lport(const struct sbrec_port_binding *pb,
+                       struct binding_ctx_in *b_ctx_in,
+                       struct binding_ctx_out *b_ctx_out,
+                       struct hmap *qos_map)
+{
+    struct local_binding * parent_lbinding =
+        pb->virtual_parent ? local_binding_find(b_ctx_out->local_bindings,
+                                                pb->virtual_parent)
+        : NULL;
+
+    if (parent_lbinding && !parent_lbinding->pb) {
+        parent_lbinding->pb = lport_lookup_by_name(
+            b_ctx_in->sbrec_port_binding_by_name, pb->virtual_parent);
+
+        if (parent_lbinding->pb) {
+            /* Its possible that the parent lport is not considered yet.
+             * So call consider_vif_lport() to process it first. */
+            consider_vif_lport(parent_lbinding->pb, b_ctx_in, b_ctx_out,
+                               parent_lbinding, qos_map);
+        }
+    }
+
+    struct local_binding *virtual_lbinding = NULL;
+    if (is_lbinding_this_chassis(parent_lbinding, b_ctx_in->chassis_rec)) {
+        virtual_lbinding =
+            local_binding_find_child(parent_lbinding, pb->logical_port);
+        ovs_assert(!virtual_lbinding);
+        virtual_lbinding = local_binding_create(pb->logical_port,
+                                                parent_lbinding->iface,
+                                                pb, BT_VIRTUAL);
+        local_binding_add_child(parent_lbinding, virtual_lbinding);
+    }
+
+    return consider_vif_lport_(pb, true, NULL, b_ctx_in, b_ctx_out,
+                               virtual_lbinding, qos_map);
+}
+
+/* Considers either claiming the lport or releasing the lport
+ * for non VIF lports.
+ */
+static void
+consider_nonvif_lport_(const struct sbrec_port_binding *pb,
+                       bool our_chassis,
+                       bool has_local_l3gateway,
+                       struct binding_ctx_in *b_ctx_in,
+                       struct binding_ctx_out *b_ctx_out)
+{
+    ovs_assert(b_ctx_in->ovnsb_idl_txn);
+    if (our_chassis) {
+        sset_add(b_ctx_out->local_lports, pb->logical_port);
+        add_local_datapath(b_ctx_in->sbrec_datapath_binding_by_key,
+                           b_ctx_in->sbrec_port_binding_by_datapath,
+                           b_ctx_in->sbrec_port_binding_by_name,
+                           pb->datapath, has_local_l3gateway,
+                           b_ctx_out->local_datapaths);
+
+        update_local_lport_ids(b_ctx_out->local_lport_ids, pb);
+        claim_lport(pb, b_ctx_in->chassis_rec, NULL);
+    } else if (pb->chassis == b_ctx_in->chassis_rec) {
+        release_lport(pb);
+    }
+}
+
+static void
+consider_l2gw_lport(const struct sbrec_port_binding *pb,
+                    struct binding_ctx_in *b_ctx_in,
+                    struct binding_ctx_out *b_ctx_out)
+{
+    const char *chassis_id = smap_get(&pb->options, "l2gateway-chassis");
+    bool our_chassis = chassis_id && !strcmp(chassis_id,
+                                             b_ctx_in->chassis_rec->name);
+
+    consider_nonvif_lport_(pb, our_chassis, false, b_ctx_in, b_ctx_out);
+}
+
+static void
+consider_l3gw_lport(const struct sbrec_port_binding *pb,
+                    struct binding_ctx_in *b_ctx_in,
+                    struct binding_ctx_out *b_ctx_out)
+{
+    const char *chassis_id = smap_get(&pb->options, "l3gateway-chassis");
+    bool our_chassis = chassis_id && !strcmp(chassis_id,
+                                             b_ctx_in->chassis_rec->name);
+
+    consider_nonvif_lport_(pb, our_chassis, true, b_ctx_in, b_ctx_out);
+}
+
+static void
+consider_localnet_lport(const struct sbrec_port_binding *pb,
+                        struct binding_ctx_in *b_ctx_in,
+                        struct binding_ctx_out *b_ctx_out,
+                        struct hmap *qos_map)
+{
+    /* Add all localnet ports to local_lports so that we allocate ct zones
+     * for them. */
+    sset_add(b_ctx_out->local_lports, pb->logical_port);
+    if (qos_map && b_ctx_in->ovs_idl_txn) {
+        get_qos_params(pb, qos_map);
+    }
+
+    update_local_lport_ids(b_ctx_out->local_lport_ids, pb);
+}
+
+static void
+consider_ha_lport(const struct sbrec_port_binding *pb,
+                  struct binding_ctx_in *b_ctx_in,
+                  struct binding_ctx_out *b_ctx_out)
+{
+    bool our_chassis = false;
+    bool is_ha_chassis = ha_chassis_group_contains(pb->ha_chassis_group,
+                                                   b_ctx_in->chassis_rec);
+    our_chassis = is_ha_chassis &&
+                  ha_chassis_group_is_active(pb->ha_chassis_group,
+                                             b_ctx_in->active_tunnels,
+                                             b_ctx_in->chassis_rec);
+
+    if (is_ha_chassis && !our_chassis) {
+        /* If the chassis_rec is part of ha_chassis_group associated with
+         * the port_binding 'pb', we need to add to the local_datapath
+         * in even if its not active.
+         *
+         * If the chassis is active, consider_nonvif_lport_() takes care
+         * of adding the datapath of this 'pb' to local datapaths.
+         * */
+        add_local_datapath(b_ctx_in->sbrec_datapath_binding_by_key,
+                           b_ctx_in->sbrec_port_binding_by_datapath,
+                           b_ctx_in->sbrec_port_binding_by_name,
+                           pb->datapath, false,
+                           b_ctx_out->local_datapaths);
+    }
+
+    consider_nonvif_lport_(pb, our_chassis, false, b_ctx_in, b_ctx_out);
+}
+
+static void
+consider_cr_lport(const struct sbrec_port_binding *pb,
+                  struct binding_ctx_in *b_ctx_in,
+                  struct binding_ctx_out *b_ctx_out)
+{
+    consider_ha_lport(pb, b_ctx_in, b_ctx_out);
+}
+
+static void
+consider_external_lport(const struct sbrec_port_binding *pb,
+                        struct binding_ctx_in *b_ctx_in,
+                        struct binding_ctx_out *b_ctx_out)
+{
+    return consider_ha_lport(pb, b_ctx_in, b_ctx_out);
+}
+
+/*
+ * Builds local_bindings from the OVS interfaces.
+ */
+static void
+build_local_bindings(struct binding_ctx_in *b_ctx_in,
+                     struct binding_ctx_out *b_ctx_out)
+{
+    int i;
+    for (i = 0; i < b_ctx_in->br_int->n_ports; i++) {
+        const struct ovsrec_port *port_rec = b_ctx_in->br_int->ports[i];
+        const char *iface_id;
+        int j;
+
+        if (!strcmp(port_rec->name, b_ctx_in->br_int->name)) {
+            continue;
+        }
+
+        for (j = 0; j < port_rec->n_interfaces; j++) {
+            const struct ovsrec_interface *iface_rec;
+
+            iface_rec = port_rec->interfaces[j];
+            iface_id = smap_get(&iface_rec->external_ids, "iface-id");
+            int64_t ofport = iface_rec->n_ofport ? *iface_rec->ofport : 0;
+
+            if (iface_id && ofport > 0) {
+                struct local_binding *lbinding =
+                    local_binding_find(b_ctx_out->local_bindings, iface_id);
+                if (!lbinding) {
+                    lbinding = local_binding_create(iface_id, iface_rec, NULL,
+                                                    BT_VIF);
+                    local_binding_add(b_ctx_out->local_bindings, lbinding);
+                } else {
+                    static struct vlog_rate_limit rl =
+                        VLOG_RATE_LIMIT_INIT(1, 5);
+                    VLOG_WARN_RL(
+                        &rl,
+                        "Invalid configuration: iface-id is configured on "
+                        "interfaces : [%s] and [%s]. Ignoring the "
+                        "configuration on interface [%s]",
+                        lbinding->iface->name, iface_rec->name,
+                        iface_rec->name);
+                    ovs_assert(lbinding->type == BT_VIF);
+                }
+
+                sset_add(b_ctx_out->local_lports, iface_id);
+            }
+
+            /* Check if this is a tunnel interface. */
+            if (smap_get(&iface_rec->options, "remote_ip")) {
+                const char *tunnel_iface
+                    = smap_get(&iface_rec->status, "tunnel_egress_iface");
+                if (tunnel_iface) {
+                    sset_add(b_ctx_out->egress_ifaces, tunnel_iface);
+                }
+            }
+        }
+    }
+}
+
 void
 binding_run(struct binding_ctx_in *b_ctx_in, struct binding_ctx_out *b_ctx_out)
 {
@@ -743,106 +1067,135 @@ binding_run(struct binding_ctx_in *b_ctx_in, struct binding_ctx_out *b_ctx_out)
         return;
     }
 
-    const struct sbrec_port_binding *binding_rec;
     struct shash bridge_mappings = SHASH_INITIALIZER(&bridge_mappings);
-    struct shash lport_to_iface = SHASH_INITIALIZER(&lport_to_iface);
-    struct sset egress_ifaces = SSET_INITIALIZER(&egress_ifaces);
     struct hmap qos_map;
 
     hmap_init(&qos_map);
     if (b_ctx_in->br_int) {
-        get_local_iface_ids(b_ctx_in->br_int, &lport_to_iface,
-                            b_ctx_out->local_lports, &egress_ifaces);
+        build_local_bindings(b_ctx_in, b_ctx_out);
     }
 
-    /* Array to store pointers to local virtual ports. It is populated by
-     * consider_local_datapath.
-     */
-    const struct sbrec_port_binding **vpbs = NULL;
-    size_t n_vpbs = 0;
-    size_t n_allocated_vpbs = 0;
+    struct hmap *qos_map_ptr =
+        !sset_is_empty(b_ctx_out->egress_ifaces) ? &qos_map : NULL;
+
+    struct ovs_list localnet_lports = OVS_LIST_INITIALIZER(&localnet_lports);
+
+    struct localnet_lport {
+        struct ovs_list list_node;
+        const struct sbrec_port_binding *pb;
+    };
 
     /* Run through each binding record to see if it is resident on this
      * chassis and update the binding accordingly.  This includes both
-     * directly connected logical ports and children of those ports.
-     * Virtual ports are just added to vpbs array and will be processed
-     * later. This is special case for virtual ports is needed in order to
-     * make sure we update the virtual_parent port bindings first.
+     * directly connected logical ports and children of those ports
+     * (which also includes virtual ports).
      */
-    SBREC_PORT_BINDING_TABLE_FOR_EACH (binding_rec,
+    const struct sbrec_port_binding *pb;
+    SBREC_PORT_BINDING_TABLE_FOR_EACH (pb,
                                        b_ctx_in->port_binding_table) {
-        const struct ovsrec_interface *iface_rec
-            = shash_find_data(&lport_to_iface, binding_rec->logical_port);
-        vpbs =
-            consider_local_datapath(binding_rec,
-                                    iface_rec, b_ctx_in, b_ctx_out,
-                                    sset_is_empty(&egress_ifaces) ? NULL :
-                                    &qos_map,
-                                    vpbs, &n_vpbs, &n_allocated_vpbs);
-    }
-
-    /* Now also update the virtual ports in case their parent ports were
-     * updated above.
-     */
-    for (size_t i = 0; i < n_vpbs; i++) {
-        consider_local_virtual_port(b_ctx_in->sbrec_port_binding_by_name,
-                                    b_ctx_in->chassis_rec, vpbs[i]);
+        enum en_lport_type lport_type = get_lport_type(pb);
+
+        switch (lport_type) {
+        case LP_PATCH:
+        case LP_LOCALPORT:
+        case LP_VTEP:
+            update_local_lport_ids(b_ctx_out->local_lport_ids, pb);
+            break;
+
+        case LP_VIF:
+            if (is_lport_container(pb)) {
+                consider_container_lport(pb, b_ctx_in, b_ctx_out, qos_map_ptr);
+            } else {
+                consider_vif_lport(pb, b_ctx_in, b_ctx_out, NULL, qos_map_ptr);
+            }
+            break;
+
+        case LP_VIRTUAL:
+            consider_virtual_lport(pb, b_ctx_in, b_ctx_out, qos_map_ptr);
+            break;
+
+        case LP_L2GATEWAY:
+            consider_l2gw_lport(pb, b_ctx_in, b_ctx_out);
+            break;
+
+        case LP_L3GATEWAY:
+            consider_l3gw_lport(pb, b_ctx_in, b_ctx_out);
+            break;
+
+        case LP_CHASSISREDIRECT:
+            consider_cr_lport(pb, b_ctx_in, b_ctx_out);
+            break;
+
+        case LP_EXTERNAL:
+            consider_external_lport(pb, b_ctx_in, b_ctx_out);
+            break;
+
+        case LP_LOCALNET: {
+            consider_localnet_lport(pb, b_ctx_in, b_ctx_out, qos_map_ptr);
+            struct localnet_lport *lnet_lport = xmalloc(sizeof *lnet_lport);
+            lnet_lport->pb = pb;
+            ovs_list_push_back(&localnet_lports, &lnet_lport->list_node);
+            break;
+        }
+
+        case LP_REMOTE:
+            /* Nothing to be done for REMOTE type. */
+            break;
+
+        case LP_UNKNOWN: {
+            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+            VLOG_WARN_RL(&rl,
+                         "Unknown port binding type [%s] for port binding "
+                         "[%s]. Does ovn-controller needs update ?",
+                         pb->type, pb->logical_port);
+            break;
+        }
+        }
     }
-    free(vpbs);
 
     add_ovs_bridge_mappings(b_ctx_in->ovs_table, b_ctx_in->bridge_table,
                             &bridge_mappings);
 
-    /* Run through each binding record to see if it is a localnet port
+    /* Run through each localnet lport list to see if it is a localnet port
      * on local datapaths discovered from above loop, and update the
      * corresponding local datapath accordingly. */
-    SBREC_PORT_BINDING_TABLE_FOR_EACH (binding_rec,
-                                       b_ctx_in->port_binding_table) {
-        if (!strcmp(binding_rec->type, "localnet")) {
-            consider_localnet_port(binding_rec, &bridge_mappings,
-                                   &egress_ifaces, b_ctx_out->local_datapaths);
-        }
+    struct localnet_lport *lnet_lport;
+    LIST_FOR_EACH_POP (lnet_lport, list_node, &localnet_lports) {
+        consider_localnet_port(lnet_lport->pb, &bridge_mappings,
+                               b_ctx_out->egress_ifaces,
+                               b_ctx_out->local_datapaths);
+        free(lnet_lport);
     }
+
     shash_destroy(&bridge_mappings);
 
-    if (!sset_is_empty(&egress_ifaces)
+    if (!sset_is_empty(b_ctx_out->egress_ifaces)
         && set_noop_qos(b_ctx_in->ovs_idl_txn, b_ctx_in->port_table,
-                        b_ctx_in->qos_table, &egress_ifaces)) {
+                        b_ctx_in->qos_table, b_ctx_out->egress_ifaces)) {
         const char *entry;
-        SSET_FOR_EACH (entry, &egress_ifaces) {
+        SSET_FOR_EACH (entry, b_ctx_out->egress_ifaces) {
             setup_qos(entry, &qos_map);
         }
     }
 
-    shash_destroy(&lport_to_iface);
-    sset_destroy(&egress_ifaces);
     destroy_qos_map(&qos_map);
 }
 
 /* Returns true if port-binding changes potentially require flow changes on
  * the current chassis. Returns false if we are sure there is no impact. */
 bool
-binding_evaluate_port_binding_changes(
-        const struct sbrec_port_binding_table *pb_table,
-        const struct ovsrec_bridge *br_int,
-        const struct sbrec_chassis *chassis_rec,
-        struct sset *active_tunnels,
-        struct sset *local_lports)
+binding_evaluate_port_binding_changes(struct binding_ctx_in *b_ctx_in,
+                                      struct binding_ctx_out *b_ctx_out)
 {
-    if (!chassis_rec) {
+    if (!b_ctx_in->chassis_rec) {
         return true;
     }
 
     bool changed = false;
 
     const struct sbrec_port_binding *binding_rec;
-    struct shash lport_to_iface = SHASH_INITIALIZER(&lport_to_iface);
-    struct sset egress_ifaces = SSET_INITIALIZER(&egress_ifaces);
-    if (br_int) {
-        get_local_iface_ids(br_int, &lport_to_iface, local_lports,
-                            &egress_ifaces);
-    }
-    SBREC_PORT_BINDING_TABLE_FOR_EACH_TRACKED (binding_rec, pb_table) {
+    SBREC_PORT_BINDING_TABLE_FOR_EACH_TRACKED (binding_rec,
+                                               b_ctx_in->port_binding_table) {
         /* XXX: currently OVSDB change tracking doesn't support getting old
          * data when the operation is update, so if a port-binding moved from
          * this chassis to another, there is no easy way to find out the
@@ -854,24 +1207,35 @@ binding_evaluate_port_binding_changes(
          *   interface table will be updated, which will trigger recompute.
          *
          * - If the port is not a regular VIF, always trigger recompute. */
-        if (binding_rec->chassis == chassis_rec) {
+        if (binding_rec->chassis == b_ctx_in->chassis_rec) {
             changed = true;
             break;
         }
 
-        const struct ovsrec_interface *iface_rec
-            = shash_find_data(&lport_to_iface, binding_rec->logical_port);
-        if (is_our_chassis(chassis_rec, binding_rec, active_tunnels, iface_rec,
-                           local_lports) ||
-            (strcmp(binding_rec->type, "") && strcmp(binding_rec->type,
-                                                     "remote"))) {
+        if (!strcmp(binding_rec->type, "remote")) {
+            continue;
+        }
+
+        if (strcmp(binding_rec->type, "")) {
+            changed = true;
+            break;
+        }
+
+        struct local_binding *lbinding = NULL;
+        if (!binding_rec->parent_port || !binding_rec->parent_port[0]) {
+            lbinding = local_binding_find(b_ctx_out->local_bindings,
+                                          binding_rec->logical_port);
+        } else {
+            lbinding = local_binding_find(b_ctx_out->local_bindings,
+                                          binding_rec->parent_port);
+        }
+
+        if (lbinding) {
             changed = true;
             break;
         }
     }
 
-    shash_destroy(&lport_to_iface);
-    sset_destroy(&egress_ifaces);
     return changed;
 }
 
diff --git a/controller/binding.h b/controller/binding.h
index d0b8331af..9affc9a96 100644
--- a/controller/binding.h
+++ b/controller/binding.h
@@ -31,6 +31,8 @@ struct ovsrec_open_vswitch_table;
 struct sbrec_chassis;
 struct sbrec_port_binding_table;
 struct sset;
+struct sbrec_port_binding;
+struct shash;
 
 struct binding_ctx_in {
     struct ovsdb_idl_txn *ovnsb_idl_txn;
@@ -51,8 +53,10 @@ struct binding_ctx_in {
 
 struct binding_ctx_out {
     struct hmap *local_datapaths;
+    struct shash *local_bindings;
     struct sset *local_lports;
     struct sset *local_lport_ids;
+    struct sset *egress_ifaces;
 };
 
 void binding_register_ovs_idl(struct ovsdb_idl *);
@@ -60,11 +64,9 @@ void binding_run(struct binding_ctx_in *, struct binding_ctx_out *);
 bool binding_cleanup(struct ovsdb_idl_txn *ovnsb_idl_txn,
                      const struct sbrec_port_binding_table *,
                      const struct sbrec_chassis *);
-bool binding_evaluate_port_binding_changes(
-        const struct sbrec_port_binding_table *,
-        const struct ovsrec_bridge *br_int,
-        const struct sbrec_chassis *,
-        struct sset *active_tunnels,
-        struct sset *local_lports);
+bool binding_evaluate_port_binding_changes(struct binding_ctx_in *,
+                                           struct binding_ctx_out *);
 
+void local_bindings_init(struct shash *local_bindings);
+void local_bindings_destroy(struct shash *local_bindings);
 #endif /* controller/binding.h */
diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c
index 794d4aa2f..386a64059 100644
--- a/controller/ovn-controller.c
+++ b/controller/ovn-controller.c
@@ -958,6 +958,9 @@ struct ed_type_runtime_data {
     /* Contains "struct local_datapath" nodes. */
     struct hmap local_datapaths;
 
+    /* Contains "struct local_binding" nodes. */
+    struct shash local_bindings;
+
     /* Contains the name of each logical port resident on the local
      * hypervisor.  These logical ports include the VIFs (and their child
      * logical ports, if any) that belong to VMs running on the hypervisor,
@@ -969,6 +972,8 @@ struct ed_type_runtime_data {
      * <datapath-tunnel-key>_<port-tunnel-key> */
     struct sset local_lport_ids;
     struct sset active_tunnels;
+
+    struct sset egress_ifaces;
 };
 
 static void *
@@ -981,6 +986,8 @@ en_runtime_data_init(struct engine_node *node OVS_UNUSED,
     sset_init(&data->local_lports);
     sset_init(&data->local_lport_ids);
     sset_init(&data->active_tunnels);
+    sset_init(&data->egress_ifaces);
+    local_bindings_init(&data->local_bindings);
     return data;
 }
 
@@ -992,6 +999,7 @@ en_runtime_data_cleanup(void *data)
     sset_destroy(&rt_data->local_lports);
     sset_destroy(&rt_data->local_lport_ids);
     sset_destroy(&rt_data->active_tunnels);
+    sset_destroy(&rt_data->egress_ifaces);
     struct local_datapath *cur_node, *next_node;
     HMAP_FOR_EACH_SAFE (cur_node, next_node, hmap_node,
                         &rt_data->local_datapaths) {
@@ -1000,6 +1008,7 @@ en_runtime_data_cleanup(void *data)
         free(cur_node);
     }
     hmap_destroy(&rt_data->local_datapaths);
+    local_bindings_destroy(&rt_data->local_bindings);
 }
 
 static void
@@ -1072,6 +1081,8 @@ init_binding_ctx(struct engine_node *node,
     b_ctx_out->local_datapaths = &rt_data->local_datapaths;
     b_ctx_out->local_lports = &rt_data->local_lports;
     b_ctx_out->local_lport_ids = &rt_data->local_lport_ids;
+    b_ctx_out->egress_ifaces = &rt_data->egress_ifaces;
+    b_ctx_out->local_bindings = &rt_data->local_bindings;
 }
 
 static void
@@ -1095,12 +1106,16 @@ en_runtime_data_run(struct engine_node *node, void *data)
             free(cur_node);
         }
         hmap_clear(local_datapaths);
+        local_bindings_destroy(&rt_data->local_bindings);
         sset_destroy(local_lports);
         sset_destroy(local_lport_ids);
         sset_destroy(active_tunnels);
+        sset_destroy(&rt_data->egress_ifaces);
         sset_init(local_lports);
         sset_init(local_lport_ids);
         sset_init(active_tunnels);
+        sset_init(&rt_data->egress_ifaces);
+        local_bindings_init(&rt_data->local_bindings);
     }
 
     struct binding_ctx_in b_ctx_in;
@@ -1129,35 +1144,12 @@ static bool
 runtime_data_sb_port_binding_handler(struct engine_node *node, void *data)
 {
     struct ed_type_runtime_data *rt_data = data;
-    struct sset *local_lports = &rt_data->local_lports;
-    struct sset *active_tunnels = &rt_data->active_tunnels;
-
-    struct ovsrec_open_vswitch_table *ovs_table =
-        (struct ovsrec_open_vswitch_table *)EN_OVSDB_GET(
-            engine_get_input("OVS_open_vswitch", node));
-    struct ovsrec_bridge_table *bridge_table =
-        (struct ovsrec_bridge_table *)EN_OVSDB_GET(
-            engine_get_input("OVS_bridge", node));
-    const char *chassis_id = chassis_get_id();
-    const struct ovsrec_bridge *br_int = get_br_int(bridge_table, ovs_table);
-
-    ovs_assert(br_int && chassis_id);
-
-    struct ovsdb_idl_index *sbrec_chassis_by_name =
-        engine_ovsdb_node_get_index(
-                engine_get_input("SB_chassis", node),
-                "name");
-
-    const struct sbrec_chassis *chassis
-        = chassis_lookup_by_name(sbrec_chassis_by_name, chassis_id);
-    ovs_assert(chassis);
-
-    struct sbrec_port_binding_table *pb_table =
-        (struct sbrec_port_binding_table *)EN_OVSDB_GET(
-            engine_get_input("SB_port_binding", node));
+    struct binding_ctx_in b_ctx_in;
+    struct binding_ctx_out b_ctx_out;
+    init_binding_ctx(node, rt_data, &b_ctx_in, &b_ctx_out);
 
-    bool changed = binding_evaluate_port_binding_changes(
-        pb_table, br_int, chassis, active_tunnels, local_lports);
+    bool changed = binding_evaluate_port_binding_changes(&b_ctx_in,
+                                                         &b_ctx_out);
 
     return !changed;
 }
diff --git a/tests/ovn.at b/tests/ovn.at
index 15b40ca1e..8743e0efb 100644
--- a/tests/ovn.at
+++ b/tests/ovn.at
@@ -8606,6 +8606,42 @@ as hv1 ovs-appctl netdev-dummy/receive vm1 $packet
 # expected packet at VM1
 OVN_CHECK_PACKETS([hv1/vm1-tx.pcap], [expected1])
 
+# Test binding of parent and container ports.
+ovn-nbctl lsp-set-options vm1 requested-chassis=foo
+
+OVS_WAIT_UNTIL([test xdown = x$(ovn-nbctl lsp-get-up vm1)])
+OVS_WAIT_UNTIL([test xdown = x$(ovn-nbctl lsp-get-up foo1)])
+OVS_WAIT_UNTIL([test xdown = x$(ovn-nbctl lsp-get-up bar1)])
+
+ovn-nbctl clear logical_switch_port vm1 options
+OVS_WAIT_UNTIL([test xup = x$(ovn-nbctl lsp-get-up vm1)])
+OVS_WAIT_UNTIL([test xup = x$(ovn-nbctl lsp-get-up foo1)])
+OVS_WAIT_UNTIL([test xup = x$(ovn-nbctl lsp-get-up bar1)])
+
+as hv1 ovs-vsctl set interface vm1 external_ids:iface-id=foo
+OVS_WAIT_UNTIL([test xdown = x$(ovn-nbctl lsp-get-up vm1)])
+OVS_WAIT_UNTIL([test xdown = x$(ovn-nbctl lsp-get-up foo1)])
+OVS_WAIT_UNTIL([test xdown = x$(ovn-nbctl lsp-get-up bar1)])
+
+as hv1 ovs-vsctl set interface vm1 external_ids:iface-id=vm1
+OVS_WAIT_UNTIL([test xup = x$(ovn-nbctl lsp-get-up vm1)])
+OVS_WAIT_UNTIL([test xup = x$(ovn-nbctl lsp-get-up foo1)])
+OVS_WAIT_UNTIL([test xup = x$(ovn-nbctl lsp-get-up bar1)])
+
+ovn-nbctl lsp-del vm1
+OVS_WAIT_UNTIL([test xdown = x$(ovn-nbctl lsp-get-up foo1)])
+OVS_WAIT_UNTIL([test xdown = x$(ovn-nbctl lsp-get-up bar1)])
+
+ovn-nbctl lsp-add mgmt vm1
+OVS_WAIT_UNTIL([test xup = x$(ovn-nbctl lsp-get-up vm1)])
+OVS_WAIT_UNTIL([test xup = x$(ovn-nbctl lsp-get-up foo1)])
+OVS_WAIT_UNTIL([test xup = x$(ovn-nbctl lsp-get-up bar1)])
+
+as hv1 ovs-vsctl del-port vm1
+OVS_WAIT_UNTIL([test xdown = x$(ovn-nbctl lsp-get-up vm1)])
+OVS_WAIT_UNTIL([test xdown = x$(ovn-nbctl lsp-get-up foo1)])
+OVS_WAIT_UNTIL([test xdown = x$(ovn-nbctl lsp-get-up bar1)])
+
 OVN_CLEANUP([hv1],[hv2])
 
 AT_CLEANUP
-- 
2.26.2



More information about the dev mailing list