[ovs-dev,v4,3/5] ovn-controller: Make use of ha_chassis_group table to bind the chassisredirect ports

Message ID 20190314193158.24511-1-nusiddiq@redhat.com
State New
Headers show
Series
  • ovn: Add HA chassis group and 'external' port
Related show

Commit Message

Numan Siddique March 14, 2019, 7:31 p.m.
From: Numan Siddique <nusiddiq@redhat.com>

This patch uses the newly added ha_chassis_group table in Southbound DB

 - to bind the chassisredirect ports.

 - to establish BFD sessions with the required chassis. The previous patch
   in this series sets the list of chassis which references a ha chassis group
   in the 'ref_chassis' column of 'ha_chassis_group' table (in ovn-northd).
   This patch uses that information to establish BFD sessions with only the
   required chassis. There is no need to traverse the local_datapath list
   to determine if a local chasis has to establish a BFD session with another
   chassis. For eg, if chassis - HV1, HV2 and HV3 are part of a chassis group
   G1 and G1 is referenced by compute chassis - C1 and C2, the chassis C1
   will establish BFD sessions with HV1, HV2 and HV3 since C1 references the
   group G1. The ha chassis HV1, HV2 and HV3 also establish BFD sessions
   amongst themselves and also with C1 and C2.

This patch also deletes the old code (which used gateway_chassis table)
to bind the chassisredirect port.

The rational behind the refactor is to make the ha chassis binding support
generic, so that logical ports of type 'external' (which will be
added in the upcoming patch) can also make use of it and to simplify
the gateway chassis support code in OVN. Functionally this new
approach is same as the older one.

Signed-off-by: Numan Siddique <nusiddiq@redhat.com>
---
 ovn/controller/automake.mk      |   4 +-
 ovn/controller/bfd.c            | 229 +++++++++++---------------------
 ovn/controller/bfd.h            |  11 +-
 ovn/controller/binding.c        |  19 +--
 ovn/controller/binding.h        |   1 -
 ovn/controller/gchassis.c       | 222 -------------------------------
 ovn/controller/gchassis.h       |  71 ----------
 ovn/controller/ha-chassis.c     | 203 ++++++++++++++++++++++++++++
 ovn/controller/ha-chassis.h     |  50 +++++++
 ovn/controller/lflow.c          |  29 ++--
 ovn/controller/lflow.h          |   3 +-
 ovn/controller/ovn-controller.c |  13 +-
 ovn/controller/physical.c       | 109 +++++++--------
 ovn/controller/physical.h       |   3 +-
 ovn/controller/pinctrl.c        |  38 ++----
 ovn/controller/pinctrl.h        |   1 -
 16 files changed, 425 insertions(+), 581 deletions(-)
 delete mode 100644 ovn/controller/gchassis.c
 delete mode 100644 ovn/controller/gchassis.h
 create mode 100644 ovn/controller/ha-chassis.c
 create mode 100644 ovn/controller/ha-chassis.h

Patch

diff --git a/ovn/controller/automake.mk b/ovn/controller/automake.mk
index cd5505ca6..fcdf7a431 100644
--- a/ovn/controller/automake.mk
+++ b/ovn/controller/automake.mk
@@ -8,8 +8,8 @@  ovn_controller_ovn_controller_SOURCES = \
 	ovn/controller/chassis.h \
 	ovn/controller/encaps.c \
 	ovn/controller/encaps.h \
-	ovn/controller/gchassis.c \
-	ovn/controller/gchassis.h \
+	ovn/controller/ha-chassis.c \
+	ovn/controller/ha-chassis.h \
 	ovn/controller/lflow.c \
 	ovn/controller/lflow.h \
 	ovn/controller/lport.c \
diff --git a/ovn/controller/bfd.c b/ovn/controller/bfd.c
index 10ab6ac37..d016e27b7 100644
--- a/ovn/controller/bfd.c
+++ b/ovn/controller/bfd.c
@@ -15,7 +15,6 @@ 
 
 #include <config.h>
 #include "bfd.h"
-#include "gchassis.h"
 #include "lport.h"
 #include "ovn-controller.h"
 
@@ -89,184 +88,104 @@  bfd_calculate_active_tunnels(const struct ovsrec_bridge *br_int,
     }
 }
 
-struct local_datapath_node {
-    struct ovs_list node;
-    const struct local_datapath *dp;
-};
-
+/* Loops through the HA chassis groups in the SB DB and returns
+ * the set of chassis which the call can establish the BFD sessions
+ * with.
+ * Eg.
+ * If there are 2 HA chassis groups.
+ * Group name - hapgrp1
+ *   - HA chassis - (HA1, HA2, HA3)
+ *   - ref chassis - (C1, C2)
+ *
+ * Group name - hapgrp2
+ *   - HA chassis - (HA1, HA4, HA5)
+ *   - ref chassis - (C1, C3, C4)
+ *
+ * If 'our_chassis' is HA1 then this function returns
+ *  bfd chassis set - (HA2, HA3, HA4 HA5, C1, C2, C3, C4)
+ *
+ * If 'our_chassis' is C1 then this function returns
+ *  bfd chassis set - (HA1, HA2, HA3, HA4, HA5)
+ *
+ * If 'our_chassis' is HA5 then this function returns
+ *  bfd chassis set - (HA1, HA4, C1, C3, C4)
+ *
+ * If 'our_chassis' is C2 then this function returns
+ *  bfd chassis set - (HA1, HA2, HA3)
+ *
+ * If 'our_chassis' is C5 then this function returns empty bfd set.
+ */
 static void
-bfd_travel_gw_related_chassis(
-    struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
-    const struct local_datapath *dp,
-    const struct hmap *local_datapaths,
+bfd_calculate_chassis(
+    const struct sbrec_chassis *our_chassis,
+    const struct sbrec_ha_chassis_group_table *ha_chassis_grp_table,
     struct sset *bfd_chassis)
 {
-    struct ovs_list dp_list;
-    const struct sbrec_port_binding *pb;
-    struct sset visited_dp = SSET_INITIALIZER(&visited_dp);
-    const char *dp_key;
-    struct local_datapath_node *dp_binding;
-
-    if (!(dp_key = smap_get(&dp->datapath->external_ids, "logical-router")) &&
-        !(dp_key = smap_get(&dp->datapath->external_ids, "logical-switch"))) {
-        VLOG_INFO("datapath has no uuid, cannot travel graph");
-        return;
-    }
-
-    sset_add(&visited_dp, dp_key);
-
-    ovs_list_init(&dp_list);
-    dp_binding = xmalloc(sizeof *dp_binding);
-    dp_binding->dp = dp;
-    ovs_list_push_back(&dp_list, &dp_binding->node);
-
-    struct sbrec_port_binding *target = sbrec_port_binding_index_init_row(
-        sbrec_port_binding_by_datapath);
-
-    /* Go through whole graph to figure out all chassis which may deliver
-     * packets to gateway. */
-    LIST_FOR_EACH_POP (dp_binding, node, &dp_list) {
-        dp = dp_binding->dp;
-        free(dp_binding);
-        for (size_t i = 0; i < dp->n_peer_dps; i++) {
-            const struct sbrec_datapath_binding *pdp = dp->peer_dps[i];
-            if (!pdp) {
-                continue;
-            }
-
-            if (!(dp_key = smap_get(&pdp->external_ids, "logical-router")) &&
-                !(dp_key = smap_get(&pdp->external_ids, "logical-switch"))) {
+    const struct sbrec_ha_chassis_group *ha_chassis_grp;
+    SBREC_HA_CHASSIS_GROUP_TABLE_FOR_EACH (ha_chassis_grp,
+                                           ha_chassis_grp_table) {
+        bool is_ha_chassis = false;
+        struct sset grp_chassis = SSET_INITIALIZER(&grp_chassis);
+        const struct sbrec_ha_chassis *ha_ch;
+        bool bfd_setup_required = false;
+        if (ha_chassis_grp->n_ha_chassis < 2) {
+            /* No need to consider the chassis group for BFD if
+             * there is  1 or no chassis in it. */
+            continue;
+        }
+        for (size_t i = 0; i < ha_chassis_grp->n_ha_chassis; i++) {
+            ha_ch = ha_chassis_grp->ha_chassis[i];
+            if (!ha_ch->chassis) {
                 continue;
             }
-
-            if (sset_contains(&visited_dp, dp_key)) {
-                continue;
+            sset_add(&grp_chassis, ha_ch->chassis->name);
+            if (our_chassis == ha_ch->chassis) {
+                is_ha_chassis = true;
+                bfd_setup_required = true;
             }
+        }
 
-            sset_add(&visited_dp, dp_key);
-
-            struct hmap_node *node = hmap_first_with_hash(local_datapaths,
-                                                          pdp->tunnel_key);
-            if (!node) {
-                continue;
+        if (is_ha_chassis) {
+            /* It's an HA chassis. So add the ref_chassis to the bfd set. */
+            for (size_t i = 0; i < ha_chassis_grp->n_ref_chassis; i++) {
+                sset_add(&grp_chassis, ha_chassis_grp->ref_chassis[i]->name);
             }
-
-            dp_binding = xmalloc(sizeof *dp_binding);
-            dp_binding->dp = CONTAINER_OF(node, struct local_datapath,
-                                          hmap_node);
-            ovs_list_push_back(&dp_list, &dp_binding->node);
-
-            sbrec_port_binding_index_set_datapath(target, pdp);
-            SBREC_PORT_BINDING_FOR_EACH_EQUAL (pb, target,
-                                               sbrec_port_binding_by_datapath) {
-                if (pb->chassis) {
-                    const char *chassis_name = pb->chassis->name;
-                    if (chassis_name) {
-                        sset_add(bfd_chassis, chassis_name);
-                    }
+        } else {
+            /* This is not an HA chassis. Check if this chassis is present
+             * in the ref_chassis list. If so add the ha_chassis to the
+             * sset .*/
+            for (size_t i = 0; i < ha_chassis_grp->n_ref_chassis; i++) {
+                if (our_chassis == ha_chassis_grp->ref_chassis[i]) {
+                    bfd_setup_required = true;
+                    break;
                 }
             }
         }
-    }
-    sbrec_port_binding_index_destroy_row(target);
-
-    sset_destroy(&visited_dp);
-}
-
-static struct ovs_list *
-bfd_find_ha_gateway_chassis(
-    struct ovsdb_idl_index *sbrec_chassis_by_name,
-    struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
-    const struct sbrec_datapath_binding *datapath)
-{
-    struct sbrec_port_binding *target = sbrec_port_binding_index_init_row(
-        sbrec_port_binding_by_datapath);
-    sbrec_port_binding_index_set_datapath(target, datapath);
-
-    struct ovs_list *ha_gateway_chassis = NULL;
-    const struct sbrec_port_binding *pb;
-    SBREC_PORT_BINDING_FOR_EACH_EQUAL (pb, target,
-                                       sbrec_port_binding_by_datapath) {
-        if (strcmp(pb->type, "chassisredirect")) {
-            continue;
-        }
-
-        struct ovs_list *gateway_chassis = gateway_chassis_get_ordered(
-            sbrec_chassis_by_name, pb);
-        if (!gateway_chassis || ovs_list_is_short(gateway_chassis)) {
-            /* We don't need BFD for non-HA chassisredirect. */
-            gateway_chassis_destroy(gateway_chassis);
-            continue;
-        }
-
-        ha_gateway_chassis = gateway_chassis;
-        break;
-    }
-    sbrec_port_binding_index_destroy_row(target);
-    return ha_gateway_chassis;
-}
-
-static void
-bfd_calculate_chassis(
-    struct ovsdb_idl_index *sbrec_chassis_by_name,
-    struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
-    const struct sbrec_chassis *our_chassis,
-    const struct hmap *local_datapaths,
-    struct sset *bfd_chassis)
-{
-    /* Identify all chassis nodes to which we need to enable bfd.
-     * 1) Any chassis hosting the chassisredirect ports for known
-     *    router datapaths.
-     * 2) Chassis hosting peer datapaths (with ports) connected
-     *    to a router datapath  when our chassis is hosting a router
-     *    with a chassis redirect port. */
 
-    const struct local_datapath *dp;
-    HMAP_FOR_EACH (dp, hmap_node, local_datapaths) {
-        const char *is_router = smap_get(&dp->datapath->external_ids,
-                                         "logical-router");
-        bool our_chassis_is_gw_for_dp = false;
-        if (is_router) {
-            struct ovs_list *ha_gateway_chassis
-                = bfd_find_ha_gateway_chassis(sbrec_chassis_by_name,
-                                              sbrec_port_binding_by_datapath,
-                                              dp->datapath);
-            if (ha_gateway_chassis) {
-                our_chassis_is_gw_for_dp = gateway_chassis_contains(
-                    ha_gateway_chassis, our_chassis);
-                struct gateway_chassis *gwc;
-                LIST_FOR_EACH (gwc, node, ha_gateway_chassis) {
-                    if (gwc->db->chassis) {
-                        sset_add(bfd_chassis, gwc->db->chassis->name);
-                    }
-                }
-                gateway_chassis_destroy(ha_gateway_chassis);
+        if (bfd_setup_required) {
+            const char *name;
+            SSET_FOR_EACH (name, &grp_chassis) {
+                sset_add(bfd_chassis, name);
             }
         }
-        if (our_chassis_is_gw_for_dp) {
-            bfd_travel_gw_related_chassis(sbrec_port_binding_by_datapath,
-                                          dp, local_datapaths, bfd_chassis);
-        }
+        sset_destroy(&grp_chassis);
     }
 }
 
 void
-bfd_run(struct ovsdb_idl_index *sbrec_chassis_by_name,
-        struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
-        const struct ovsrec_interface_table *interface_table,
+bfd_run(const struct ovsrec_interface_table *interface_table,
         const struct ovsrec_bridge *br_int,
         const struct sbrec_chassis *chassis_rec,
-        const struct sbrec_sb_global_table *sb_global_table,
-        const struct hmap *local_datapaths)
+        const struct sbrec_ha_chassis_group_table *ha_chassis_grp_table,
+        const struct sbrec_sb_global_table *sb_global_table)
 {
-
     if (!chassis_rec) {
         return;
     }
     struct sset bfd_chassis = SSET_INITIALIZER(&bfd_chassis);
-    bfd_calculate_chassis(sbrec_chassis_by_name,
-                          sbrec_port_binding_by_datapath,
-                          chassis_rec, local_datapaths, &bfd_chassis);
+    bfd_calculate_chassis(chassis_rec, ha_chassis_grp_table,
+                          &bfd_chassis);
+
     /* Identify tunnels ports(connected to remote chassis id) to enable bfd */
     struct sset tunnels = SSET_INITIALIZER(&tunnels);
     struct sset bfd_ifaces = SSET_INITIALIZER(&bfd_ifaces);
@@ -336,7 +255,7 @@  bfd_run(struct ovsdb_idl_index *sbrec_chassis_by_name,
                     VLOG_INFO("Disabled BFD on interface %s", iface->name);
                 }
             }
-         }
+        }
     }
 
     smap_destroy(&bfd);
diff --git a/ovn/controller/bfd.h b/ovn/controller/bfd.h
index e36820afb..789f7b269 100644
--- a/ovn/controller/bfd.h
+++ b/ovn/controller/bfd.h
@@ -24,16 +24,17 @@  struct ovsrec_interface_table;
 struct ovsrec_open_vswitch_table;
 struct sbrec_chassis;
 struct sbrec_sb_global_table;
+struct sbrec_ha_chassis_group_table;
 struct sset;
 
 void bfd_register_ovs_idl(struct ovsdb_idl *);
-void bfd_run(struct ovsdb_idl_index *sbrec_chassis_by_name,
-             struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
-             const struct ovsrec_interface_table *interface_table,
+
+void bfd_run(const struct ovsrec_interface_table *interface_table,
              const struct ovsrec_bridge *br_int,
              const struct sbrec_chassis *chassis_rec,
-             const struct sbrec_sb_global_table *sb_global_table,
-             const struct hmap *local_datapaths);
+             const struct sbrec_ha_chassis_group_table *ha_chassis_grp_table,
+             const struct sbrec_sb_global_table *sb_global_table);
+
 void  bfd_calculate_active_tunnels(const struct ovsrec_bridge *br_int,
                                    struct sset *active_tunnels);
 
diff --git a/ovn/controller/binding.c b/ovn/controller/binding.c
index 74ba12743..2929eccd7 100644
--- a/ovn/controller/binding.c
+++ b/ovn/controller/binding.c
@@ -15,7 +15,7 @@ 
 
 #include <config.h>
 #include "binding.h"
-#include "gchassis.h"
+#include "ha-chassis.h"
 #include "lflow.h"
 #include "lport.h"
 
@@ -430,7 +430,6 @@  sbrec_get_port_encap(const struct sbrec_chassis *chassis_rec,
 static void
 consider_local_datapath(struct ovsdb_idl_txn *ovnsb_idl_txn,
                         struct ovsdb_idl_txn *ovs_idl_txn,
-                        struct ovsdb_idl_index *sbrec_chassis_by_name,
                         struct ovsdb_idl_index *sbrec_datapath_binding_by_key,
                         struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
                         struct ovsdb_idl_index *sbrec_port_binding_by_name,
@@ -445,7 +444,6 @@  consider_local_datapath(struct ovsdb_idl_txn *ovnsb_idl_txn,
 {
     const struct ovsrec_interface *iface_rec
         = shash_find_data(lport_to_iface, binding_rec->logical_port);
-    struct ovs_list *gateway_chassis = NULL;
 
     bool our_chassis = false;
     if (iface_rec
@@ -478,20 +476,17 @@  consider_local_datapath(struct ovsdb_idl_txn *ovnsb_idl_txn,
                                binding_rec->datapath, false, local_datapaths);
         }
     } else if (!strcmp(binding_rec->type, "chassisredirect")) {
-        gateway_chassis = gateway_chassis_get_ordered(sbrec_chassis_by_name,
-                                                      binding_rec);
-        if (gateway_chassis &&
-            gateway_chassis_contains(gateway_chassis, chassis_rec)) {
-
-            our_chassis = gateway_chassis_is_active(
-                gateway_chassis, chassis_rec, active_tunnels);
+        if (ha_chassis_group_contains(binding_rec->ha_chassis_group,
+                                      chassis_rec)) {
+            our_chassis = ha_chassis_group_is_active(
+                binding_rec->ha_chassis_group,
+                active_tunnels, chassis_rec);
 
             add_local_datapath(sbrec_datapath_binding_by_key,
                                sbrec_port_binding_by_datapath,
                                sbrec_port_binding_by_name,
                                binding_rec->datapath, false, local_datapaths);
         }
-        gateway_chassis_destroy(gateway_chassis);
     } else if (!strcmp(binding_rec->type, "l3gateway")) {
         const char *chassis_id = smap_get(&binding_rec->options,
                                           "l3gateway-chassis");
@@ -592,7 +587,6 @@  consider_localnet_port(const struct sbrec_port_binding *binding_rec,
 void
 binding_run(struct ovsdb_idl_txn *ovnsb_idl_txn,
             struct ovsdb_idl_txn *ovs_idl_txn,
-            struct ovsdb_idl_index *sbrec_chassis_by_name,
             struct ovsdb_idl_index *sbrec_datapath_binding_by_key,
             struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
             struct ovsdb_idl_index *sbrec_port_binding_by_name,
@@ -625,7 +619,6 @@  binding_run(struct ovsdb_idl_txn *ovnsb_idl_txn,
      * directly connected logical ports and children of those ports. */
     SBREC_PORT_BINDING_TABLE_FOR_EACH (binding_rec, port_binding_table) {
         consider_local_datapath(ovnsb_idl_txn, ovs_idl_txn,
-                                sbrec_chassis_by_name,
                                 sbrec_datapath_binding_by_key,
                                 sbrec_port_binding_by_datapath,
                                 sbrec_port_binding_by_name,
diff --git a/ovn/controller/binding.h b/ovn/controller/binding.h
index 837e1099f..0f24a0efd 100644
--- a/ovn/controller/binding.h
+++ b/ovn/controller/binding.h
@@ -33,7 +33,6 @@  struct sset;
 void binding_register_ovs_idl(struct ovsdb_idl *);
 void binding_run(struct ovsdb_idl_txn *ovnsb_idl_txn,
                  struct ovsdb_idl_txn *ovs_idl_txn,
-                 struct ovsdb_idl_index *sbrec_chassis_by_name,
                  struct ovsdb_idl_index *sbrec_datapath_binding_by_key,
                  struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
                  struct ovsdb_idl_index *sbrec_port_binding_by_name,
diff --git a/ovn/controller/gchassis.c b/ovn/controller/gchassis.c
deleted file mode 100644
index 34b78bcc0..000000000
--- a/ovn/controller/gchassis.c
+++ /dev/null
@@ -1,222 +0,0 @@ 
-/* Copyright (c) 2017, Red Hat, 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 "gchassis.h"
-#include "lport.h"
-#include "lib/sset.h"
-#include "openvswitch/vlog.h"
-#include "ovn/lib/chassis-index.h"
-#include "ovn/lib/ovn-sb-idl.h"
-
-VLOG_DEFINE_THIS_MODULE(gchassis);
-
-/* gateway_chassis ordering
- */
-static int
-compare_chassis_prio_(const void *a_, const void *b_)
-{
-    const struct gateway_chassis *gc_a = a_;
-    const struct gateway_chassis *gc_b = b_;
-    int prio_diff = gc_b->db->priority - gc_a->db->priority;
-    if (!prio_diff) {
-        return strcmp(gc_b->db->name, gc_a->db->name);
-    }
-    return prio_diff;
-}
-
-struct ovs_list*
-gateway_chassis_get_ordered(struct ovsdb_idl_index *sbrec_chassis_by_name,
-                            const struct sbrec_port_binding *binding)
-{
-    const char *redir_chassis_str;
-    const struct sbrec_chassis *redirect_chassis = NULL;
-
-    /* XXX: redirect-chassis SBDB option handling is supported for backwards
-     * compatibility with N-1 version of ovn-northd. This support can
-     * be removed in OVS 2.9 where Gateway_Chassis list on the port binding
-     * will always be populated by northd */
-    redir_chassis_str = smap_get(&binding->options, "redirect-chassis");
-
-    if (redir_chassis_str) {
-        redirect_chassis = chassis_lookup_by_name(sbrec_chassis_by_name,
-                                                  redir_chassis_str);
-        if (!redirect_chassis) {
-            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
-            VLOG_WARN_RL(&rl, "chassis name (%s) in redirect-chassis option "
-                              "of logical port %s not known",
-                              redir_chassis_str, binding->logical_port);
-        }
-    }
-
-    if (!redirect_chassis && binding->n_gateway_chassis == 0) {
-        return NULL;
-    }
-
-    struct gateway_chassis *gateway_chassis = NULL;
-    int n = 0;
-
-    if (binding->n_gateway_chassis) {
-        gateway_chassis = xmalloc(sizeof *gateway_chassis *
-                                  binding->n_gateway_chassis);
-        for (n = 0; n < binding->n_gateway_chassis; n++) {
-            gateway_chassis[n].db = binding->gateway_chassis[n];
-            gateway_chassis[n].virtual_gwc = false;
-        }
-        qsort(gateway_chassis, n, sizeof *gateway_chassis,
-              compare_chassis_prio_);
-    } else if (redirect_chassis) {
-        /* When only redirect_chassis is available, return a single
-         * virtual entry that it's not on OVSDB, this way the code
-         * handling the returned list will be uniform, regardless
-         * of gateway_chassis being populated or redirect-chassis option
-         * being used */
-        gateway_chassis = xmalloc(sizeof *gateway_chassis);
-        struct sbrec_gateway_chassis *gwc =
-            xzalloc(sizeof *gateway_chassis->db);
-        sbrec_gateway_chassis_init(gwc);
-        gwc->name = xasprintf("%s_%s", binding->logical_port,
-                                       redirect_chassis->name);
-        gwc->chassis = CONST_CAST(struct sbrec_chassis *, redirect_chassis);
-        gateway_chassis->db = gwc;
-        gateway_chassis->virtual_gwc = true;
-        n++;
-    }
-
-    struct ovs_list *list = NULL;
-    if (n) {
-        list = xmalloc(sizeof *list);
-        ovs_list_init(list);
-
-        int i;
-        for (i = 0; i < n; i++) {
-            ovs_list_push_back(list, &gateway_chassis[i].node);
-        }
-    }
-
-    return list;
-}
-
-bool
-gateway_chassis_contains(const struct ovs_list *gateway_chassis,
-                         const struct sbrec_chassis *chassis) {
-    struct gateway_chassis *chassis_item;
-    if (gateway_chassis) {
-        LIST_FOR_EACH (chassis_item, node, gateway_chassis) {
-            if (chassis_item->db->chassis
-                && !strcmp(chassis_item->db->chassis->name, chassis->name)) {
-                return true;
-            }
-        }
-    }
-    return false;
-}
-
-void
-gateway_chassis_destroy(struct ovs_list *list)
-{
-    if (!list) {
-        return;
-    }
-
-    /* XXX: This loop is for backwards compatibility with redirect-chassis
-     * which we insert as a single virtual Gateway_Chassis on the ordered
-     * list */
-    struct gateway_chassis *chassis_item;
-    LIST_FOR_EACH (chassis_item, node, list) {
-        if (chassis_item->virtual_gwc) {
-            free(chassis_item->db->name);
-            free(CONST_CAST(struct sbrec_gateway_chassis *, chassis_item->db));
-        }
-    }
-
-    free(ovs_list_front(list));
-    free(list);
-}
-
-bool
-gateway_chassis_in_pb_contains(const struct sbrec_port_binding *binding,
-                               const struct sbrec_chassis *chassis)
-{
-    if (!binding || !chassis) {
-        return false;
-    }
-
-    /* XXX: redirect-chassis handling for backwards compatibility,
-     * with older ovs-northd during upgrade phase, can be removed
-     * for OVS 2.9 */
-    const char *redirect_chassis = smap_get(&binding->options,
-                                            "redirect-chassis");
-    if (binding->n_gateway_chassis) {
-        int n;
-        for (n = 0; n < binding->n_gateway_chassis; n++) {
-            if (binding->gateway_chassis[n]->chassis
-                && !strcmp(binding->gateway_chassis[n]->chassis->name,
-                           chassis->name)) {
-                return true;
-            }
-        }
-    } else if (redirect_chassis) {
-        return !strcmp(redirect_chassis, chassis->name);
-    }
-
-    return false;
-}
-
-bool
-gateway_chassis_is_active(const struct ovs_list *gateway_chassis,
-                          const struct sbrec_chassis *local_chassis,
-                          const struct sset *active_tunnels)
-{
-    struct gateway_chassis *gwc;
-
-    if (!gateway_chassis
-        || (gateway_chassis && ovs_list_is_empty(gateway_chassis))) {
-        return false;
-    }
-    /* if there's only one chassis, and local chassis is on the list
-     * it's not HA and it's the equivalent of being active */
-    if (ovs_list_is_singleton(gateway_chassis) &&
-        gateway_chassis_contains(gateway_chassis, local_chassis)) {
-        return true;
-    }
-
-    /* if there are no other tunnels active, we assume that the
-     * connection providing tunneling is down, hence we're down */
-    if (sset_is_empty(active_tunnels)) {
-        return false;
-    }
-
-    /* gateway_chassis is an ordered list, by priority, of chassis
-     * hosting the redirect of the port */
-    LIST_FOR_EACH (gwc, node, gateway_chassis) {
-        if (!gwc->db->chassis) {
-            continue;
-        }
-        /* if we found the chassis on the list, and we didn't exit before
-         * on the active_tunnels check for other higher priority chassis
-         * being active, then this chassis is master. */
-        if (!strcmp(gwc->db->chassis->name, local_chassis->name)) {
-            return true;
-        }
-        /* if we find this specific chassis on the list to have an active
-         * tunnel, then 'local_chassis' is not master */
-        if (sset_contains(active_tunnels, gwc->db->chassis->name)) {
-            return false;
-        }
-    }
-    return false;
-}
diff --git a/ovn/controller/gchassis.h b/ovn/controller/gchassis.h
deleted file mode 100644
index 901be4491..000000000
--- a/ovn/controller/gchassis.h
+++ /dev/null
@@ -1,71 +0,0 @@ 
-/* Copyright (c) 2017 Red Hat, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef OVN_GCHASSIS_H
-#define OVN_GCHASSIS_H 1
-
-#include <stdint.h>
-#include "lib/uuid.h"
-#include "openvswitch/hmap.h"
-#include "openvswitch/list.h"
-
-struct ovsdb_idl;
-struct ovsdb_idl_index;
-struct sbrec_chassis;
-struct sbrec_gateway_chassis;
-struct sbrec_port_binding;
-struct sset;
-
-
-/* Gateway_Chassis management
- * ==========================
- *
- * The following structure and methods handle ordering of Gateway_Chassis
- * entries in a chassisredirect port. And parsing redirect-chassis option
- * for backwards compatibility with older (N-1 version of ovn-northd).
- */
-struct gateway_chassis {
-    struct ovs_list node;
-    const struct sbrec_gateway_chassis *db; /* sbrec row for the gwc */
-    bool virtual_gwc; /* db entry not from SBDB, but from redirect-chassis */
-};
-
-/* Gets, and orders by priority/name the list of Gateway_Chassis */
-struct ovs_list *gateway_chassis_get_ordered(
-    struct ovsdb_idl_index *sbrec_chassis_by_name,
-    const struct sbrec_port_binding *binding);
-
-/* Checks if an specific chassis is contained in the gateway_chassis
- * list */
-bool gateway_chassis_contains(const struct ovs_list *gateway_chassis,
-                              const struct sbrec_chassis *chassis);
-
-/* Destroy a gateway_chassis list from memory */
-void gateway_chassis_destroy(struct ovs_list *list);
-
-/* Checks if a chassis is referenced in the port_binding gateway_chassis
- * list or redirect-chassis option (backwards compatibility) */
-bool gateway_chassis_in_pb_contains(
-        const struct sbrec_port_binding *binding,
-        const struct sbrec_chassis *chassis);
-
-/* Returns true if the local chassis is the active gateway among a set
- * of gateway_chassis.  Return false if the local chassis is currently a
- * backup in a set of multiple gateway_chassis. */
-bool gateway_chassis_is_active(
-        const struct ovs_list *gateway_chassis,
-        const struct sbrec_chassis *local_chassis,
-        const struct sset *active_tunnels);
-#endif /* ovn/controller/gchassis.h */
diff --git a/ovn/controller/ha-chassis.c b/ovn/controller/ha-chassis.c
new file mode 100644
index 000000000..498e5ce5a
--- /dev/null
+++ b/ovn/controller/ha-chassis.c
@@ -0,0 +1,203 @@ 
+/* Copyright (c) 2019 Red Hat, 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 "ha-chassis.h"
+#include "lib/sset.h"
+#include "openvswitch/vlog.h"
+#include "ovn/lib/ovn-sb-idl.h"
+
+VLOG_DEFINE_THIS_MODULE(ha_chassis);
+
+static int
+compare_chassis_prio_(const void *a_, const void *b_)
+{
+    const struct sbrec_ha_chassis *ch_a = a_;
+    const struct sbrec_ha_chassis *ch_b = b_;
+    int prio_diff = ch_b->priority - ch_a->priority;
+    if (!prio_diff) {
+        return strcmp(ch_b->chassis->name, ch_a->chassis->name);
+    }
+    return prio_diff;
+}
+
+/* Returns the ordered HA chassis list in the HA chassis group.
+ * Eg. If an HA chassis group has 3 HA chassis
+ *   - HA1 - pri 30
+ *   - HA2 - pri 40 and
+ *   - HA3 - pri 20
+ * and the ref_chassis of HA chassis group is set to - C1 and C2.
+ *
+ * If active_tunnels is NULL, then it returns the ordered list
+ *   -  (HA2, HA1, HA3)
+ *
+ * If active_tunnels is set to - (HA1, HA2, C1, C2) and
+ * local_chassis is HA3, then it returns the ordered list
+ *  - (HA2, HA1, HA3)
+ *
+ * If active_tunnels is set to - (HA1, C1, C2) and
+ * local_chassis is HA3, then it returns the ordered list
+ *  - (HA1, HA3)
+ *
+ * If active_tunnels is set to - (C1, C2) and
+ * local_chassis is HA3, then it returns the ordered list
+ *  - (HA3)
+ *
+ * If active_tunnels is set is empty and local_chassis is HA3,
+ * then it returns NULL.
+ */
+static struct ha_chassis_ordered *
+get_ordered_ha_chassis_list(const struct sbrec_ha_chassis_group *ha_ch_grp,
+                            const struct sset *active_tunnels,
+                            const struct sbrec_chassis *local_chassis)
+{
+    struct sbrec_ha_chassis *ha_ch_order =
+        xzalloc(sizeof *ha_ch_order * ha_ch_grp->n_ha_chassis);
+
+    size_t n_ha_ch = 0;
+
+    for (size_t i = 0; i < ha_ch_grp->n_ha_chassis; i++) {
+        if (!ha_ch_grp->ha_chassis[i]->chassis) {
+            continue;
+        }
+
+        /* Don't add it to the list for ordering if it is not active. */
+        if (ha_ch_grp->ha_chassis[i]->chassis != local_chassis &&
+            active_tunnels &&
+            !sset_contains(active_tunnels,
+                           ha_ch_grp->ha_chassis[i]->chassis->name)) {
+            continue;
+        }
+
+        ha_ch_order[n_ha_ch].chassis = ha_ch_grp->ha_chassis[i]->chassis;
+        ha_ch_order[n_ha_ch].priority = ha_ch_grp->ha_chassis[i]->priority;
+        n_ha_ch++;
+    }
+
+    if (!n_ha_ch) {
+        free(ha_ch_order);
+        return NULL;
+    }
+
+    struct ha_chassis_ordered *ordered_ha_ch;
+    if (n_ha_ch == 1) {
+        if (active_tunnels) {
+            /* If n_ha_ch is 1, it means only the local chassis is in the
+            * ha_ch_order list. Check if this local chassis has active
+            * bfd session with any of the referenced chassis. If so,
+            * then the local chassis can be active. Otherwise it can't.
+            * This can happen in the following scenario.
+            * Lets say we have chassis HA1 (prioirty 20) and HA2 (priority 10)
+            * in the ha_chasis_group and compute chassis C1 and C2 are in the
+            * reference chassis list. If HA1 chassis has lost the link and
+            * when this function is called for HA2 we need to consider
+            * HA2 as active since it has active BFD sessions with C1 and C2.
+            * On HA1 chassis, this function won't be called since
+            * active_tunnels set will be empty.
+            * */
+            bool can_local_chassis_be_active = false;
+            for (size_t i = 0; i < ha_ch_grp->n_ref_chassis; i++) {
+                if (sset_contains(active_tunnels,
+                                ha_ch_grp->ref_chassis[i]->name)) {
+                    can_local_chassis_be_active = true;
+                    break;
+                }
+            }
+            if (!can_local_chassis_be_active) {
+                free(ha_ch_order);
+                return NULL;
+            }
+        }
+    } else {
+        qsort(ha_ch_order, n_ha_ch, sizeof *ha_ch_order,
+              compare_chassis_prio_);
+    }
+
+    ordered_ha_ch = xmalloc(sizeof *ordered_ha_ch);
+    ordered_ha_ch->ha_ch = ha_ch_order;
+    ordered_ha_ch->n_ha_ch = n_ha_ch;
+
+    return ordered_ha_ch;
+}
+
+void
+ha_chassis_destroy_ordered(struct ha_chassis_ordered *ordered_ha_ch)
+{
+    if (ordered_ha_ch) {
+        free(ordered_ha_ch->ha_ch);
+        free(ordered_ha_ch);
+    }
+}
+
+
+/* Returns true if the local_chassis is the master of
+ * the HA chassis group, false otherwise. */
+bool
+ha_chassis_group_is_active(
+    const struct sbrec_ha_chassis_group *ha_ch_grp,
+    const struct sset *active_tunnels,
+    const struct sbrec_chassis *local_chassis)
+{
+    if (!ha_ch_grp || !ha_ch_grp->n_ha_chassis) {
+        return false;
+    }
+
+    if (ha_ch_grp->n_ha_chassis == 1) {
+        return (ha_ch_grp->ha_chassis[0]->chassis == local_chassis);
+    }
+
+    if (sset_is_empty(active_tunnels)) {
+        /* If active tunnel sset is empty, it means it has lost
+         * connectivity with other chassis. */
+        return false;
+    }
+
+    struct ha_chassis_ordered *ordered_ha_ch =
+        get_ordered_ha_chassis_list(ha_ch_grp, active_tunnels, local_chassis);
+    if (!ordered_ha_ch) {
+        return false;
+    }
+
+    struct sbrec_chassis *active_ch = ordered_ha_ch->ha_ch[0].chassis;
+    ha_chassis_destroy_ordered(ordered_ha_ch);
+
+    return (active_ch == local_chassis);
+}
+
+bool
+ha_chassis_group_contains(
+    const struct sbrec_ha_chassis_group *ha_chassis_grp,
+    const struct sbrec_chassis *chassis)
+{
+    if (ha_chassis_grp && chassis) {
+        for (size_t i = 0; i < ha_chassis_grp->n_ha_chassis; i++) {
+            if (ha_chassis_grp->ha_chassis[i]->chassis == chassis) {
+                return true;
+            }
+        }
+    }
+    return false;
+}
+
+struct ha_chassis_ordered *
+ha_chassis_get_ordered(const struct sbrec_ha_chassis_group *ha_chassis_grp)
+{
+    if (!ha_chassis_grp || !ha_chassis_grp->n_ha_chassis) {
+        return NULL;
+    }
+
+    return get_ordered_ha_chassis_list(ha_chassis_grp, NULL, NULL);
+}
diff --git a/ovn/controller/ha-chassis.h b/ovn/controller/ha-chassis.h
new file mode 100644
index 000000000..3768c2a5c
--- /dev/null
+++ b/ovn/controller/ha-chassis.h
@@ -0,0 +1,50 @@ 
+/* Copyright (c) 2019 Red Hat, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef OVN_HA_CHASSIS_H
+#define OVN_HA_CHASSIS_H 1
+
+#include <stdint.h>
+#include "openvswitch/hmap.h"
+#include "openvswitch/list.h"
+
+struct sbrec_chassis;
+struct sbrec_ha_chassis_group;
+struct sset;
+
+struct ha_chassis_ordered {
+    struct sbrec_ha_chassis *ha_ch;
+    size_t n_ha_ch;
+};
+
+/* Returns true if the local chassis is the active gateway among a set
+ * of gateway_chassis.  Return false if the local chassis is currently a
+ * backup in a set of multiple gateway_chassis. */
+bool ha_chassis_group_is_active(
+    const struct sbrec_ha_chassis_group *ha_chassis_grp,
+    const struct sset *active_tunnels,
+    const struct sbrec_chassis *local_chassis);
+
+bool ha_chassis_group_contains(
+    const struct sbrec_ha_chassis_group *ha_chassis_grp,
+    const struct sbrec_chassis *chassis);
+
+struct ha_chassis_ordered *ha_chassis_get_ordered(
+    const struct sbrec_ha_chassis_group *ha_chassis_grp);
+
+void ha_chassis_destroy_ordered(
+    struct ha_chassis_ordered *ordered_ha_ch);
+
+#endif /* OVN_HA_CHASSIS_H */
diff --git a/ovn/controller/lflow.c b/ovn/controller/lflow.c
index 8db81927e..e4a8004fc 100644
--- a/ovn/controller/lflow.c
+++ b/ovn/controller/lflow.c
@@ -16,7 +16,7 @@ 
 #include <config.h>
 #include "lflow.h"
 #include "coverage.h"
-#include "gchassis.h"
+#include "ha-chassis.h"
 #include "lport.h"
 #include "ofctrl.h"
 #include "openvswitch/dynamic-string.h"
@@ -56,14 +56,12 @@  struct lookup_port_aux {
 };
 
 struct condition_aux {
-    struct ovsdb_idl_index *sbrec_chassis_by_name;
     struct ovsdb_idl_index *sbrec_port_binding_by_name;
     const struct sbrec_chassis *chassis;
     const struct sset *active_tunnels;
 };
 
 static void consider_logical_flow(
-    struct ovsdb_idl_index *sbrec_chassis_by_name,
     struct ovsdb_idl_index *sbrec_multicast_group_by_name_datapath,
     struct ovsdb_idl_index *sbrec_port_binding_by_name,
     const struct sbrec_logical_flow *,
@@ -117,14 +115,11 @@  is_chassis_resident_cb(const void *c_aux_, const char *port_name)
         /* for non-chassisredirect ports */
         return pb->chassis && pb->chassis == c_aux->chassis;
     } else {
-        struct ovs_list *gateway_chassis;
-        gateway_chassis = gateway_chassis_get_ordered(
-            c_aux->sbrec_chassis_by_name, pb);
-        if (gateway_chassis) {
-            bool active = gateway_chassis_is_active(gateway_chassis,
-                                                    c_aux->chassis,
-                                                    c_aux->active_tunnels);
-            gateway_chassis_destroy(gateway_chassis);
+        if (ha_chassis_group_contains(pb->ha_chassis_group,
+                                      c_aux->chassis)) {
+            bool active = ha_chassis_group_is_active(pb->ha_chassis_group,
+                                                     c_aux->active_tunnels,
+                                                     c_aux->chassis);
             return active;
         }
         return false;
@@ -141,7 +136,6 @@  is_switch(const struct sbrec_datapath_binding *ldp)
 /* Adds the logical flows from the Logical_Flow table to flow tables. */
 static void
 add_logical_flows(
-    struct ovsdb_idl_index *sbrec_chassis_by_name,
     struct ovsdb_idl_index *sbrec_multicast_group_by_name_datapath,
     struct ovsdb_idl_index *sbrec_port_binding_by_name,
     const struct sbrec_dhcp_options_table *dhcp_options_table,
@@ -180,8 +174,7 @@  add_logical_flows(
     nd_ra_opts_init(&nd_ra_opts);
 
     SBREC_LOGICAL_FLOW_TABLE_FOR_EACH (lflow, logical_flow_table) {
-        consider_logical_flow(sbrec_chassis_by_name,
-                              sbrec_multicast_group_by_name_datapath,
+        consider_logical_flow(sbrec_multicast_group_by_name_datapath,
                               sbrec_port_binding_by_name,
                               lflow, local_datapaths,
                               chassis, &dhcp_opts, &dhcpv6_opts, &nd_ra_opts,
@@ -197,7 +190,6 @@  add_logical_flows(
 
 static void
 consider_logical_flow(
-    struct ovsdb_idl_index *sbrec_chassis_by_name,
     struct ovsdb_idl_index *sbrec_multicast_group_by_name_datapath,
     struct ovsdb_idl_index *sbrec_port_binding_by_name,
     const struct sbrec_logical_flow *lflow,
@@ -295,7 +287,6 @@  consider_logical_flow(
         .dp = lflow->logical_datapath
     };
     struct condition_aux cond_aux = {
-        .sbrec_chassis_by_name = sbrec_chassis_by_name,
         .sbrec_port_binding_by_name = sbrec_port_binding_by_name,
         .chassis = chassis,
         .active_tunnels = active_tunnels,
@@ -460,8 +451,7 @@  add_neighbor_flows(struct ovsdb_idl_index *sbrec_port_binding_by_name,
 /* Translates logical flows in the Logical_Flow table in the OVN_SB database
  * into OpenFlow flows.  See ovn-architecture(7) for more information. */
 void
-lflow_run(struct ovsdb_idl_index *sbrec_chassis_by_name,
-          struct ovsdb_idl_index *sbrec_multicast_group_by_name_datapath,
+lflow_run(struct ovsdb_idl_index *sbrec_multicast_group_by_name_datapath,
           struct ovsdb_idl_index *sbrec_port_binding_by_name,
           const struct sbrec_dhcp_options_table *dhcp_options_table,
           const struct sbrec_dhcpv6_options_table *dhcpv6_options_table,
@@ -479,8 +469,7 @@  lflow_run(struct ovsdb_idl_index *sbrec_chassis_by_name,
 {
     COVERAGE_INC(lflow_run);
 
-    add_logical_flows(sbrec_chassis_by_name,
-                      sbrec_multicast_group_by_name_datapath,
+    add_logical_flows(sbrec_multicast_group_by_name_datapath,
                       sbrec_port_binding_by_name, dhcp_options_table,
                       dhcpv6_options_table, logical_flow_table,
                       local_datapaths, chassis, addr_sets, port_groups,
diff --git a/ovn/controller/lflow.h b/ovn/controller/lflow.h
index d19338140..05d479d1a 100644
--- a/ovn/controller/lflow.h
+++ b/ovn/controller/lflow.h
@@ -65,8 +65,7 @@  struct uuid;
 #define LOG_PIPELINE_LEN 24
 
 void lflow_init(void);
-void lflow_run(struct ovsdb_idl_index *sbrec_chassis_by_name,
-               struct ovsdb_idl_index *sbrec_multicast_group_by_name_datapath,
+void lflow_run(struct ovsdb_idl_index *sbrec_multicast_group_by_name_datapath,
                struct ovsdb_idl_index *sbrec_port_binding_by_name,
                const struct sbrec_dhcp_options_table *,
                const struct sbrec_dhcpv6_options_table *,
diff --git a/ovn/controller/ovn-controller.c b/ovn/controller/ovn-controller.c
index 882cc195f..986a28c8a 100644
--- a/ovn/controller/ovn-controller.c
+++ b/ovn/controller/ovn-controller.c
@@ -710,7 +710,7 @@  main(int argc, char *argv[])
                     bfd_calculate_active_tunnels(br_int, &active_tunnels);
                 }
 
-                binding_run(ovnsb_idl_txn, ovs_idl_txn, sbrec_chassis_by_name,
+                binding_run(ovnsb_idl_txn, ovs_idl_txn,
                             sbrec_datapath_binding_by_key,
                             sbrec_port_binding_by_datapath,
                             sbrec_port_binding_by_name,
@@ -740,7 +740,7 @@  main(int argc, char *argv[])
                 enum mf_field_id mff_ovn_geneve = ofctrl_run(
                     br_int, &pending_ct_zones);
 
-                pinctrl_run(ovnsb_idl_txn, sbrec_chassis_by_name,
+                pinctrl_run(ovnsb_idl_txn,
                             sbrec_datapath_binding_by_key,
                             sbrec_port_binding_by_datapath,
                             sbrec_port_binding_by_key,
@@ -760,7 +760,6 @@  main(int argc, char *argv[])
 
                         struct hmap flow_table = HMAP_INITIALIZER(&flow_table);
                         lflow_run(
-                            sbrec_chassis_by_name,
                             sbrec_multicast_group_by_name_datapath,
                             sbrec_port_binding_by_name,
                             sbrec_dhcp_options_table_get(ovnsb_idl_loop.idl),
@@ -774,15 +773,13 @@  main(int argc, char *argv[])
 
                         if (chassis_id) {
                             bfd_run(
-                                sbrec_chassis_by_name,
-                                sbrec_port_binding_by_datapath,
                                 ovsrec_interface_table_get(ovs_idl_loop.idl),
                                 br_int, chassis,
-                                sbrec_sb_global_table_get(ovnsb_idl_loop.idl),
-                                &local_datapaths);
+                                sbrec_ha_chassis_group_table_get(
+                                    ovnsb_idl_loop.idl),
+                                sbrec_sb_global_table_get(ovnsb_idl_loop.idl));
                         }
                         physical_run(
-                            sbrec_chassis_by_name,
                             sbrec_port_binding_by_name,
                             sbrec_multicast_group_table_get(
                                 ovnsb_idl_loop.idl),
diff --git a/ovn/controller/physical.c b/ovn/controller/physical.c
index da89890ac..738640446 100644
--- a/ovn/controller/physical.c
+++ b/ovn/controller/physical.c
@@ -17,7 +17,7 @@ 
 #include "binding.h"
 #include "byte-order.h"
 #include "flow.h"
-#include "gchassis.h"
+#include "ha-chassis.h"
 #include "lflow.h"
 #include "lport.h"
 #include "lib/bundle.h"
@@ -377,8 +377,7 @@  load_logical_ingress_metadata(const struct sbrec_port_binding *binding,
 }
 
 static void
-consider_port_binding(struct ovsdb_idl_index *sbrec_chassis_by_name,
-                      struct ovsdb_idl_index *sbrec_port_binding_by_name,
+consider_port_binding(struct ovsdb_idl_index *sbrec_port_binding_by_name,
                       enum mf_field_id mff_ovn_geneve,
                       const struct simap *ct_zones,
                       const struct sset *active_tunnels,
@@ -446,13 +445,13 @@  consider_port_binding(struct ovsdb_idl_index *sbrec_chassis_by_name,
         return;
     }
 
-    struct ovs_list *gateway_chassis
-        = gateway_chassis_get_ordered(sbrec_chassis_by_name, binding);
+    struct ha_chassis_ordered *ha_ch_ordered
+        = ha_chassis_get_ordered(binding->ha_chassis_group);
 
     if (!strcmp(binding->type, "chassisredirect")
         && (binding->chassis == chassis
-            || gateway_chassis_is_active(gateway_chassis, chassis,
-                                         active_tunnels))) {
+            || ha_chassis_group_is_active(binding->ha_chassis_group,
+                                          active_tunnels, chassis))) {
 
         /* Table 33, priority 100.
          * =======================
@@ -588,7 +587,7 @@  consider_port_binding(struct ovsdb_idl_index *sbrec_chassis_by_name,
                 goto out;
             }
         } else {
-            if (!gateway_chassis || ovs_list_is_short(gateway_chassis)) {
+            if (!ha_ch_ordered || ha_ch_ordered->n_ha_ch < 2) {
                 /* It's on a single remote chassis */
                 if (!binding->chassis) {
                     goto out;
@@ -599,7 +598,8 @@  consider_port_binding(struct ovsdb_idl_index *sbrec_chassis_by_name,
                 }
                 ofport = tun->ofport;
             } else {
-                /* It's distributed across the "gateway_chassis" list */
+                /* It's distributed across the chassis belonging to
+                 * an HA chassis group. */
                 is_ha_remote = true;
             }
         }
@@ -753,34 +753,36 @@  consider_port_binding(struct ovsdb_idl_index *sbrec_chassis_by_name,
             /* Output to tunnel. */
             ofpact_put_OUTPUT(ofpacts_p)->port = rem_tun->ofport;
         } else {
-            struct gateway_chassis *gwc;
             /* Make sure all tunnel endpoints use the same encapsulation,
              * and set it up */
-            LIST_FOR_EACH (gwc, node, gateway_chassis) {
-                if (gwc->db->chassis) {
-                    if (!tun) {
-                        tun = chassis_tunnel_find(gwc->db->chassis->name, NULL);
-                    } else {
-                        struct chassis_tunnel *chassis_tunnel =
-                            chassis_tunnel_find(gwc->db->chassis->name, NULL);
-                        if (chassis_tunnel &&
-                            tun->type != chassis_tunnel->type) {
-                            static struct vlog_rate_limit rl =
-                                VLOG_RATE_LIMIT_INIT(1, 1);
-                            VLOG_ERR_RL(&rl, "Port %s has Gateway_Chassis "
-                                             "with mixed encapsulations, only "
-                                             "uniform encapsulations are "
-                                             "supported.",
-                                        binding->logical_port);
-                            goto out;
-                        }
+            for (size_t i = 0; i < ha_ch_ordered->n_ha_ch; i++) {
+                const struct sbrec_chassis *ch =
+                    ha_ch_ordered->ha_ch[i].chassis;
+                if (!ch) {
+                    continue;
+                }
+                if (!tun) {
+                    tun = chassis_tunnel_find(ch->name, NULL);
+                } else {
+                    struct chassis_tunnel *chassis_tunnel =
+                        chassis_tunnel_find(ch->name, NULL);
+                    if (chassis_tunnel &&
+                        tun->type != chassis_tunnel->type) {
+                        static struct vlog_rate_limit rl =
+                            VLOG_RATE_LIMIT_INIT(1, 1);
+                        VLOG_ERR_RL(&rl, "Port %s has Gateway_Chassis "
+                                            "with mixed encapsulations, only "
+                                            "uniform encapsulations are "
+                                            "supported.",
+                                    binding->logical_port);
+                        goto out;
                     }
                 }
             }
             if (!tun) {
                 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
-                VLOG_ERR_RL(&rl, "No tunnel endpoint found for gateways in "
-                                 "Gateway_Chassis of port %s",
+                VLOG_ERR_RL(&rl, "No tunnel endpoint found for HA chassis in "
+                                 "HA chassis group of port %s",
                             binding->logical_port);
                 goto out;
             }
@@ -791,24 +793,27 @@  consider_port_binding(struct ovsdb_idl_index *sbrec_chassis_by_name,
             /* Output to tunnels with active/backup */
             struct ofpact_bundle *bundle = ofpact_put_BUNDLE(ofpacts_p);
 
-            LIST_FOR_EACH (gwc, node, gateway_chassis) {
-                if (gwc->db->chassis) {
-                    tun = chassis_tunnel_find(gwc->db->chassis->name, NULL);
-                    if (!tun) {
-                        continue;
-                    }
-                    if (bundle->n_slaves >= BUNDLE_MAX_SLAVES) {
-                        static struct vlog_rate_limit rl =
-                                VLOG_RATE_LIMIT_INIT(1, 1);
-                        VLOG_WARN_RL(&rl, "Remote endpoints for port beyond "
-                                          "BUNDLE_MAX_SLAVES");
-                        break;
-                    }
-                    ofpbuf_put(ofpacts_p, &tun->ofport,
-                               sizeof tun->ofport);
-                    bundle = ofpacts_p->header;
-                    bundle->n_slaves++;
+            for (size_t i = 0; i < ha_ch_ordered->n_ha_ch; i++) {
+                const struct sbrec_chassis *ch =
+                    ha_ch_ordered->ha_ch[i].chassis;
+                if (!ch) {
+                    continue;
+                }
+                tun = chassis_tunnel_find(ch->name, NULL);
+                if (!tun) {
+                    continue;
+                }
+                if (bundle->n_slaves >= BUNDLE_MAX_SLAVES) {
+                    static struct vlog_rate_limit rl =
+                            VLOG_RATE_LIMIT_INIT(1, 1);
+                    VLOG_WARN_RL(&rl, "Remote endpoints for port beyond "
+                                        "BUNDLE_MAX_SLAVES");
+                    break;
                 }
+                ofpbuf_put(ofpacts_p, &tun->ofport,
+                            sizeof tun->ofport);
+                bundle = ofpacts_p->header;
+                bundle->n_slaves++;
             }
 
             bundle->algorithm = NX_BD_ALG_ACTIVE_BACKUP;
@@ -822,8 +827,8 @@  consider_port_binding(struct ovsdb_idl_index *sbrec_chassis_by_name,
                         &match, ofpacts_p);
     }
 out:
-    if (gateway_chassis) {
-        gateway_chassis_destroy(gateway_chassis);
+    if (ha_ch_ordered) {
+        ha_chassis_destroy_ordered(ha_ch_ordered);
     }
 }
 
@@ -967,8 +972,7 @@  update_ofports(struct simap *old, struct simap *new)
 }
 
 void
-physical_run(struct ovsdb_idl_index *sbrec_chassis_by_name,
-             struct ovsdb_idl_index *sbrec_port_binding_by_name,
+physical_run(struct ovsdb_idl_index *sbrec_port_binding_by_name,
              const struct sbrec_multicast_group_table *multicast_group_table,
              const struct sbrec_port_binding_table *port_binding_table,
              enum mf_field_id mff_ovn_geneve,
@@ -1119,8 +1123,7 @@  physical_run(struct ovsdb_idl_index *sbrec_chassis_by_name,
      * 64 for logical-to-physical translation. */
     const struct sbrec_port_binding *binding;
     SBREC_PORT_BINDING_TABLE_FOR_EACH (binding, port_binding_table) {
-        consider_port_binding(sbrec_chassis_by_name,
-                              sbrec_port_binding_by_name,
+        consider_port_binding(sbrec_port_binding_by_name,
                               mff_ovn_geneve, ct_zones,
                               active_tunnels,
                               local_datapaths, binding, chassis,
diff --git a/ovn/controller/physical.h b/ovn/controller/physical.h
index a7a4def37..38c3a8771 100644
--- a/ovn/controller/physical.h
+++ b/ovn/controller/physical.h
@@ -43,8 +43,7 @@  struct sset;
 #define OVN_GENEVE_LEN 4
 
 void physical_register_ovs_idl(struct ovsdb_idl *);
-void physical_run(struct ovsdb_idl_index *sbrec_chassis_by_name,
-                  struct ovsdb_idl_index *sbrec_port_binding_by_name,
+void physical_run(struct ovsdb_idl_index *sbrec_port_binding_by_name,
                   const struct sbrec_multicast_group_table *,
                   const struct sbrec_port_binding_table *,
                   enum mf_field_id mff_ovn_geneve,
diff --git a/ovn/controller/pinctrl.c b/ovn/controller/pinctrl.c
index 100a20ff2..78a617210 100644
--- a/ovn/controller/pinctrl.c
+++ b/ovn/controller/pinctrl.c
@@ -23,7 +23,7 @@ 
 #include "dirs.h"
 #include "dp-packet.h"
 #include "flow.h"
-#include "gchassis.h"
+#include "ha-chassis.h"
 #include "lport.h"
 #include "nx-match.h"
 #include "ovn-controller.h"
@@ -81,7 +81,6 @@  static void init_send_garps(void);
 static void destroy_send_garps(void);
 static void send_garp_wait(void);
 static void send_garp_run(
-    struct ovsdb_idl_index *sbrec_chassis_by_name,
     struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
     struct ovsdb_idl_index *sbrec_port_binding_by_name,
     const struct ovsrec_bridge *,
@@ -1505,7 +1504,6 @@  pinctrl_recv(const struct sbrec_dns_table *dns_table,
 
 void
 pinctrl_run(struct ovsdb_idl_txn *ovnsb_idl_txn,
-            struct ovsdb_idl_index *sbrec_chassis_by_name,
             struct ovsdb_idl_index *sbrec_datapath_binding_by_key,
             struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
             struct ovsdb_idl_index *sbrec_port_binding_by_key,
@@ -1554,7 +1552,7 @@  pinctrl_run(struct ovsdb_idl_txn *ovnsb_idl_txn,
     run_put_mac_bindings(ovnsb_idl_txn, sbrec_datapath_binding_by_key,
                          sbrec_port_binding_by_key,
                          sbrec_mac_binding_by_lport_ip);
-    send_garp_run(sbrec_chassis_by_name, sbrec_port_binding_by_datapath,
+    send_garp_run(sbrec_port_binding_by_datapath,
                   sbrec_port_binding_by_name, br_int, chassis,
                   local_datapaths, active_tunnels);
     send_ipv6_ras(sbrec_port_binding_by_datapath,
@@ -2329,8 +2327,7 @@  get_localnet_vifs_l3gwports(
 }
 
 static bool
-pinctrl_is_chassis_resident(struct ovsdb_idl_index *sbrec_chassis_by_name,
-                            struct ovsdb_idl_index *sbrec_port_binding_by_name,
+pinctrl_is_chassis_resident(struct ovsdb_idl_index *sbrec_port_binding_by_name,
                             const struct sbrec_chassis *chassis,
                             const struct sset *active_tunnels,
                             const char *port_name)
@@ -2343,13 +2340,8 @@  pinctrl_is_chassis_resident(struct ovsdb_idl_index *sbrec_chassis_by_name,
     if (strcmp(pb->type, "chassisredirect")) {
         return pb->chassis == chassis;
     } else {
-        struct ovs_list *gateway_chassis =
-            gateway_chassis_get_ordered(sbrec_chassis_by_name, pb);
-        bool active = gateway_chassis_is_active(gateway_chassis,
-                                                chassis,
-                                                active_tunnels);
-        gateway_chassis_destroy(gateway_chassis);
-        return active;
+        return ha_chassis_group_is_active(pb->ha_chassis_group,
+                                          active_tunnels, chassis);
     }
 }
 
@@ -2420,8 +2412,7 @@  extract_addresses_with_port(const char *addresses,
 }
 
 static void
-consider_nat_address(struct ovsdb_idl_index *sbrec_chassis_by_name,
-                     struct ovsdb_idl_index *sbrec_port_binding_by_name,
+consider_nat_address(struct ovsdb_idl_index *sbrec_port_binding_by_name,
                      const char *nat_address,
                      const struct sbrec_port_binding *pb,
                      struct sset *nat_address_keys,
@@ -2434,7 +2425,7 @@  consider_nat_address(struct ovsdb_idl_index *sbrec_chassis_by_name,
     if (!extract_addresses_with_port(nat_address, laddrs, &lport)
         || (!lport && !strcmp(pb->type, "patch"))
         || (lport && !pinctrl_is_chassis_resident(
-                sbrec_chassis_by_name, sbrec_port_binding_by_name, chassis,
+                sbrec_port_binding_by_name, chassis,
                 active_tunnels, lport))) {
         destroy_lport_addresses(laddrs);
         free(laddrs);
@@ -2454,8 +2445,7 @@  consider_nat_address(struct ovsdb_idl_index *sbrec_chassis_by_name,
 }
 
 static void
-get_nat_addresses_and_keys(struct ovsdb_idl_index *sbrec_chassis_by_name,
-                           struct ovsdb_idl_index *sbrec_port_binding_by_name,
+get_nat_addresses_and_keys(struct ovsdb_idl_index *sbrec_port_binding_by_name,
                            struct sset *nat_address_keys,
                            struct sset *local_l3gw_ports,
                            const struct sbrec_chassis *chassis,
@@ -2473,8 +2463,7 @@  get_nat_addresses_and_keys(struct ovsdb_idl_index *sbrec_chassis_by_name,
 
         if (pb->n_nat_addresses) {
             for (int i = 0; i < pb->n_nat_addresses; i++) {
-                consider_nat_address(sbrec_chassis_by_name,
-                                     sbrec_port_binding_by_name,
+                consider_nat_address(sbrec_port_binding_by_name,
                                      pb->nat_addresses[i], pb,
                                      nat_address_keys, chassis,
                                      active_tunnels,
@@ -2486,8 +2475,7 @@  get_nat_addresses_and_keys(struct ovsdb_idl_index *sbrec_chassis_by_name,
             const char *nat_addresses_options = smap_get(&pb->options,
                                                          "nat-addresses");
             if (nat_addresses_options) {
-                consider_nat_address(sbrec_chassis_by_name,
-                                     sbrec_port_binding_by_name,
+                consider_nat_address(sbrec_port_binding_by_name,
                                      nat_addresses_options, pb,
                                      nat_address_keys, chassis,
                                      active_tunnels,
@@ -2504,8 +2492,7 @@  send_garp_wait(void)
 }
 
 static void
-send_garp_run(struct ovsdb_idl_index *sbrec_chassis_by_name,
-              struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
+send_garp_run(struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
               struct ovsdb_idl_index *sbrec_port_binding_by_name,
               const struct ovsrec_bridge *br_int,
               const struct sbrec_chassis *chassis,
@@ -2524,8 +2511,7 @@  send_garp_run(struct ovsdb_idl_index *sbrec_chassis_by_name,
                                 br_int, chassis, local_datapaths,
                                 &localnet_vifs, &local_l3gw_ports);
 
-    get_nat_addresses_and_keys(sbrec_chassis_by_name,
-                               sbrec_port_binding_by_name,
+    get_nat_addresses_and_keys(sbrec_port_binding_by_name,
                                &nat_ip_keys, &local_l3gw_ports,
                                chassis, active_tunnels,
                                &nat_addresses);
diff --git a/ovn/controller/pinctrl.h b/ovn/controller/pinctrl.h
index 697d02478..f61d7056e 100644
--- a/ovn/controller/pinctrl.h
+++ b/ovn/controller/pinctrl.h
@@ -32,7 +32,6 @@  struct sbrec_dns_table;
 
 void pinctrl_init(void);
 void pinctrl_run(struct ovsdb_idl_txn *ovnsb_idl_txn,
-                 struct ovsdb_idl_index *sbrec_chassis_by_name,
                  struct ovsdb_idl_index *sbrec_datapath_binding_by_key,
                  struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
                  struct ovsdb_idl_index *sbrec_port_binding_by_key,