The active tunnels are only used to find out whether the local chassis
is the active chassis of an HA chassis group.  In preparation for
handling changes to the tunnel interfaces incrementally, which only
needs a recompute if the groups for which the local chassis is active
change, cache in runtime data the HA chassis groups that contain the
local chassis and the ones for which it is the active chassis.

Add a handler for the SB HA_Chassis_Group table to keep them up to date.
Updates of the reference chassis, which follow the workloads, are
handled incrementally as long as they don't change the groups for which
the local chassis is active.  Other updates of a group trigger a
recompute if the group contains or contained the local chassis, or if a
port binding of a local datapath references it, as the local datapaths
and the physical flows of such port bindings depend on the HA chassis of
the group.  So far, these changes were taken into account by the
recompute triggered by the BFD configuration changes of the tunnel
interfaces that follow them.

Assisted-by: Claude Opus 4.8, Claude Code
Signed-off-by: Paulo Guilherme Silva <[email protected]>
---
 controller/ha-chassis.c          |  30 +++++
 controller/ha-chassis.h          |   9 ++
 controller/ovn-controller.c      | 194 +++++++++++++++++++++++++++++--
 tests/ovn-controller.at          | 164 ++++++++++++++++++++++++++
 tests/ovn-inc-proc-graph-dump.at |   5 +-
 5 files changed, 389 insertions(+), 13 deletions(-)

diff --git a/controller/ha-chassis.c b/controller/ha-chassis.c
index ad0b3ef0b..bb2402ff7 100644
--- a/controller/ha-chassis.c
+++ b/controller/ha-chassis.c
@@ -17,6 +17,7 @@
 
 #include "ha-chassis.h"
 #include "lib/sset.h"
+#include "lib/uuidset.h"
 #include "openvswitch/vlog.h"
 #include "lib/ovn-sb-idl.h"
 
@@ -217,6 +218,35 @@ ha_chassis_group_contains(
     return false;
 }
 
+/* Adds to 'local_groups' the UUID of every HA chassis group in
+ * 'ha_ch_grp_table' that contains 'local_chassis' and to 'active_groups'
+ * the UUID of every such group for which 'local_chassis' is currently the
+ * active chassis, given the set of 'active_tunnels'. */
+void
+ha_chassis_groups_collect_local(
+    const struct sbrec_ha_chassis_group_table *ha_ch_grp_table,
+    const struct sset *active_tunnels,
+    const struct sbrec_chassis *local_chassis,
+    struct uuidset *local_groups,
+    struct uuidset *active_groups)
+{
+    if (!local_chassis) {
+        return;
+    }
+
+    const struct sbrec_ha_chassis_group *ha_ch_grp;
+    SBREC_HA_CHASSIS_GROUP_TABLE_FOR_EACH (ha_ch_grp, ha_ch_grp_table) {
+        if (!ha_chassis_group_contains(ha_ch_grp, local_chassis)) {
+            continue;
+        }
+        uuidset_insert(local_groups, &ha_ch_grp->header_.uuid);
+        if (ha_chassis_group_is_active(ha_ch_grp, active_tunnels,
+                                       local_chassis)) {
+            uuidset_insert(active_groups, &ha_ch_grp->header_.uuid);
+        }
+    }
+}
+
 struct ha_chassis_ordered *
 ha_chassis_get_ordered(const struct sbrec_ha_chassis_group *ha_chassis_grp)
 {
diff --git a/controller/ha-chassis.h b/controller/ha-chassis.h
index c7c91e001..0dd8caec7 100644
--- a/controller/ha-chassis.h
+++ b/controller/ha-chassis.h
@@ -22,7 +22,9 @@
 
 struct sbrec_chassis;
 struct sbrec_ha_chassis_group;
+struct sbrec_ha_chassis_group_table;
 struct sset;
+struct uuidset;
 
 struct ha_chassis_ordered {
     struct sbrec_ha_chassis *ha_ch;
@@ -41,6 +43,13 @@ bool ha_chassis_group_contains(
     const struct sbrec_ha_chassis_group *ha_chassis_grp,
     const struct sbrec_chassis *chassis);
 
+void ha_chassis_groups_collect_local(
+    const struct sbrec_ha_chassis_group_table *ha_ch_grp_table,
+    const struct sset *active_tunnels,
+    const struct sbrec_chassis *local_chassis,
+    struct uuidset *local_groups,
+    struct uuidset *active_groups);
+
 struct ha_chassis_ordered *ha_chassis_get_ordered(
     const struct sbrec_ha_chassis_group *ha_chassis_grp);
 
diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c
index 5343753ca..ee49e8153 100644
--- a/controller/ovn-controller.c
+++ b/controller/ovn-controller.c
@@ -96,6 +96,7 @@
 #include "route-exchange.h"
 #include "route-table.h"
 #include "garp_rarp.h"
+#include "ha-chassis.h"
 #include "host-if-monitor.h"
 #include "neighbor.h"
 #include "neighbor-exchange.h"
@@ -1373,6 +1374,10 @@ struct ed_type_runtime_data {
      */
     struct related_lports related_lports;
     struct sset active_tunnels;
+    /* HA chassis groups that contain the local chassis and the subset of
+     * them for which the local chassis is the active one. */
+    struct uuidset local_ha_groups;
+    struct uuidset active_ha_groups;
 
     /* runtime data engine private data. */
     struct hmap qos_map;
@@ -1449,6 +1454,18 @@ struct ed_type_runtime_data {
  * |                  | interfaces we need to track the changes to the   |
  * |                  | active tunnels.                                  |
  *  ---------------------------------------------------------------------
+ * | local_ha_groups  | HA chassis groups that contain the local chassis |
+ * | active_ha_groups | and the ones for which it is the active chassis. |
+ * |                  | New and deleted groups and changes to the        |
+ * |                  | reference chassis of the groups that don't       |
+ * |                  | change active_ha_groups are handled without      |
+ * |                  | changing any other runtime data.  Any other      |
+ * |                  | change to a group that contains or contained the |
+ * |                  | local chassis, or that is referenced by a port   |
+ * |                  | binding of a local datapath, triggers the full   |
+ * |                  | recompute of runtime data engine and hence the   |
+ * |                  | tracked data doesn't track them.                 |
+ *  ---------------------------------------------------------------------
  *
  */
 
@@ -1473,6 +1490,8 @@ en_runtime_data_init(struct engine_node *node OVS_UNUSED,
     simap_init(&data->local_lports);
     related_lports_init(&data->related_lports);
     sset_init(&data->active_tunnels);
+    uuidset_init(&data->local_ha_groups);
+    uuidset_init(&data->active_ha_groups);
     hmap_init(&data->qos_map);
     smap_init(&data->local_iface_ids);
     local_binding_data_init(&data->lbinding_data);
@@ -1493,6 +1512,8 @@ en_runtime_data_cleanup(void *data)
     simap_destroy(&rt_data->local_lports);
     related_lports_destroy(&rt_data->related_lports);
     sset_destroy(&rt_data->active_tunnels);
+    uuidset_destroy(&rt_data->local_ha_groups);
+    uuidset_destroy(&rt_data->active_ha_groups);
     destroy_qos_map(&rt_data->qos_map);
     smap_destroy(&rt_data->local_iface_ids);
     local_datapaths_destroy(&rt_data->local_datapaths);
@@ -1597,6 +1618,37 @@ init_binding_ctx(struct engine_node *node,
     b_ctx_out->localnet_learn_fdb_changed = false;
 }
 
+/* Calculates the set of 'active_tunnels', the set of HA chassis groups
+ * that contain the local chassis ('local_ha_groups') and the subset of
+ * them for which the local chassis is the active one ('active_ha_groups').
+ */
+static void
+runtime_data_calculate_active_ha(struct engine_node *node,
+                                 const struct binding_ctx_in *b_ctx_in,
+                                 struct sset *active_tunnels,
+                                 struct uuidset *local_ha_groups,
+                                 struct uuidset *active_ha_groups)
+{
+    struct ed_type_ofctrl_is_connected *ed_ofctrl_is_connected =
+        engine_get_input_data("ofctrl_is_connected", node);
+    if (ed_ofctrl_is_connected->connected) {
+        /* Calculate the active tunnels only if have an an active
+         * OpenFlow connection to br-int.
+         * If we don't have a connection to br-int, it could mean
+         * ovs-vswitchd is down for some reason and the BFD status
+         * in the Interface rows could be stale. So its better to
+         * consider 'active_tunnels' set to be empty if it's not
+         * connected. */
+        bfd_calculate_active_tunnels(b_ctx_in->br_int, active_tunnels);
+    }
+
+    const struct sbrec_ha_chassis_group_table *ha_ch_grp_table =
+        EN_OVSDB_GET(engine_get_input("SB_ha_chassis_group", node));
+    ha_chassis_groups_collect_local(ha_ch_grp_table, active_tunnels,
+                                    b_ctx_in->chassis_rec, local_ha_groups,
+                                    active_ha_groups);
+}
+
 static enum engine_node_state
 en_runtime_data_run(struct engine_node *node, void *data)
 {
@@ -1619,6 +1671,8 @@ en_runtime_data_run(struct engine_node *node, void *data)
         simap_destroy(local_lports);
         related_lports_destroy(&rt_data->related_lports);
         sset_destroy(active_tunnels);
+        uuidset_clear(&rt_data->local_ha_groups);
+        uuidset_clear(&rt_data->active_ha_groups);
         destroy_qos_map(&rt_data->qos_map);
         smap_destroy(&rt_data->local_iface_ids);
         hmap_init(local_datapaths);
@@ -1638,18 +1692,9 @@ en_runtime_data_run(struct engine_node *node, void *data)
     struct binding_ctx_out b_ctx_out;
     init_binding_ctx(node, rt_data, &b_ctx_in, &b_ctx_out);
 
-    struct ed_type_ofctrl_is_connected *ed_ofctrl_is_connected =
-        engine_get_input_data("ofctrl_is_connected", node);
-    if (ed_ofctrl_is_connected->connected) {
-        /* Calculate the active tunnels only if have an an active
-         * OpenFlow connection to br-int.
-         * If we don't have a connection to br-int, it could mean
-         * ovs-vswitchd is down for some reason and the BFD status
-         * in the Interface rows could be stale. So its better to
-         * consider 'active_tunnels' set to be empty if it's not
-         * connected. */
-        bfd_calculate_active_tunnels(b_ctx_in.br_int, active_tunnels);
-    }
+    runtime_data_calculate_active_ha(node, &b_ctx_in, active_tunnels,
+                                     &rt_data->local_ha_groups,
+                                     &rt_data->active_ha_groups);
 
     binding_run(&b_ctx_in, &b_ctx_out);
     rt_data->localnet_learn_fdb = b_ctx_out.localnet_learn_fdb;
@@ -1748,6 +1793,126 @@ runtime_data_ovs_interface_shadow_handler(struct 
engine_node *node, void *data)
     return result;
 }
 
+/* Returns true if the only change to the tracked 'ha_ch_grp' is an update
+ * of its 'ref_chassis' column. */
+static bool
+ha_chassis_group_only_ref_chassis_updated(
+    const struct sbrec_ha_chassis_group *ha_ch_grp)
+{
+    if (!sbrec_ha_chassis_group_is_updated(
+            ha_ch_grp, SBREC_HA_CHASSIS_GROUP_COL_REF_CHASSIS)) {
+        return false;
+    }
+
+    for (size_t i = 0; i < SBREC_HA_CHASSIS_GROUP_N_COLUMNS; i++) {
+        if (i != SBREC_HA_CHASSIS_GROUP_COL_REF_CHASSIS &&
+            sbrec_ha_chassis_group_is_updated(ha_ch_grp, i)) {
+            return false;
+        }
+    }
+    return true;
+}
+
+/* Returns true if a port binding of any of the 'local_datapaths'
+ * references 'ha_ch_grp'. */
+static bool
+ha_chassis_group_is_referenced_locally(
+    const struct sbrec_ha_chassis_group *ha_ch_grp,
+    const struct hmap *local_datapaths,
+    struct ovsdb_idl_index *sbrec_port_binding_by_datapath)
+{
+    struct sbrec_port_binding *target =
+        sbrec_port_binding_index_init_row(sbrec_port_binding_by_datapath);
+    bool found = false;
+
+    const struct local_datapath *ld;
+    HMAP_FOR_EACH (ld, hmap_node, local_datapaths) {
+        sbrec_port_binding_index_set_datapath(target, ld->datapath);
+
+        const struct sbrec_port_binding *pb;
+        SBREC_PORT_BINDING_FOR_EACH_EQUAL (pb, target,
+                                           sbrec_port_binding_by_datapath) {
+            if (pb->ha_chassis_group == ha_ch_grp) {
+                found = true;
+                goto out;
+            }
+        }
+    }
+
+out:
+    sbrec_port_binding_index_destroy_row(target);
+    return found;
+}
+
+static enum engine_input_handler_result
+runtime_data_sb_ha_chassis_group_handler(struct engine_node *node,
+                                         void *data)
+{
+    struct ed_type_runtime_data *rt_data = data;
+    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);
+
+    const struct sbrec_ha_chassis_group_table *ha_ch_grp_table =
+        EN_OVSDB_GET(engine_get_input("SB_ha_chassis_group", node));
+    const struct sbrec_ha_chassis_group *ha_ch_grp;
+    SBREC_HA_CHASSIS_GROUP_TABLE_FOR_EACH_TRACKED (ha_ch_grp,
+                                                   ha_ch_grp_table) {
+        const struct uuid *uuid = &ha_ch_grp->header_.uuid;
+
+        if (sbrec_ha_chassis_group_is_deleted(ha_ch_grp)) {
+            /* Port bindings referencing a deleted group are handled by the
+             * SB_port_binding handler. */
+            uuidset_find_and_delete(&rt_data->local_ha_groups, uuid);
+            uuidset_find_and_delete(&rt_data->active_ha_groups, uuid);
+            continue;
+        }
+
+        bool is_local = ha_chassis_group_contains(ha_ch_grp,
+                                                  b_ctx_in.chassis_rec);
+        bool is_active = is_local &&
+                         ha_chassis_group_is_active(ha_ch_grp,
+                                                    &rt_data->active_tunnels,
+                                                    b_ctx_in.chassis_rec);
+        if (sbrec_ha_chassis_group_is_new(ha_ch_grp)) {
+            /* Port bindings referencing a new group are handled by the
+             * SB_port_binding handler. */
+            if (is_local) {
+                uuidset_insert(&rt_data->local_ha_groups, uuid);
+            }
+            if (is_active) {
+                uuidset_insert(&rt_data->active_ha_groups, uuid);
+            }
+            continue;
+        }
+
+        if (!ha_chassis_group_only_ref_chassis_updated(ha_ch_grp)) {
+            /* The local datapaths and the physical flows of the port
+             * bindings referencing the group depend on its HA chassis.
+             * Only care about the groups relevant to the local chassis. */
+            if (is_local ||
+                uuidset_contains(&rt_data->local_ha_groups, uuid) ||
+                ha_chassis_group_is_referenced_locally(
+                    ha_ch_grp, &rt_data->local_datapaths,
+                    b_ctx_in.sbrec_port_binding_by_datapath)) {
+                return EN_UNHANDLED;
+            }
+            continue;
+        }
+
+        /* Changes to the reference chassis, which are frequent as they
+         * follow the workloads, can't change whether the group contains
+         * the local chassis but they can change whether it is the active
+         * one. */
+        if (is_active != uuidset_contains(&rt_data->active_ha_groups,
+                                          uuid)) {
+            return EN_UNHANDLED;
+        }
+    }
+
+    return EN_HANDLED_UNCHANGED;
+}
+
 static enum engine_input_handler_result
 runtime_data_sb_port_binding_handler(struct engine_node *node, void *data)
 {
@@ -7636,6 +7801,11 @@ inc_proc_ovn_controller_init(
                      runtime_data_sb_port_binding_handler);
     /* Run sb_ro_handler after port_binding_handler in case port get deleted */
     engine_add_input(&en_runtime_data, &en_sb_ro, runtime_data_sb_ro_handler);
+    /* The HA chassis group handler must be executed after the
+     * sb_port_binding_handler as it relies on new port bindings referencing
+     * new HA chassis groups to be processed first. */
+    engine_add_input(&en_runtime_data, &en_sb_ha_chassis_group,
+                     runtime_data_sb_ha_chassis_group_handler);
 
     /* The OVS interface handler for runtime_data changes MUST be executed
      * after the sb_port_binding_handler as port_binding deletes must be
diff --git a/tests/ovn-controller.at b/tests/ovn-controller.at
index a7b79fc67..0ae4b4b54 100644
--- a/tests/ovn-controller.at
+++ b/tests/ovn-controller.at
@@ -46,6 +46,21 @@ $recompute : compute - $compute"
     check test "$node_compute_ct" -ne "0"
   fi
 }
+
+# bfd_state HV REMOTE
+#
+# Prints the BFD state of the tunnel from HV to REMOTE, or "disabled" if
+# BFD is not enabled on that tunnel.
+bfd_state() {
+    if test "$(as $1 ovs-vsctl --if-exists get interface ovn-$2-0 \
+               bfd:enable | tr -d '"')" != true; then
+        echo disabled
+    else
+        as $1 ovs-vsctl --if-exists get interface ovn-$2-0 \
+            bfd_status:state | tr -d '"'
+    fi
+}
+
 m4_divert_pop([PREPARE_TESTS])
 
 OVN_FOR_EACH_NORTHD([
@@ -4296,3 +4311,152 @@ OVS_WAIT_UNTIL([test "$(as hv2 ovs-vsctl --bare get 
port ovn-hv1-0 other_config)
 OVN_CLEANUP([hv1],[hv2])
 AT_CLEANUP
 ])
+
+m4_define([HA_GW_REF_CHASSIS_SETUP], [
+ovn_start
+
+net_add n1
+for i in 1 2; do
+    sim_add gw$i
+    as gw$i
+    check ovs-vsctl add-br br-phys
+    ovn_attach n1 br-phys 192.168.0.$i
+    check ovs-vsctl set open . external_ids:ovn-bridge-mappings=public:br-phys
+
+    sim_add hv$i
+    as hv$i
+    check ovs-vsctl add-br br-phys
+    ovn_attach n1 br-phys 192.168.0.1$i
+done
+
+check ovn-nbctl ls-add sw0
+check ovn-nbctl lsp-add sw0 vm1 -- lsp-set-addresses vm1 "50:54:00:00:00:01 
10.0.0.11"
+check ovn-nbctl lsp-add sw0 vm2 -- lsp-set-addresses vm2 "50:54:00:00:00:02 
10.0.0.12"
+check ovn-nbctl lr-add lr0
+check ovn-nbctl lrp-add lr0 lr0-sw0 00:00:00:00:ff:01 10.0.0.1/24
+check ovn-nbctl lsp-add-router-port sw0 sw0-lr0 lr0-sw0
+check ovn-nbctl lrp-add lr0 lr0-pub 00:00:00:00:ff:02 172.168.0.1/24
+check ovn-nbctl lrp-set-gateway-chassis lr0-pub gw1 $1
+check ovn-nbctl lrp-set-gateway-chassis lr0-pub gw2 $2
+check ovn-nbctl ls-add pub
+check ovn-nbctl lsp-add-router-port pub pub-lr0 lr0-pub
+check ovn-nbctl lsp-add-localnet-port pub ln-pub public
+check ovn-nbctl lr-nat-add lr0 snat 172.168.0.10 10.0.0.0/24
+check ovn-nbctl --wait=hv sync
+])
+
+OVN_FOR_EACH_NORTHD([
+AT_SETUP([ovn-controller - HA gateway failover on ref_chassis changes])
+AT_KEYWORDS([ovn ha-ref-chassis])
+
+HA_GW_REF_CHASSIS_SETUP([10], [20])
+
+gw1=$(fetch_column Chassis _uuid name=gw1)
+gw2=$(fetch_column Chassis _uuid name=gw2)
+wait_row_count Port_Binding 1 logical_port=cr-lr0-pub chassis=$gw2
+OVS_WAIT_UNTIL([test "$(bfd_state gw1 gw2)" = up])
+
+# Stop ovs-vswitchd on gw2, the active gateway.  gw1 has no BFD session
+# with any reference chassis, so it can't become active.
+as gw2
+OVS_APP_EXIT_AND_WAIT([ovs-vswitchd])
+OVS_WAIT_UNTIL([test "$(bfd_state gw1 gw2)" = down])
+check_row_count Port_Binding 0 logical_port=cr-lr0-pub chassis=$gw1
+
+# Bind a VM on hv1.  hv1 becomes the only reference chassis and once the
+# BFD session between gw1 and hv1 is up, gw1 becomes active.
+as hv1 check ovs-vsctl add-port br-int vif1 -- \
+    set interface vif1 external_ids:iface-id=vm1
+wait_for_ports_up vm1
+OVS_WAIT_UNTIL([test "$(bfd_state gw1 hv1)" = up])
+wait_row_count Port_Binding 1 logical_port=cr-lr0-pub chassis=$gw1
+
+# Remove the VM from hv1.  gw1 has no BFD session with any reference
+# chassis anymore, so it must release the gateway port.
+as hv1 check ovs-vsctl del-port br-int vif1
+wait_row_count Port_Binding 1 logical_port=vm1 chassis=[[]]
+OVS_WAIT_UNTIL([test "$(bfd_state gw1 hv1)" = disabled])
+wait_row_count Port_Binding 0 logical_port=cr-lr0-pub chassis=$gw1
+
+as gw2
+OVS_APP_EXIT_AND_WAIT([ovn-controller])
+OVS_APP_EXIT_AND_WAIT([ovsdb-server])
+
+OVN_CLEANUP([gw1],[hv1],[hv2])
+AT_CLEANUP
+])
+
+OVN_FOR_EACH_NORTHD([
+AT_SETUP([ovn-controller - HA gateway failover on ref_chassis only changes])
+AT_KEYWORDS([ovn ha-ref-chassis])
+
+HA_GW_REF_CHASSIS_SETUP([10], [20])
+
+# A second, unconnected, router whose active gateway is gw3 keeps hv1 as a
+# reference chassis of a group that contains gw1, so the BFD session
+# between gw1 and hv1 doesn't depend on the reference chassis of lr0-pub.
+sim_add gw3
+as gw3
+check ovs-vsctl add-br br-phys
+ovn_attach n1 br-phys 192.168.0.3
+check ovs-vsctl set open . external_ids:ovn-bridge-mappings=public:br-phys
+
+check ovn-nbctl ls-add sw1
+check ovn-nbctl lsp-add sw1 vm3 -- lsp-set-addresses vm3 "50:54:00:00:00:03 
10.0.1.11"
+check ovn-nbctl lr-add lr1
+check ovn-nbctl lrp-add lr1 lr1-sw1 00:00:00:00:ff:11 10.0.1.1/24
+check ovn-nbctl lsp-add-router-port sw1 sw1-lr1 lr1-sw1
+check ovn-nbctl lrp-add lr1 lr1-pub 00:00:00:00:ff:12 172.168.0.2/24
+check ovn-nbctl lrp-set-gateway-chassis lr1-pub gw1 10
+check ovn-nbctl lrp-set-gateway-chassis lr1-pub gw3 20
+check ovn-nbctl ls-add pub1
+check ovn-nbctl lsp-add-router-port pub1 pub1-lr1 lr1-pub
+check ovn-nbctl lsp-add-localnet-port pub1 ln-pub1 public
+
+gw1=$(fetch_column Chassis _uuid name=gw1)
+gw2=$(fetch_column Chassis _uuid name=gw2)
+gw3=$(fetch_column Chassis _uuid name=gw3)
+
+as hv1 check ovs-vsctl add-port br-int vif3 -- \
+    set interface vif3 external_ids:iface-id=vm3
+wait_for_ports_up vm3
+wait_row_count Port_Binding 1 logical_port=cr-lr0-pub chassis=$gw2
+wait_row_count Port_Binding 1 logical_port=cr-lr1-pub chassis=$gw3
+OVS_WAIT_UNTIL([test "$(bfd_state gw1 gw2)" = up])
+OVS_WAIT_UNTIL([test "$(bfd_state gw1 gw3)" = up])
+OVS_WAIT_UNTIL([test "$(bfd_state gw1 hv1)" = up])
+
+# Stop ovs-vswitchd on gw2, the active gateway of lr0-pub.  gw1 has a BFD
+# session with hv1 but hv1 isn't a reference chassis of the lr0-pub
+# group, so gw1 can't become active for it.
+as gw2
+OVS_APP_EXIT_AND_WAIT([ovs-vswitchd])
+OVS_WAIT_UNTIL([test "$(bfd_state gw1 gw2)" = down])
+check ovn-nbctl --wait=sb sync
+check_row_count Port_Binding 0 logical_port=cr-lr0-pub chassis=$gw1
+
+# Bind a VM of sw0 on hv1.  Only the reference chassis of the lr0-pub group
+# change, the BFD session with hv1 is already up, and gw1 becomes active.
+as hv1 check ovs-vsctl add-port br-int vif1 -- \
+    set interface vif1 external_ids:iface-id=vm1
+wait_for_ports_up vm1
+wait_row_count Port_Binding 1 logical_port=cr-lr0-pub chassis=$gw1
+
+# Stop ovs-vswitchd on hv1.  gw1 loses its BFD session with the only
+# reference chassis of the lr0-pub group, so it must release the gateway
+# port.
+as hv1
+OVS_APP_EXIT_AND_WAIT([ovs-vswitchd])
+OVS_WAIT_UNTIL([test "$(bfd_state gw1 hv1)" = down])
+wait_row_count Port_Binding 0 logical_port=cr-lr0-pub chassis=$gw1
+check_row_count Port_Binding 1 logical_port=cr-lr1-pub chassis=$gw3
+
+for hv in gw2 hv1; do
+    as $hv
+    OVS_APP_EXIT_AND_WAIT([ovn-controller])
+    OVS_APP_EXIT_AND_WAIT([ovsdb-server])
+done
+
+OVN_CLEANUP([gw1],[gw3],[hv2])
+AT_CLEANUP
+])
diff --git a/tests/ovn-inc-proc-graph-dump.at b/tests/ovn-inc-proc-graph-dump.at
index d6f93c889..b3df37a01 100644
--- a/tests/ovn-inc-proc-graph-dump.at
+++ b/tests/ovn-inc-proc-graph-dump.at
@@ -316,6 +316,7 @@ digraph "Incremental-Processing-Engine" {
        SB_port_binding [[style=filled, shape=box, fillcolor=white, 
label="SB_port_binding"]];
        postponed_ports [[style=filled, shape=box, fillcolor=white, 
label="postponed_ports"]];
        sb_ro [[style=filled, shape=box, fillcolor=white, label="sb_ro"]];
+       SB_ha_chassis_group [[style=filled, shape=box, fillcolor=white, 
label="SB_ha_chassis_group"]];
        OVS_port [[style=filled, shape=box, fillcolor=white, label="OVS_port"]];
        OVS_interface [[style=filled, shape=box, fillcolor=white, 
label="OVS_interface"]];
        ovs_interface_shadow [[style=filled, shape=box, fillcolor=white, 
label="ovs_interface_shadow"]];
@@ -331,6 +332,7 @@ digraph "Incremental-Processing-Engine" {
        SB_port_binding -> runtime_data 
[[label="runtime_data_sb_port_binding_handler"]];
        postponed_ports -> runtime_data 
[[label="runtime_data_sb_port_binding_handler"]];
        sb_ro -> runtime_data [[label="runtime_data_sb_ro_handler"]];
+       SB_ha_chassis_group -> runtime_data 
[[label="runtime_data_sb_ha_chassis_group_handler"]];
        OVS_port -> runtime_data [[label="engine_noop_handler"]];
        ovs_interface_shadow -> runtime_data 
[[label="runtime_data_ovs_interface_shadow_handler"]];
        port_groups [[style=filled, shape=box, fillcolor=white, 
label="port_groups"]];
@@ -451,7 +453,6 @@ digraph "Incremental-Processing-Engine" {
        SB_fdb -> mac_cache [[label="mac_cache_sb_fdb_handler"]];
        SB_datapath_binding -> mac_cache 
[[label="mac_cache_sb_datapath_binding_handler"]];
        SB_port_binding -> mac_cache [[label="engine_noop_handler"]];
-       SB_ha_chassis_group [[style=filled, shape=box, fillcolor=white, 
label="SB_ha_chassis_group"]];
        bfd_chassis [[style=filled, shape=box, fillcolor=white, 
label="bfd_chassis"]];
        OVS_open_vswitch -> bfd_chassis [[label=""]];
        SB_chassis -> bfd_chassis [[label=""]];
@@ -546,6 +547,8 @@ digraph "Incremental-Processing-Engine" {
        postponed_ports [[style=filled, shape=box, fillcolor=white, 
label="postponed_ports"]];
        sb_ro -> runtime_data [[label="runtime_data_sb_ro_handler"]];
        sb_ro [[style=filled, shape=box, fillcolor=white, label="sb_ro"]];
+       SB_ha_chassis_group -> runtime_data 
[[label="runtime_data_sb_ha_chassis_group_handler"]];
+       SB_ha_chassis_group [[style=filled, shape=box, fillcolor=white, 
label="SB_ha_chassis_group"]];
        OVS_port -> runtime_data [[label="engine_noop_handler"]];
        OVS_port [[style=filled, shape=box, fillcolor=white, label="OVS_port"]];
        ovs_interface_shadow -> runtime_data 
[[label="runtime_data_ovs_interface_shadow_handler"]];
-- 
2.34.1


-- 




_'Esta mensagem é direcionada apenas para os endereços constantes no 
cabeçalho inicial. Se você não está listado nos endereços constantes no 
cabeçalho, pedimos-lhe que desconsidere completamente o conteúdo dessa 
mensagem e cuja cópia, encaminhamento e/ou execução das ações citadas estão 
imediatamente anuladas e proibidas'._


* **'Apesar do Magazine Luiza tomar 
todas as precauções razoáveis para assegurar que nenhum vírus esteja 
presente nesse e-mail, a empresa não poderá aceitar a responsabilidade por 
quaisquer perdas ou danos causados por esse e-mail ou por seus anexos'.*



_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to