The "local_datapths" typo is still present in this version of the patch. However, since that's the only thing holding it back,

Acked-by: Mark Michelson <[email protected]>

I think whoever commits this can correct the typo.

On 2/16/23 02:16, Ales Musil wrote:
In order to reuse parsed data keep hash map
of 'struct ovn_controller_lb', that is local for the
controller, in separate engine node called 'lb_data'.
Those data will be later on used to determine if we need
to flush CT for changed/removed LB backends.

Reported-at: https://bugzilla.redhat.com/1839103
Signed-off-by: Ales Musil <[email protected]>
---
v2: Rebase on top of current main.
     Address comments from Dumitru:
     - Fix the memory leak.
     - Fix the find function.
     Address comments from Mark:
     - Make the function lb_is_local more ergonomic.
---
  controller/lflow.c          | 244 ++++++-------------
  controller/lflow.h          |  14 +-
  controller/local_data.c     |  26 ++
  controller/local_data.h     |   3 +
  controller/ovn-controller.c | 460 +++++++++++++++++++++++++++++++-----
  lib/lb.c                    |  34 +++
  lib/lb.h                    |   8 +
  7 files changed, 547 insertions(+), 242 deletions(-)

diff --git a/controller/lflow.c b/controller/lflow.c
index 08ce0386f..a0a26460c 100644
--- a/controller/lflow.c
+++ b/controller/lflow.c
@@ -98,10 +98,7 @@ consider_logical_flow(const struct sbrec_logical_flow *lflow,
                        struct lflow_ctx_out *l_ctx_out);
static void
-consider_lb_hairpin_flows(struct objdep_mgr *mgr,
-                          const struct sbrec_load_balancer *sbrec_lb,
-                          const struct hmap *local_datapaths,
-                          const struct smap *template_vars,
+consider_lb_hairpin_flows(const struct ovn_controller_lb *lb,
                            bool use_ct_mark,
                            struct ovn_desired_flow_table *flow_table,
                            struct simap *ids);
@@ -801,43 +798,6 @@ lflow_handle_changed_ref(enum objdep_type type, const char 
*res_name,
      return true;
  }
-bool
-lb_handle_changed_ref(enum objdep_type type, const char *res_name,
-                      struct ovs_list *objs_todo,
-                      const void *in_arg, void *out_arg)
-{
-    struct lflow_ctx_in *l_ctx_in = CONST_CAST(struct lflow_ctx_in *, in_arg);
-    struct lflow_ctx_out *l_ctx_out = out_arg;
-
-    struct object_to_resources_list_node *resource_lb_uuid;
-    LIST_FOR_EACH_POP (resource_lb_uuid, list_node, objs_todo) {
-        VLOG_DBG("Reprocess LB "UUID_FMT" for resource type: %s, name: %s",
-                 UUID_ARGS(&resource_lb_uuid->obj_uuid),
-                 objdep_type_name(type), res_name);
-
-        const struct sbrec_load_balancer *lb =
-            sbrec_load_balancer_table_get_for_uuid(
-                l_ctx_in->lb_table, &resource_lb_uuid->obj_uuid);
-        if (!lb) {
-            VLOG_DBG("Failed to find LB "UUID_FMT" referred by: %s",
-                     UUID_ARGS(&resource_lb_uuid->obj_uuid), res_name);
-        } else {
-            ofctrl_remove_flows(l_ctx_out->flow_table,
-                                &resource_lb_uuid->obj_uuid);
-
-            consider_lb_hairpin_flows(l_ctx_out->lb_deps_mgr, lb,
-                                      l_ctx_in->local_datapaths,
-                                      l_ctx_in->template_vars,
-                                      l_ctx_in->lb_hairpin_use_ct_mark,
-                                      l_ctx_out->flow_table,
-                                      l_ctx_out->hairpin_lb_ids);
-        }
-
-        free(resource_lb_uuid);
-    }
-    return true;
-}
-
  static void
  lflow_parse_ctrl_meter(const struct sbrec_logical_flow *lflow,
                         struct ovn_extend_table *meter_table,
@@ -1647,10 +1607,9 @@ add_lb_vip_hairpin_reply_action(struct in6_addr *vip6, 
ovs_be32 vip,
   * original destination tuple stored by ovn-northd.
   */
  static void
-add_lb_vip_hairpin_flows(struct ovn_controller_lb *lb,
+add_lb_vip_hairpin_flows(const struct ovn_controller_lb *lb,
                           struct ovn_lb_vip *lb_vip,
                           struct ovn_lb_backend *lb_backend,
-                         uint8_t lb_proto,
                           bool use_ct_mark,
                           struct ovn_desired_flow_table *flow_table)
  {
@@ -1689,7 +1648,7 @@ add_lb_vip_hairpin_flows(struct ovn_controller_lb *lb,
                            ntohl(vip4));
          }
- add_lb_vip_hairpin_reply_action(NULL, snat_vip4, lb_proto,
+        add_lb_vip_hairpin_reply_action(NULL, snat_vip4, lb->proto,
                                          lb_backend->port,
                                          lb->slb->header_.uuid.parts[0],
                                          &ofpacts);
@@ -1714,17 +1673,17 @@ add_lb_vip_hairpin_flows(struct ovn_controller_lb *lb,
                              ntoh128(vip6_value));
          }
- add_lb_vip_hairpin_reply_action(snat_vip6, 0, lb_proto,
+        add_lb_vip_hairpin_reply_action(snat_vip6, 0, lb->proto,
                                          lb_backend->port,
                                          lb->slb->header_.uuid.parts[0],
                                          &ofpacts);
      }
if (lb_backend->port) {
-        match_set_nw_proto(&hairpin_match, lb_proto);
+        match_set_nw_proto(&hairpin_match, lb->proto);
          match_set_tp_dst(&hairpin_match, htons(lb_backend->port));
          if (!lb->hairpin_orig_tuple) {
-            match_set_ct_nw_proto(&hairpin_match, lb_proto);
+            match_set_ct_nw_proto(&hairpin_match, lb->proto);
              match_set_ct_tp_dst(&hairpin_match, htons(lb_vip->vip_port));
          } else {
              match_set_reg_masked(&hairpin_match,
@@ -1784,7 +1743,7 @@ add_lb_ct_snat_hairpin_for_dp(const struct 
ovn_controller_lb *lb,
  }
static void
-add_lb_ct_snat_hairpin_dp_flows(struct ovn_controller_lb *lb,
+add_lb_ct_snat_hairpin_dp_flows(const struct ovn_controller_lb *lb,
                                  uint32_t id,
                                  struct ovn_desired_flow_table *flow_table)
  {
@@ -1877,10 +1836,9 @@ add_lb_ct_snat_hairpin_dp_flows(struct ovn_controller_lb 
*lb,
   * that this LB belongs to. These flows (and the actual SNAT flow) get added
   * by add_lb_ct_snat_hairpin_dp_flows(). */
  static void
-add_lb_ct_snat_hairpin_vip_flow(struct ovn_controller_lb *lb,
+add_lb_ct_snat_hairpin_vip_flow(const struct ovn_controller_lb *lb,
                                  uint32_t id,
                                  struct ovn_lb_vip *lb_vip,
-                                uint8_t lb_proto,
                                  struct ovn_desired_flow_table *flow_table)
  {
      uint64_t stub[1024 / 8];
@@ -1973,10 +1931,10 @@ add_lb_ct_snat_hairpin_vip_flow(struct 
ovn_controller_lb *lb,
          }
      }
- match_set_nw_proto(&match, lb_proto);
+    match_set_nw_proto(&match, lb->proto);
      if (lb_vip->vip_port) {
          if (!lb->hairpin_orig_tuple) {
-            match_set_ct_nw_proto(&match, lb_proto);
+            match_set_ct_nw_proto(&match, lb->proto);
              match_set_ct_tp_dst(&match, htons(lb_vip->vip_port));
          } else {
              match_set_reg_masked(&match, MFF_LOG_LB_ORIG_TP_DPORT - MFF_REG0,
@@ -2007,9 +1965,8 @@ add_lb_ct_snat_hairpin_vip_flow(struct ovn_controller_lb 
*lb,
   * Note: 'conjunctive_id' must be a unique identifier for each LB as it is 
used
   * as a conjunctive flow id. */
  static void
-add_lb_ct_snat_hairpin_flows(struct ovn_controller_lb *lb,
+add_lb_ct_snat_hairpin_flows(const struct ovn_controller_lb *lb,
                               uint32_t conjunctive_id,
-                             uint8_t lb_proto,
                               struct ovn_desired_flow_table *flow_table)
  {
      /* We must add a flow for each LB VIP. In the general case, this flow
@@ -2046,102 +2003,47 @@ add_lb_ct_snat_hairpin_flows(struct ovn_controller_lb 
*lb,
      for (int i = 0; i < lb->n_vips; i++) {
          struct ovn_lb_vip *lb_vip = &lb->vips[i];
          add_lb_ct_snat_hairpin_vip_flow(lb, conjunctive_id,
-                                        lb_vip, lb_proto, flow_table);
+                                        lb_vip, flow_table);
      }
add_lb_ct_snat_hairpin_dp_flows(lb, conjunctive_id, flow_table);
  }
static void
-consider_lb_hairpin_flows(struct objdep_mgr *mgr,
-                          const struct sbrec_load_balancer *sbrec_lb,
-                          const struct hmap *local_datapaths,
-                          const struct smap *template_vars,
+consider_lb_hairpin_flows(const struct ovn_controller_lb *lb,
                            bool use_ct_mark,
                            struct ovn_desired_flow_table *flow_table,
                            struct simap *ids)
  {
-    int id = simap_get(ids, sbrec_lb->name);
-    VLOG_DBG("Load Balancer %s has conjunctive flow id %u",
-             sbrec_lb->name, id);
-
-    /* Check if we need to add flows or not.  If there is one datapath
-     * in the local_datapaths, it means all the datapaths of the lb
-     * will be in the local_datapaths. */
-    size_t i;
-    for (i = 0; i < sbrec_lb->n_datapaths; i++) {
-        if (get_local_datapath(local_datapaths,
-                               sbrec_lb->datapaths[i]->tunnel_key)) {
-            break;
-        }
-    }
-
-    if (sbrec_lb->n_datapaths && i == sbrec_lb->n_datapaths) {
-        return;
-    }
-
-    struct sbrec_logical_dp_group *dp_group = sbrec_lb->datapath_group;
-
-    for (i = 0; dp_group && i < dp_group->n_datapaths; i++) {
-        if (get_local_datapath(local_datapaths,
-                               dp_group->datapaths[i]->tunnel_key)) {
-            break;
-        }
-    }
-
-    if (dp_group && i == dp_group->n_datapaths) {
-        return;
-    }
-
-    struct sset template_vars_ref = SSET_INITIALIZER(&template_vars_ref);
-    struct ovn_controller_lb *lb =
-        ovn_controller_lb_create(sbrec_lb, template_vars, &template_vars_ref);
-    uint8_t lb_proto = IPPROTO_TCP;
-    if (lb->slb->protocol && lb->slb->protocol[0]) {
-        if (!strcmp(lb->slb->protocol, "udp")) {
-            lb_proto = IPPROTO_UDP;
-        } else if (!strcmp(lb->slb->protocol, "sctp")) {
-            lb_proto = IPPROTO_SCTP;
-        }
-    }
+    int id = simap_get(ids, lb->slb->name);
+    VLOG_DBG("Load Balancer %s has conjunctive flow id %u", lb->slb->name, id);
- const char *tv_name;
-    SSET_FOR_EACH (tv_name, &template_vars_ref) {
-        objdep_mgr_add(mgr, OBJDEP_TYPE_TEMPLATE, tv_name,
-                       &sbrec_lb->header_.uuid);
-    }
-    for (i = 0; i < lb->n_vips; i++) {
+    for (size_t i = 0; i < lb->n_vips; i++) {
          struct ovn_lb_vip *lb_vip = &lb->vips[i];
for (size_t j = 0; j < lb_vip->n_backends; j++) {
              struct ovn_lb_backend *lb_backend = &lb_vip->backends[j];
- add_lb_vip_hairpin_flows(lb, lb_vip, lb_backend, lb_proto,
+            add_lb_vip_hairpin_flows(lb, lb_vip, lb_backend,
                                       use_ct_mark, flow_table);
          }
      }
- add_lb_ct_snat_hairpin_flows(lb, id, lb_proto, flow_table);
-
-    ovn_controller_lb_destroy(lb);
-    sset_destroy(&template_vars_ref);
+    add_lb_ct_snat_hairpin_flows(lb, id, flow_table);
  }
/* Adds OpenFlow flows to flow tables for each Load balancer VIPs and
   * backends to handle the load balanced hairpin traffic. */
  static void
-add_lb_hairpin_flows(struct objdep_mgr *mgr,
-                     const struct sbrec_load_balancer_table *lb_table,
-                     const struct hmap *local_datapaths,
-                     const struct smap *template_vars,
+add_lb_hairpin_flows(const struct hmap *local_lbs,
                       bool use_ct_mark,
                       struct ovn_desired_flow_table *flow_table,
                       struct simap *ids,
                       struct id_pool *pool)
  {
      uint32_t id;
-    const struct sbrec_load_balancer *lb;
-    SBREC_LOAD_BALANCER_TABLE_FOR_EACH (lb, lb_table) {
+    const struct ovn_controller_lb *lb;
+    HMAP_FOR_EACH (lb, hmap_node, local_lbs) {
          /* Allocate a unique 32-bit integer to this load-balancer. This will
           * be used as a conjunctive flow id in the OFTABLE_CT_SNAT_HAIRPIN
           * table.
@@ -2152,13 +2054,12 @@ add_lb_hairpin_flows(struct objdep_mgr *mgr,
           * "UINT32_MAX" load-balancers.
           */
- id = simap_get(ids, lb->name);
+        id = simap_get(ids, lb->slb->name);
          if (!id) {
              ovs_assert(id_pool_alloc_id(pool, &id));
-            simap_put(ids, lb->name, id);
+            simap_put(ids, lb->slb->name, id);
          }
-        consider_lb_hairpin_flows(mgr, lb, local_datapaths, template_vars,
-                                  use_ct_mark, flow_table, ids);
+        consider_lb_hairpin_flows(lb, use_ct_mark, flow_table, ids);
      }
  }
@@ -2294,9 +2195,7 @@ lflow_run(struct lflow_ctx_in *l_ctx_in, struct lflow_ctx_out *l_ctx_out)
                         l_ctx_in->static_mac_binding_table,
                         l_ctx_in->local_datapaths,
                         l_ctx_out->flow_table);
-    add_lb_hairpin_flows(l_ctx_out->lb_deps_mgr, l_ctx_in->lb_table,
-                         l_ctx_in->local_datapaths,
-                         l_ctx_in->template_vars,
+    add_lb_hairpin_flows(l_ctx_in->local_lbs,
                           l_ctx_in->lb_hairpin_use_ct_mark,
                           l_ctx_out->flow_table,
                           l_ctx_out->hairpin_lb_ids,
@@ -2333,8 +2232,6 @@ lflow_destroy(void)
bool
  lflow_add_flows_for_datapath(const struct sbrec_datapath_binding *dp,
-                             const struct sbrec_load_balancer **dp_lbs,
-                             size_t n_dp_lbs,
                               struct lflow_ctx_in *l_ctx_in,
                               struct lflow_ctx_out *l_ctx_out)
  {
@@ -2424,17 +2321,6 @@ lflow_add_flows_for_datapath(const struct 
sbrec_datapath_binding *dp,
      }
      sbrec_static_mac_binding_index_destroy_row(smb_index_row);
- /* Add load balancer hairpin flows if the datapath has any load balancers
-     * associated. */
-    for (size_t i = 0; i < n_dp_lbs; i++) {
-        consider_lb_hairpin_flows(l_ctx_out->lb_deps_mgr, dp_lbs[i],
-                                  l_ctx_in->local_datapaths,
-                                  l_ctx_in->template_vars,
-                                  l_ctx_in->lb_hairpin_use_ct_mark,
-                                  l_ctx_out->flow_table,
-                                  l_ctx_out->hairpin_lb_ids);
-    }
-
      return handled;
  }
@@ -2529,52 +2415,58 @@ lflow_handle_changed_mc_groups(struct lflow_ctx_in *l_ctx_in, bool
  lflow_handle_changed_lbs(struct lflow_ctx_in *l_ctx_in,
-                         struct lflow_ctx_out *l_ctx_out)
+                         struct lflow_ctx_out *l_ctx_out,
+                         const struct uuidset *deleted_lbs,
+                         const struct uuidset *updated_lbs,
+                         const struct uuidset *new_lbs,
+                         const struct hmap *old_lbs)
  {
-    const struct sbrec_load_balancer *lb;
+    const struct ovn_controller_lb *lb;
+
      struct id_pool *pool = l_ctx_out->hairpin_id_pool;
      struct simap *ids = l_ctx_out->hairpin_lb_ids;
- SBREC_LOAD_BALANCER_TABLE_FOR_EACH_TRACKED (lb, l_ctx_in->lb_table) {
-        if (sbrec_load_balancer_is_deleted(lb)) {
-            VLOG_DBG("Remove hairpin flows for deleted load balancer "UUID_FMT,
-                     UUID_ARGS(&lb->header_.uuid));
-            ofctrl_remove_flows(l_ctx_out->flow_table, &lb->header_.uuid);
-            id_pool_free_id(pool, simap_get(ids, lb->name));
-            simap_find_and_delete(ids, lb->name);
-        }
+    struct uuidset_node *uuid_node;
+    UUIDSET_FOR_EACH (uuid_node, deleted_lbs) {
+        lb = ovn_controller_lb_find(old_lbs, &uuid_node->uuid);
+
+        VLOG_DBG("Remove hairpin flows for deleted load balancer "UUID_FMT,
+                 UUID_ARGS(&uuid_node->uuid));
+        ofctrl_remove_flows(l_ctx_out->flow_table, &uuid_node->uuid);
+        id_pool_free_id(pool, simap_get(ids, lb->slb->name));
+        simap_find_and_delete(ids, lb->slb->name);
      }
- SBREC_LOAD_BALANCER_TABLE_FOR_EACH_TRACKED (lb, l_ctx_in->lb_table) {
-        if (sbrec_load_balancer_is_deleted(lb)) {
-            continue;
-        }
+    UUIDSET_FOR_EACH (uuid_node, updated_lbs) {
+        lb = ovn_controller_lb_find(l_ctx_in->local_lbs, &uuid_node->uuid);
- if (!sbrec_load_balancer_is_new(lb)) {
-            VLOG_DBG("Remove hairpin flows for updated load balancer "UUID_FMT,
-                     UUID_ARGS(&lb->header_.uuid));
-            ofctrl_remove_flows(l_ctx_out->flow_table, &lb->header_.uuid);
-        } else {
-            /* Allocate a unique 32-bit integer to this load-balancer. This
-             * will be used as a conjunctive flow id in the
-             * OFTABLE_CT_SNAT_HAIRPIN table.
-             *
-             * If we are unable to allocate a unique ID then we have run out of
-             * ids. As this is unrecoverable then we abort. However, this is
-             * unlikely to happen as it would be mean that we have created
-             * "UINT32_MAX" load-balancers.
-             */
-            uint32_t id;
-            ovs_assert(id_pool_alloc_id(pool, &id));
-            simap_put(ids, lb->name, id);
-        }
+        VLOG_DBG("Remove and add hairpin flows for updated load balancer "
+                  UUID_FMT, UUID_ARGS(&uuid_node->uuid));
+        ofctrl_remove_flows(l_ctx_out->flow_table, &uuid_node->uuid);
+        consider_lb_hairpin_flows(lb, l_ctx_in->lb_hairpin_use_ct_mark,
+                                  l_ctx_out->flow_table,
+                                  l_ctx_out->hairpin_lb_ids);
+    }
+
+    UUIDSET_FOR_EACH (uuid_node, new_lbs) {
+        lb = ovn_controller_lb_find(l_ctx_in->local_lbs, &uuid_node->uuid);
+
+        /* Allocate a unique 32-bit integer to this load-balancer. This
+         * will be used as a conjunctive flow id in the
+         * OFTABLE_CT_SNAT_HAIRPIN table.
+         *
+         * If we are unable to allocate a unique ID then we have run out of
+         * ids. As this is unrecoverable then we abort. However, this is
+         * unlikely to happen as it would be mean that we have created
+         * "UINT32_MAX" load-balancers.
+         */
+        uint32_t id;
+        ovs_assert(id_pool_alloc_id(pool, &id));
+        simap_put(ids, lb->slb->name, id);
VLOG_DBG("Add load balancer hairpin flows for "UUID_FMT,
-                 UUID_ARGS(&lb->header_.uuid));
-        consider_lb_hairpin_flows(l_ctx_out->lb_deps_mgr, lb,
-                                  l_ctx_in->local_datapaths,
-                                  l_ctx_in->template_vars,
-                                  l_ctx_in->lb_hairpin_use_ct_mark,
+                 UUID_ARGS(&uuid_node->uuid));
+        consider_lb_hairpin_flows(lb, l_ctx_in->lb_hairpin_use_ct_mark,
                                    l_ctx_out->flow_table,
                                    l_ctx_out->hairpin_lb_ids);
      }
diff --git a/controller/lflow.h b/controller/lflow.h
index 9bb61c039..44e534696 100644
--- a/controller/lflow.h
+++ b/controller/lflow.h
@@ -101,7 +101,6 @@ struct lflow_ctx_in {
      const struct sbrec_multicast_group_table *mc_group_table;
      const struct sbrec_fdb_table *fdb_table;
      const struct sbrec_chassis *chassis;
-    const struct sbrec_load_balancer_table *lb_table;
      const struct sbrec_static_mac_binding_table *static_mac_binding_table;
      const struct hmap *local_datapaths;
      const struct shash *addr_sets;
@@ -116,6 +115,7 @@ struct lflow_ctx_in {
      const struct controller_event_options *controller_event_opts;
      const struct smap *template_vars;
      const struct flow_collector_ids *collector_ids;
+    const struct hmap *local_lbs;
      bool lb_hairpin_use_ct_mark;
  };
@@ -161,13 +161,16 @@ void lflow_handle_changed_static_mac_bindings(
      const struct sbrec_static_mac_binding_table *smb_table,
      const struct hmap *local_datapaths,
      struct ovn_desired_flow_table *);
-bool lflow_handle_changed_lbs(struct lflow_ctx_in *, struct lflow_ctx_out *);
+bool lflow_handle_changed_lbs(struct lflow_ctx_in *l_ctx_in,
+                              struct lflow_ctx_out *l_ctx_out,
+                              const struct uuidset *deleted_lbs,
+                              const struct uuidset *updated_lbs,
+                              const struct uuidset *new_lbs,
+                              const struct hmap *old_lbs);
  bool lflow_handle_changed_fdbs(struct lflow_ctx_in *, struct lflow_ctx_out *);
  void lflow_destroy(void);
bool lflow_add_flows_for_datapath(const struct sbrec_datapath_binding *,
-                                  const struct sbrec_load_balancer **dp_lbs,
-                                  size_t n_dp_lbs,
                                    struct lflow_ctx_in *,
                                    struct lflow_ctx_out *);
  bool lflow_handle_flows_for_lport(const struct sbrec_port_binding *,
@@ -178,7 +181,4 @@ bool lflow_handle_changed_mc_groups(struct lflow_ctx_in *,
  bool lflow_handle_changed_port_bindings(struct lflow_ctx_in *,
                                          struct lflow_ctx_out *);
-bool lb_handle_changed_ref(enum objdep_type type, const char *res_name,
-                           struct ovs_list *objs_todo,
-                           const void *in_arg, void *out_arg);
  #endif /* controller/lflow.h */
diff --git a/controller/local_data.c b/controller/local_data.c
index 035f10fff..abd372172 100644
--- a/controller/local_data.c
+++ b/controller/local_data.c
@@ -645,3 +645,29 @@ datapath_is_transit_switch(const struct 
sbrec_datapath_binding *ldp)
  {
      return smap_get(&ldp->external_ids, "interconn-ts") != NULL;
  }
+
+bool
+lb_is_local(const struct sbrec_load_balancer *sbrec_lb,
+            const struct hmap *local_datapaths)
+{
+    /* Check if the lb is local or not.  It is enough to find one datapath
+     * in "local_datapaths" to consider the LB to be local. */
+    size_t i;
+    for (i = 0; i < sbrec_lb->n_datapaths; i++) {
+        if (get_local_datapath(local_datapaths,
+                               sbrec_lb->datapaths[i]->tunnel_key)) {
+            return true;
+        }
+    }
+
+    struct sbrec_logical_dp_group *dp_group = sbrec_lb->datapath_group;
+
+    for (i = 0; dp_group && i < dp_group->n_datapaths; i++) {
+        if (get_local_datapath(local_datapaths,
+                               dp_group->datapaths[i]->tunnel_key)) {
+            return true;
+        }
+    }
+
+    return false;
+}
diff --git a/controller/local_data.h b/controller/local_data.h
index b5429eb58..748f009aa 100644
--- a/controller/local_data.h
+++ b/controller/local_data.h
@@ -30,6 +30,7 @@ struct sbrec_chassis;
  struct ovsdb_idl_index;
  struct ovsrec_bridge;
  struct ovsrec_interface_table;
+struct sbrec_load_balancer;
/* A logical datapath that has some relevance to this hypervisor. A logical
   * datapath D is relevant to hypervisor H if:
@@ -161,5 +162,7 @@ void add_local_datapath_multichassis_port(struct 
local_datapath *ld,
                                            const void *data);
  void remove_local_datapath_multichassis_port(struct local_datapath *ld,
                                               char *logical_port);
+bool lb_is_local(const struct sbrec_load_balancer *sbrec_lb,
+                 const struct hmap *local_datapaths);
#endif /* controller/local_data.h */
diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c
index 6ea96e2dd..d96f16cc2 100644
--- a/controller/ovn-controller.c
+++ b/controller/ovn-controller.c
@@ -38,6 +38,7 @@
  #include "if-status.h"
  #include "ip-mcast.h"
  #include "openvswitch/hmap.h"
+#include "lb.h"
  #include "lflow.h"
  #include "lflow-cache.h"
  #include "lflow-conj-ids.h"
@@ -2661,6 +2662,377 @@ load_balancers_by_dp_cleanup(struct hmap *lbs)
      free(lbs);
  }
+/* Engine node which is used to handle runtime related data to
+ * load balancers. */
+struct ed_type_lb_data {
+    /* Locally installed 'struct ovn_controller_lb' by UUID. */
+    struct hmap local_lbs;
+    /* Load balancer <-> resource cross reference */
+    struct objdep_mgr deps_mgr;
+    /* Objects processed in the current engine execution.
+     * Cleared by 'en_lb_data_clear_tracked_data' before each engine
+     * execution. */
+    struct uuidset objs_processed;
+
+    bool change_tracked;
+    /* Load balancers removed/updated during last run. */
+    struct hmap old_lbs;
+    /* uuids of load balancers removed during last run. */
+    struct uuidset deleted;
+    /* uuids of load balancers updated during last run. */
+    struct uuidset updated;
+    /* uuids of load balancers added during last run. */
+    struct uuidset new;
+};
+
+struct lb_data_ctx_in {
+    const struct sbrec_load_balancer_table *lb_table;
+    const struct hmap *local_datapths;
+    const struct smap *template_vars;
+};
+
+static void
+lb_data_local_lb_add(struct ed_type_lb_data *lb_data,
+                     const struct sbrec_load_balancer *sbrec_lb,
+                     const struct smap *template_vars, bool tracked)
+{
+    struct sset template_vars_ref = SSET_INITIALIZER(&template_vars_ref);
+    const struct uuid *uuid = &sbrec_lb->header_.uuid;
+
+    struct ovn_controller_lb *lb =
+        ovn_controller_lb_create(sbrec_lb, template_vars, &template_vars_ref);
+    hmap_insert(&lb_data->local_lbs, &lb->hmap_node, uuid_hash(uuid));
+
+    const char *tv_name;
+    SSET_FOR_EACH (tv_name, &template_vars_ref) {
+        objdep_mgr_add(&lb_data->deps_mgr, OBJDEP_TYPE_TEMPLATE, tv_name,
+                       uuid);
+    }
+
+    sset_destroy(&template_vars_ref);
+
+    if (!tracked) {
+        return;
+    }
+
+    if (ovn_controller_lb_find(&lb_data->old_lbs, uuid)) {
+        uuidset_insert(&lb_data->updated, uuid);
+        uuidset_find_and_delete(&lb_data->deleted, uuid);
+    } else {
+        uuidset_insert(&lb_data->new, uuid);
+    }
+}
+
+static void
+lb_data_local_lb_remove(struct ed_type_lb_data *lb_data,
+                        struct ovn_controller_lb *lb, bool tracked)
+{
+    const struct uuid *uuid = &lb->slb->header_.uuid;
+
+    objdep_mgr_remove_obj(&lb_data->deps_mgr, uuid);
+    hmap_remove(&lb_data->local_lbs, &lb->hmap_node);
+
+    if (tracked) {
+        hmap_insert(&lb_data->old_lbs, &lb->hmap_node, uuid_hash(uuid));
+        uuidset_insert(&lb_data->deleted, uuid);
+    } else {
+        ovn_controller_lb_destroy(lb);
+    }
+}
+
+static bool
+lb_data_handle_changed_ref(enum objdep_type type, const char *res_name,
+                           struct ovs_list *objs_todo, const void *in_arg,
+                           void *out_arg)
+{
+    const struct lb_data_ctx_in *ctx_in = in_arg;
+    struct ed_type_lb_data *lb_data = out_arg;
+
+    struct object_to_resources_list_node *resource_lb_uuid;
+    LIST_FOR_EACH_POP (resource_lb_uuid, list_node, objs_todo) {
+        struct uuid *uuid = &resource_lb_uuid->obj_uuid;
+
+        VLOG_DBG("Reprocess LB "UUID_FMT" for resource type: %s, name: %s",
+                 UUID_ARGS(uuid), objdep_type_name(type), res_name);
+
+        struct ovn_controller_lb *lb =
+            ovn_controller_lb_find(&lb_data->local_lbs, uuid);
+        if (!lb) {
+            free(resource_lb_uuid);
+            continue;
+        }
+
+        lb_data_local_lb_remove(lb_data, lb, true);
+
+        const struct sbrec_load_balancer *sbrec_lb =
+            sbrec_load_balancer_table_get_for_uuid(ctx_in->lb_table, uuid);
+        if (!lb_is_local(sbrec_lb, ctx_in->local_datapths)) {
+            free(resource_lb_uuid);
+            continue;
+        }
+
+        lb_data_local_lb_add(lb_data, sbrec_lb, ctx_in->template_vars, true);
+
+        free(resource_lb_uuid);
+    }
+    return true;
+}
+
+static void *
+en_lb_data_init(struct engine_node *node OVS_UNUSED,
+                struct engine_arg *arg OVS_UNUSED)
+{
+    struct ed_type_lb_data *lb_data = xzalloc(sizeof *lb_data);
+
+    hmap_init(&lb_data->local_lbs);
+    objdep_mgr_init(&lb_data->deps_mgr);
+    uuidset_init(&lb_data->objs_processed);
+    lb_data->change_tracked = false;
+    hmap_init(&lb_data->old_lbs);
+    uuidset_init(&lb_data->deleted);
+    uuidset_init(&lb_data->updated);
+    uuidset_init(&lb_data->new);
+
+    return lb_data;
+}
+
+static void
+en_lb_data_run(struct engine_node *node, void *data)
+{
+    struct ed_type_lb_data *lb_data = data;
+    struct ed_type_runtime_data *rt_data =
+        engine_get_input_data("runtime_data", node);
+    struct ed_type_template_vars *tv_data =
+        engine_get_input_data("template_vars", node);
+    const struct sbrec_load_balancer_table *lb_table =
+        EN_OVSDB_GET(engine_get_input("SB_load_balancer", node));
+
+    struct ovn_controller_lb *lb;
+    HMAP_FOR_EACH_SAFE (lb, hmap_node, &lb_data->local_lbs) {
+        lb_data_local_lb_remove(lb_data, lb, false);
+    }
+
+    const struct sbrec_load_balancer *sbrec_lb;
+    SBREC_LOAD_BALANCER_TABLE_FOR_EACH (sbrec_lb, lb_table) {
+        if (!lb_is_local(sbrec_lb, &rt_data->local_datapaths)) {
+            continue;
+        }
+
+        lb_data_local_lb_add(lb_data, sbrec_lb,
+                             &tv_data->local_templates, false);
+    }
+
+    engine_set_node_state(node, EN_UPDATED);
+}
+
+static bool
+lb_data_sb_load_balancer_handler(struct engine_node *node, void *data)
+{
+    struct ed_type_lb_data *lb_data = data;
+    struct ed_type_runtime_data *rt_data =
+        engine_get_input_data("runtime_data", node);
+    struct ed_type_template_vars *tv_data =
+        engine_get_input_data("template_vars", node);
+    const struct sbrec_load_balancer_table *lb_table =
+        EN_OVSDB_GET(engine_get_input("SB_load_balancer", node));
+
+    const struct sbrec_load_balancer *sbrec_lb;
+    SBREC_LOAD_BALANCER_TABLE_FOR_EACH_TRACKED (sbrec_lb, lb_table) {
+        struct ovn_controller_lb *lb;
+
+        if (!sbrec_load_balancer_is_new(sbrec_lb)) {
+            lb = ovn_controller_lb_find(&lb_data->local_lbs,
+                                        &sbrec_lb->header_.uuid);
+            if (!lb) {
+                continue;
+            }
+
+            lb_data_local_lb_remove(lb_data, lb, true);
+        }
+
+        if (sbrec_load_balancer_is_deleted(sbrec_lb) ||
+            !lb_is_local(sbrec_lb, &rt_data->local_datapaths)) {
+            continue;
+        }
+
+        lb_data_local_lb_add(lb_data, sbrec_lb,
+                             &tv_data->local_templates, true);
+    }
+
+    lb_data->change_tracked = true;
+    if (!uuidset_is_empty(&lb_data->deleted) ||
+        !uuidset_is_empty(&lb_data->updated) ||
+        !uuidset_is_empty(&lb_data->new)) {
+        engine_set_node_state(node, EN_UPDATED);
+    }
+
+    return true;
+}
+
+static bool
+lb_data_template_var_handler(struct engine_node *node, void *data)
+{
+    struct ed_type_lb_data *lb_data = data;
+    struct ed_type_runtime_data *rt_data =
+        engine_get_input_data("runtime_data", node);
+    struct ed_type_template_vars *tv_data =
+        engine_get_input_data("template_vars", node);
+    const struct sbrec_load_balancer_table *lb_table =
+        EN_OVSDB_GET(engine_get_input("SB_load_balancer", node));
+
+    if (!tv_data->change_tracked) {
+        return false;
+    }
+
+    const struct lb_data_ctx_in ctx_in = {
+        .lb_table = lb_table,
+        .local_datapths = &rt_data->local_datapaths,
+        .template_vars = &tv_data->local_templates
+    };
+
+    const char *res_name;
+    bool changed;
+
+    SSET_FOR_EACH (res_name, &tv_data->deleted) {
+        if (!objdep_mgr_handle_change(&lb_data->deps_mgr,
+                                      OBJDEP_TYPE_TEMPLATE,
+                                      res_name, lb_data_handle_changed_ref,
+                                      &lb_data->objs_processed,
+                                      &ctx_in, lb_data, &changed)) {
+            return false;
+        }
+        if (changed) {
+            engine_set_node_state(node, EN_UPDATED);
+        }
+    }
+    SSET_FOR_EACH (res_name, &tv_data->updated) {
+        if (!objdep_mgr_handle_change(&lb_data->deps_mgr,
+                                      OBJDEP_TYPE_TEMPLATE,
+                                      res_name, lb_data_handle_changed_ref,
+                                      &lb_data->objs_processed,
+                                      &ctx_in, lb_data, &changed)) {
+            return false;
+        }
+        if (changed) {
+            engine_set_node_state(node, EN_UPDATED);
+        }
+    }
+    SSET_FOR_EACH (res_name, &tv_data->new) {
+        if (!objdep_mgr_handle_change(&lb_data->deps_mgr,
+                                      OBJDEP_TYPE_TEMPLATE,
+                                      res_name, lb_data_handle_changed_ref,
+                                      &lb_data->objs_processed,
+                                      &ctx_in, lb_data, &changed)) {
+            return false;
+        }
+        if (changed) {
+            engine_set_node_state(node, EN_UPDATED);
+        }
+    }
+
+    lb_data->change_tracked = true;
+
+    return true;
+}
+
+static bool
+lb_data_runtime_data_handler(struct engine_node *node, void *data OVS_UNUSED)
+{
+    struct ed_type_lb_data *lb_data = data;
+    struct ed_type_runtime_data *rt_data =
+        engine_get_input_data("runtime_data", node);
+    struct ed_type_template_vars *tv_data =
+        engine_get_input_data("template_vars", node);
+    const struct sbrec_load_balancer_table *lb_table =
+        EN_OVSDB_GET(engine_get_input("SB_load_balancer", node));
+
+    /* There are no tracked data. Fall back to full recompute of
+     * lb_ct_tuple. */
+    if (!rt_data->tracked) {
+        return false;
+    }
+
+    struct hmap *tracked_dp_bindings = &rt_data->tracked_dp_bindings;
+    if (hmap_is_empty(tracked_dp_bindings)) {
+        return true;
+    }
+
+    struct hmap *lbs = NULL;
+
+    struct tracked_datapath *tdp;
+    HMAP_FOR_EACH (tdp, node, tracked_dp_bindings) {
+        if (tdp->tracked_type != TRACKED_RESOURCE_NEW) {
+            continue;
+        }
+
+        if (!lbs) {
+            lbs = load_balancers_by_dp_init(&rt_data->local_datapaths,
+                                            lb_table);
+        }
+
+        struct load_balancers_by_dp *lbs_by_dp =
+            load_balancers_by_dp_find(lbs, tdp->dp);
+        if (!lbs_by_dp) {
+            continue;
+        }
+
+        for (size_t i = 0; i < lbs_by_dp->n_dp_lbs; i++) {
+            const struct sbrec_load_balancer *sbrec_lb = lbs_by_dp->dp_lbs[i];
+            struct ovn_controller_lb *lb =
+                ovn_controller_lb_find(&lb_data->local_lbs,
+                                       &sbrec_lb->header_.uuid);
+
+            if (!lb && lb_is_local(sbrec_lb, &rt_data->local_datapaths)) {
+                lb_data_local_lb_add(lb_data, sbrec_lb,
+                                     &tv_data->local_templates, true);
+            }
+        }
+    }
+
+    load_balancers_by_dp_cleanup(lbs);
+
+    lb_data->change_tracked = true;
+    if (!uuidset_is_empty(&lb_data->deleted) ||
+        !uuidset_is_empty(&lb_data->updated) ||
+        !uuidset_is_empty(&lb_data->new)) {
+        engine_set_node_state(node, EN_UPDATED);
+    }
+
+    return true;
+}
+
+static void
+en_lb_data_clear_tracked_data(void *data)
+{
+    struct ed_type_lb_data *lb_data = data;
+
+    struct ovn_controller_lb *lb;
+    HMAP_FOR_EACH_POP (lb, hmap_node, &lb_data->old_lbs) {
+        ovn_controller_lb_destroy(lb);
+    }
+
+    hmap_clear(&lb_data->old_lbs);
+    uuidset_clear(&lb_data->objs_processed);
+    uuidset_clear(&lb_data->deleted);
+    uuidset_clear(&lb_data->updated);
+    uuidset_clear(&lb_data->new);
+    lb_data->change_tracked = false;
+}
+
+static void
+en_lb_data_cleanup(void *data)
+{
+    struct ed_type_lb_data *lb_data = data;
+
+    ovn_controller_lbs_destroy(&lb_data->local_lbs);
+    objdep_mgr_destroy(&lb_data->deps_mgr);
+    uuidset_destroy(&lb_data->objs_processed);
+    ovn_controller_lbs_destroy(&lb_data->old_lbs);
+    uuidset_destroy(&lb_data->deleted);
+    uuidset_destroy(&lb_data->updated);
+    uuidset_destroy(&lb_data->new);
+}
+
  /* Engine node which is used to handle the Non VIF data like
   *   - OVS patch ports
   *   - Tunnel ports and the related chassis information.
@@ -2858,12 +3230,10 @@ struct ed_type_lflow_output {
      struct ovn_extend_table meter_table;
      /* lflow <-> resource cross reference */
      struct objdep_mgr lflow_deps_mgr;;
-    /* load balancer <-> resource cross reference */
-    struct objdep_mgr lb_deps_mgr;
      /* conjunciton ID usage information of lflows */
      struct conj_ids conj_ids;
- /* objects (lflows and lbs) processed in the current engine execution.
+    /* objects (lflows) processed in the current engine execution.
       * Cleared by en_lflow_output_clear_tracked_data before each engine
       * execution. */
      struct uuidset objs_processed;
@@ -2941,9 +3311,6 @@ init_lflow_ctx(struct engine_node *node,
      const struct sbrec_multicast_group_table *multicast_group_table =
          EN_OVSDB_GET(engine_get_input("SB_multicast_group", node));
- const struct sbrec_load_balancer_table *lb_table =
-        EN_OVSDB_GET(engine_get_input("SB_load_balancer", node));
-
      const struct sbrec_fdb_table *fdb_table =
          EN_OVSDB_GET(engine_get_input("SB_fdb", node));
@@ -2988,6 +3355,9 @@ init_lflow_ctx(struct engine_node *node,
      struct ed_type_template_vars *template_vars =
          engine_get_input_data("template_vars", node);
+ struct ed_type_lb_data *lb_data =
+        engine_get_input_data("lb_data", node);
+
      l_ctx_in->sbrec_multicast_group_by_name_datapath =
          sbrec_mc_group_by_name_dp;
      l_ctx_in->sbrec_logical_flow_by_logical_datapath =
@@ -3006,7 +3376,6 @@ init_lflow_ctx(struct engine_node *node,
      l_ctx_in->mc_group_table = multicast_group_table;
      l_ctx_in->fdb_table = fdb_table,
      l_ctx_in->chassis = chassis;
-    l_ctx_in->lb_table = lb_table;
      l_ctx_in->static_mac_binding_table = smb_table;
      l_ctx_in->local_datapaths = &rt_data->local_datapaths;
      l_ctx_in->addr_sets = addr_sets;
@@ -3022,12 +3391,12 @@ init_lflow_ctx(struct engine_node *node,
      l_ctx_in->controller_event_opts = &fo->controller_event_opts;
      l_ctx_in->template_vars = &template_vars->local_templates;
      l_ctx_in->collector_ids = &fo->collector_ids;
+    l_ctx_in->local_lbs = &lb_data->local_lbs;
l_ctx_out->flow_table = &fo->flow_table;
      l_ctx_out->group_table = &fo->group_table;
      l_ctx_out->meter_table = &fo->meter_table;
      l_ctx_out->lflow_deps_mgr = &fo->lflow_deps_mgr;
-    l_ctx_out->lb_deps_mgr = &fo->lb_deps_mgr;
      l_ctx_out->conj_ids = &fo->conj_ids;
      l_ctx_out->objs_processed = &fo->objs_processed;
      l_ctx_out->lflow_cache = fo->pd.lflow_cache;
@@ -3044,7 +3413,6 @@ en_lflow_output_init(struct engine_node *node OVS_UNUSED,
      ovn_extend_table_init(&data->group_table);
      ovn_extend_table_init(&data->meter_table);
      objdep_mgr_init(&data->lflow_deps_mgr);
-    objdep_mgr_init(&data->lb_deps_mgr);
      lflow_conj_ids_init(&data->conj_ids);
      uuidset_init(&data->objs_processed);
      simap_init(&data->hd.ids);
@@ -3070,7 +3438,6 @@ en_lflow_output_cleanup(void *data)
      ovn_extend_table_destroy(&flow_output_data->group_table);
      ovn_extend_table_destroy(&flow_output_data->meter_table);
      objdep_mgr_destroy(&flow_output_data->lflow_deps_mgr);
-    objdep_mgr_destroy(&flow_output_data->lb_deps_mgr);
      lflow_conj_ids_destroy(&flow_output_data->conj_ids);
      uuidset_destroy(&flow_output_data->objs_processed);
      lflow_cache_destroy(flow_output_data->pd.lflow_cache);
@@ -3108,7 +3475,6 @@ en_lflow_output_run(struct engine_node *node, void *data)
      struct ovn_extend_table *group_table = &fo->group_table;
      struct ovn_extend_table *meter_table = &fo->meter_table;
      struct objdep_mgr *lflow_deps_mgr = &fo->lflow_deps_mgr;
-    struct objdep_mgr *lb_deps_mgr = &fo->lb_deps_mgr;
static bool first_run = true;
      if (first_run) {
@@ -3118,7 +3484,6 @@ en_lflow_output_run(struct engine_node *node, void *data)
          ovn_extend_table_clear(group_table, false /* desired */);
          ovn_extend_table_clear(meter_table, false /* desired */);
          objdep_mgr_clear(lflow_deps_mgr);
-        objdep_mgr_clear(lb_deps_mgr);
          lflow_conj_ids_clear(&fo->conj_ids);
      }
@@ -3481,16 +3846,6 @@ lflow_output_template_vars_handler(struct engine_node *node, void *data)
          if (changed) {
              engine_set_node_state(node, EN_UPDATED);
          }
-        if (!objdep_mgr_handle_change(l_ctx_out.lb_deps_mgr,
-                                      OBJDEP_TYPE_TEMPLATE,
-                                      res_name, lb_handle_changed_ref,
-                                      l_ctx_out.objs_processed,
-                                      &l_ctx_in, &l_ctx_out, &changed)) {
-            return false;
-        }
-        if (changed) {
-            engine_set_node_state(node, EN_UPDATED);
-        }
      }
      SSET_FOR_EACH (res_name, &tv_data->updated) {
          if (!objdep_mgr_handle_change(l_ctx_out.lflow_deps_mgr,
@@ -3503,16 +3858,6 @@ lflow_output_template_vars_handler(struct engine_node 
*node, void *data)
          if (changed) {
              engine_set_node_state(node, EN_UPDATED);
          }
-        if (!objdep_mgr_handle_change(l_ctx_out.lb_deps_mgr,
-                                      OBJDEP_TYPE_TEMPLATE,
-                                      res_name, lb_handle_changed_ref,
-                                      l_ctx_out.objs_processed,
-                                      &l_ctx_in, &l_ctx_out, &changed)) {
-            return false;
-        }
-        if (changed) {
-            engine_set_node_state(node, EN_UPDATED);
-        }
      }
      SSET_FOR_EACH (res_name, &tv_data->new) {
          if (!objdep_mgr_handle_change(l_ctx_out.lflow_deps_mgr,
@@ -3525,16 +3870,6 @@ lflow_output_template_vars_handler(struct engine_node 
*node, void *data)
          if (changed) {
              engine_set_node_state(node, EN_UPDATED);
          }
-        if (!objdep_mgr_handle_change(l_ctx_out.lb_deps_mgr,
-                                      OBJDEP_TYPE_TEMPLATE,
-                                      res_name, lb_handle_changed_ref,
-                                      l_ctx_out.objs_processed,
-                                      &l_ctx_in, &l_ctx_out, &changed)) {
-            return false;
-        }
-        if (changed) {
-            engine_set_node_state(node, EN_UPDATED);
-        }
      }
return true;
@@ -3564,23 +3899,13 @@ lflow_output_runtime_data_handler(struct engine_node 
*node,
      struct lflow_ctx_in l_ctx_in;
      struct lflow_ctx_out l_ctx_out;
      struct ed_type_lflow_output *fo = data;
-    struct hmap *lbs = NULL;
      init_lflow_ctx(node, fo, &l_ctx_in, &l_ctx_out);
struct tracked_datapath *tdp;
      HMAP_FOR_EACH (tdp, node, tracked_dp_bindings) {
          if (tdp->tracked_type == TRACKED_RESOURCE_NEW) {
-            if (!lbs) {
-                lbs = load_balancers_by_dp_init(&rt_data->local_datapaths,
-                                                l_ctx_in.lb_table);
-            }
-
-            struct load_balancers_by_dp *lbs_by_dp =
-                load_balancers_by_dp_find(lbs, tdp->dp);
-            if (!lflow_add_flows_for_datapath(
-                    tdp->dp, lbs_by_dp ? lbs_by_dp->dp_lbs : NULL,
-                    lbs_by_dp ? lbs_by_dp->n_dp_lbs : 0,
-                    &l_ctx_in, &l_ctx_out)) {
+            if (!lflow_add_flows_for_datapath(tdp->dp, &l_ctx_in,
+                                              &l_ctx_out)) {
                  return false;
              }
          }
@@ -3594,20 +3919,29 @@ lflow_output_runtime_data_handler(struct engine_node 
*node,
          }
      }
- load_balancers_by_dp_cleanup(lbs);
      engine_set_node_state(node, EN_UPDATED);
      return true;
  }
static bool
-lflow_output_sb_load_balancer_handler(struct engine_node *node, void *data)
+lflow_output_lb_data_handler(struct engine_node *node, void *data)
  {
      struct ed_type_lflow_output *fo = data;
+    struct ed_type_lb_data *lb_data = engine_get_input_data("lb_data", node);
+
+    if (!lb_data->change_tracked) {
+        return false;
+    }
+
      struct lflow_ctx_in l_ctx_in;
      struct lflow_ctx_out l_ctx_out;
      init_lflow_ctx(node, fo, &l_ctx_in, &l_ctx_out);
- bool handled = lflow_handle_changed_lbs(&l_ctx_in, &l_ctx_out);
+    bool handled = lflow_handle_changed_lbs(&l_ctx_in, &l_ctx_out,
+                                            &lb_data->deleted,
+                                            &lb_data->updated,
+                                            &lb_data->new,
+                                            &lb_data->old_lbs);
engine_set_node_state(node, EN_UPDATED);
      return handled;
@@ -4243,6 +4577,7 @@ main(int argc, char *argv[])
      ENGINE_NODE_WITH_CLEAR_TRACK_DATA(port_groups, "port_groups");
      ENGINE_NODE(northd_options, "northd_options");
      ENGINE_NODE(dhcp_options, "dhcp_options");
+    ENGINE_NODE_WITH_CLEAR_TRACK_DATA(lb_data, "lb_data");
#define SB_NODE(NAME, NAME_STR) ENGINE_NODE_SB(NAME, NAME_STR);
      SB_NODES
@@ -4258,6 +4593,13 @@ main(int argc, char *argv[])
      engine_add_input(&en_template_vars, &en_sb_chassis_template_var,
                       template_vars_sb_chassis_template_var_handler);
+ engine_add_input(&en_lb_data, &en_sb_load_balancer,
+                     lb_data_sb_load_balancer_handler);
+    engine_add_input(&en_lb_data, &en_template_vars,
+                     lb_data_template_var_handler);
+    engine_add_input(&en_lb_data, &en_runtime_data,
+                     lb_data_runtime_data_handler);
+
      engine_add_input(&en_addr_sets, &en_sb_address_set,
                       addr_sets_sb_address_set_handler);
      engine_add_input(&en_port_groups, &en_sb_port_group,
@@ -4362,8 +4704,8 @@ main(int argc, char *argv[])
      engine_add_input(&en_lflow_output, &en_sb_logical_dp_group,
                       engine_noop_handler);
      engine_add_input(&en_lflow_output, &en_sb_dns, NULL);
-    engine_add_input(&en_lflow_output, &en_sb_load_balancer,
-                     lflow_output_sb_load_balancer_handler);
+    engine_add_input(&en_lflow_output, &en_lb_data,
+                     lflow_output_lb_data_handler);
      engine_add_input(&en_lflow_output, &en_sb_fdb,
                       lflow_output_sb_fdb_handler);
      engine_add_input(&en_lflow_output, &en_sb_meter,
diff --git a/lib/lb.c b/lib/lb.c
index e0e97572f..de25c68c0 100644
--- a/lib/lb.c
+++ b/lib/lb.c
@@ -741,6 +741,15 @@ ovn_controller_lb_create(const struct sbrec_load_balancer 
*sbrec_lb,
          lex_str_free(&value_s);
      }
+ lb->proto = IPPROTO_TCP;
+    if (sbrec_lb->protocol && sbrec_lb->protocol[0]) {
+        if (!strcmp(sbrec_lb->protocol, "udp")) {
+            lb->proto = IPPROTO_UDP;
+        } else if (!strcmp(sbrec_lb->protocol, "sctp")) {
+            lb->proto = IPPROTO_SCTP;
+        }
+    }
+
      /* It's possible that parsing VIPs fails.  Update the lb->n_vips to the
       * correct value.
       */
@@ -764,3 +773,28 @@ ovn_controller_lb_destroy(struct ovn_controller_lb *lb)
      destroy_lport_addresses(&lb->hairpin_snat_ips);
      free(lb);
  }
+
+void
+ovn_controller_lbs_destroy(struct hmap *ovn_controller_lbs)
+{
+    struct ovn_controller_lb *lb;
+    HMAP_FOR_EACH_POP (lb, hmap_node, ovn_controller_lbs) {
+        ovn_controller_lb_destroy(lb);
+    }
+
+    hmap_destroy(ovn_controller_lbs);
+}
+
+struct ovn_controller_lb *
+ovn_controller_lb_find(const struct hmap *ovn_controller_lbs,
+                       const struct uuid *uuid)
+{
+    struct ovn_controller_lb *lb;
+    size_t hash = uuid_hash(uuid);
+    HMAP_FOR_EACH_WITH_HASH (lb, hmap_node, hash, ovn_controller_lbs) {
+        if (uuid_equals(&lb->slb->header_.uuid, uuid)) {
+            return lb;
+        }
+    }
+    return NULL;
+}
diff --git a/lib/lb.h b/lib/lb.h
index 7594553d5..f32891ea6 100644
--- a/lib/lb.h
+++ b/lib/lb.h
@@ -172,8 +172,12 @@ ovn_lb_group_add_lr(struct ovn_lb_group *lb_group, struct 
ovn_datapath *lr)
  }
struct ovn_controller_lb {
+    struct hmap_node hmap_node;
+
      const struct sbrec_load_balancer *slb; /* May be NULL. */
+ uint8_t proto;
+
      struct ovn_lb_vip *vips;
      size_t n_vips;
      bool hairpin_orig_tuple; /* True if ovn-northd stores the original
@@ -191,6 +195,10 @@ struct ovn_controller_lb *ovn_controller_lb_create(
      const struct smap *template_vars,
      struct sset *template_vars_ref);
  void ovn_controller_lb_destroy(struct ovn_controller_lb *);
+void ovn_controller_lbs_destroy(struct hmap *ovn_controller_lbs);
+struct ovn_controller_lb *ovn_controller_lb_find(
+    const struct hmap *ovn_controller_lbs,
+    const struct uuid *uuid);
char *ovn_lb_vip_init(struct ovn_lb_vip *lb_vip, const char *lb_key,
                        const char *lb_value, bool template, int 
address_family);

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

Reply via email to