diff mbox series

[ovs-dev,v6,03/16] northd: Add initial I-P for load balancer and load balancer groups

Message ID 20230818085730.1030949-1-numans@ovn.org
State Accepted
Headers show
Series northd: I-P for load balancer and lb groups | expand

Checks

Context Check Description
ovsrobot/apply-robot success apply and check: success
ovsrobot/github-robot-_ovn-kubernetes fail github build: failed

Commit Message

Numan Siddique Aug. 18, 2023, 8:57 a.m. UTC
From: Numan Siddique <numans@ovn.org>

Any changes to load balancers and load balancer groups
are handled incrementally in the newly added 'lb_data'
engine node.  'lb_data' is input to 'northd' node
and the handler - northd_lb_data_handler in 'northd'
node handles the changes.

If a load balancer or load balancer group is associated to
a logical switch or router then 'northd' node falls back
to full recompute.  Upcoming patch will handle this scenario.

Signed-off-by: Numan Siddique <numans@ovn.org>
---
 lib/lb.c                 |  82 +++++--
 lib/lb.h                 |   9 +
 northd/en-lb-data.c      | 273 ++++++++++++++++++++++-
 northd/en-lb-data.h      |  50 +++++
 northd/en-northd.c       |  41 ++++
 northd/en-northd.h       |   1 +
 northd/inc-proc-northd.c |  12 +-
 northd/northd.c          |  59 +++++
 northd/northd.h          |   7 +
 tests/ovn-northd.at      | 457 +++++++++++++++++++++++++++++++++++++++
 10 files changed, 960 insertions(+), 31 deletions(-)

Comments

Han Zhou Aug. 30, 2023, 6:27 a.m. UTC | #1
On Fri, Aug 18, 2023 at 1:57 AM <numans@ovn.org> wrote:
>
> From: Numan Siddique <numans@ovn.org>
>
> Any changes to load balancers and load balancer groups
> are handled incrementally in the newly added 'lb_data'
> engine node.  'lb_data' is input to 'northd' node
> and the handler - northd_lb_data_handler in 'northd'
> node handles the changes.
>
> If a load balancer or load balancer group is associated to
> a logical switch or router then 'northd' node falls back
> to full recompute.  Upcoming patch will handle this scenario.
>
> Signed-off-by: Numan Siddique <numans@ovn.org>
> ---
>  lib/lb.c                 |  82 +++++--
>  lib/lb.h                 |   9 +
>  northd/en-lb-data.c      | 273 ++++++++++++++++++++++-
>  northd/en-lb-data.h      |  50 +++++
>  northd/en-northd.c       |  41 ++++
>  northd/en-northd.h       |   1 +
>  northd/inc-proc-northd.c |  12 +-
>  northd/northd.c          |  59 +++++
>  northd/northd.h          |   7 +
>  tests/ovn-northd.at      | 457 +++++++++++++++++++++++++++++++++++++++
>  10 files changed, 960 insertions(+), 31 deletions(-)
>
> diff --git a/lib/lb.c b/lib/lb.c
> index e874680a3f..6fd67e2218 100644
> --- a/lib/lb.c
> +++ b/lib/lb.c
> @@ -606,13 +606,13 @@ ovn_lb_get_health_check(const struct
nbrec_load_balancer *nbrec_lb,
>      return NULL;
>  }
>
> -struct ovn_northd_lb *
> -ovn_northd_lb_create(const struct nbrec_load_balancer *nbrec_lb)
> +static void
> +ovn_northd_lb_init(struct ovn_northd_lb *lb,
> +                   const struct nbrec_load_balancer *nbrec_lb)
>  {
>      bool template = smap_get_bool(&nbrec_lb->options, "template", false);
>      bool is_udp = nullable_string_is_equal(nbrec_lb->protocol, "udp");
>      bool is_sctp = nullable_string_is_equal(nbrec_lb->protocol, "sctp");
> -    struct ovn_northd_lb *lb = xzalloc(sizeof *lb);
>      int address_family = !strcmp(smap_get_def(&nbrec_lb->options,
>                                                "address-family", "ipv4"),
>                                   "ipv4")
> @@ -668,6 +668,10 @@ ovn_northd_lb_create(const struct
nbrec_load_balancer *nbrec_lb)
>                                                    "reject", false);
>          ovn_northd_lb_vip_init(lb_vip_nb, lb_vip, nbrec_lb,
>                                 node->key, node->value, template);
> +        if (lb_vip_nb->lb_health_check) {
> +            lb->health_checks = true;
> +        }
> +
>          if (IN6_IS_ADDR_V4MAPPED(&lb_vip->vip)) {
>              sset_add(&lb->ips_v4, lb_vip->vip_str);
>          } else {
> @@ -711,6 +715,13 @@ ovn_northd_lb_create(const struct
nbrec_load_balancer *nbrec_lb)
>          ds_chomp(&sel_fields, ',');
>          lb->selection_fields = ds_steal_cstr(&sel_fields);
>      }
> +}
> +
> +struct ovn_northd_lb *
> +ovn_northd_lb_create(const struct nbrec_load_balancer *nbrec_lb)
> +{
> +    struct ovn_northd_lb *lb = xzalloc(sizeof *lb);
> +    ovn_northd_lb_init(lb, nbrec_lb);
>      return lb;
>  }
>
> @@ -736,8 +747,8 @@ ovn_northd_lb_get_vips(const struct ovn_northd_lb *lb)
>      return &lb->nlb->vips;
>  }
>
> -void
> -ovn_northd_lb_destroy(struct ovn_northd_lb *lb)
> +static void
> +ovn_northd_lb_cleanup(struct ovn_northd_lb *lb)
>  {
>      for (size_t i = 0; i < lb->n_vips; i++) {
>          ovn_lb_vip_destroy(&lb->vips[i]);
> @@ -745,24 +756,36 @@ ovn_northd_lb_destroy(struct ovn_northd_lb *lb)
>      }
>      free(lb->vips);
>      free(lb->vips_nb);
> +    lb->vips = NULL;
> +    lb->vips_nb = NULL;
>      smap_destroy(&lb->template_vips);
>      sset_destroy(&lb->ips_v4);
>      sset_destroy(&lb->ips_v6);
>      free(lb->selection_fields);
> +    lb->selection_fields = NULL;
> +    lb->health_checks = false;
> +}
> +
> +void
> +ovn_northd_lb_destroy(struct ovn_northd_lb *lb)
> +{
> +    ovn_northd_lb_cleanup(lb);
>      free(lb);
>  }
>
> -/* Constructs a new 'struct ovn_lb_group' object from the Nb LB Group
record
> - * and an array of 'struct ovn_northd_lb' objects for its associated
> - * load balancers. */
> -struct ovn_lb_group *
> -ovn_lb_group_create(const struct nbrec_load_balancer_group
*nbrec_lb_group,
> -                    const struct hmap *lbs)
> +void
> +ovn_northd_lb_reinit(struct ovn_northd_lb *lb,
> +                     const struct nbrec_load_balancer *nbrec_lb)
>  {
> -    struct ovn_lb_group *lb_group;
> +    ovn_northd_lb_cleanup(lb);
> +    ovn_northd_lb_init(lb, nbrec_lb);
> +}
>
> -    lb_group = xzalloc(sizeof *lb_group);
> -    lb_group->uuid = nbrec_lb_group->header_.uuid;
> +static void
> +ovn_lb_group_init(struct ovn_lb_group *lb_group,
> +                  const struct nbrec_load_balancer_group *nbrec_lb_group,
> +                  const struct hmap *lbs)
> +{
>      lb_group->n_lbs = nbrec_lb_group->n_load_balancer;
>      lb_group->lbs = xmalloc(lb_group->n_lbs * sizeof *lb_group->lbs);
>      lb_group->lb_ips = ovn_lb_ip_set_create();
> @@ -772,10 +795,29 @@ ovn_lb_group_create(const struct
nbrec_load_balancer_group *nbrec_lb_group,
>              &nbrec_lb_group->load_balancer[i]->header_.uuid;
>          lb_group->lbs[i] = ovn_northd_lb_find(lbs, lb_uuid);
>      }
> +}
>
> +/* Constructs a new 'struct ovn_lb_group' object from the Nb LB Group
record
> + * and an array of 'struct ovn_northd_lb' objects for its associated
> + * load balancers. */
> +struct ovn_lb_group *
> +ovn_lb_group_create(const struct nbrec_load_balancer_group
*nbrec_lb_group,
> +                    const struct hmap *lbs)
> +{
> +    struct ovn_lb_group *lb_group = xzalloc(sizeof *lb_group);
> +    lb_group->uuid = nbrec_lb_group->header_.uuid;
> +    ovn_lb_group_init(lb_group, nbrec_lb_group, lbs);
>      return lb_group;
>  }
>
> +static void
> +ovn_lb_group_cleanup(struct ovn_lb_group *lb_group)
> +{
> +    ovn_lb_ip_set_destroy(lb_group->lb_ips);
> +    lb_group->lb_ips = NULL;
> +    free(lb_group->lbs);
> +}
> +
>  void
>  ovn_lb_group_destroy(struct ovn_lb_group *lb_group)
>  {
> @@ -783,11 +825,19 @@ ovn_lb_group_destroy(struct ovn_lb_group *lb_group)
>          return;
>      }
>
> -    ovn_lb_ip_set_destroy(lb_group->lb_ips);
> -    free(lb_group->lbs);
> +    ovn_lb_group_cleanup(lb_group);
>      free(lb_group);
>  }
>
> +void
> +ovn_lb_group_reinit(struct ovn_lb_group *lb_group,
> +                    const struct nbrec_load_balancer_group
*nbrec_lb_group,
> +                    const struct hmap *lbs)
> +{
> +    ovn_lb_group_cleanup(lb_group);
> +    ovn_lb_group_init(lb_group, nbrec_lb_group, lbs);
> +}
> +
>  struct ovn_lb_group *
>  ovn_lb_group_find(const struct hmap *lb_groups, const struct uuid *uuid)
>  {
> diff --git a/lib/lb.h b/lib/lb.h
> index 0339050cba..74905c73b7 100644
> --- a/lib/lb.h
> +++ b/lib/lb.h
> @@ -77,6 +77,9 @@ struct ovn_northd_lb {
>
>      struct sset ips_v4;
>      struct sset ips_v6;
> +
> +    /* Indicates if the load balancer has health checks configured. */
> +    bool health_checks;
>  };
>
>  struct ovn_lb_vip {
> @@ -130,6 +133,8 @@ struct ovn_northd_lb *ovn_northd_lb_find(const struct
hmap *,
>                                           const struct uuid *);
>  const struct smap *ovn_northd_lb_get_vips(const struct ovn_northd_lb *);
>  void ovn_northd_lb_destroy(struct ovn_northd_lb *);
> +void ovn_northd_lb_reinit(struct ovn_northd_lb *,
> +                          const struct nbrec_load_balancer *);
>
>  void build_lrouter_lb_ips(struct ovn_lb_ip_set *,
>                            const struct ovn_northd_lb *);
> @@ -148,6 +153,10 @@ struct ovn_lb_group *ovn_lb_group_create(
>  void ovn_lb_group_destroy(struct ovn_lb_group *lb_group);
>  struct ovn_lb_group *ovn_lb_group_find(const struct hmap *lb_groups,
>                                         const struct uuid *);
> +void ovn_lb_group_reinit(
> +    struct ovn_lb_group *lb_group,
> +    const struct nbrec_load_balancer_group *,
> +    const struct hmap *lbs);
>
>  struct ovn_lb_datapaths {
>      struct hmap_node hmap_node;
> diff --git a/northd/en-lb-data.c b/northd/en-lb-data.c
> index 04d8d3e5d7..328c003675 100644
> --- a/northd/en-lb-data.c
> +++ b/northd/en-lb-data.c
> @@ -19,6 +19,7 @@
>  #include <stdio.h>
>
>  /* OVS includes */
> +#include "include/openvswitch/hmap.h"
>  #include "openvswitch/util.h"
>  #include "openvswitch/vlog.h"
>
> @@ -38,7 +39,27 @@ static void lb_data_destroy(struct ed_type_lb_data *);
>  static void build_lbs(const struct nbrec_load_balancer_table *,
>                        const struct nbrec_load_balancer_group_table *,
>                        struct hmap *lbs, struct hmap *lb_groups);
> +static struct ovn_lb_group *create_lb_group(
> +    const struct nbrec_load_balancer_group *, struct hmap *lbs,
> +    struct hmap *lb_groups);
> +static void destroy_tracked_data(struct ed_type_lb_data *);
> +static void add_crupdated_lb_to_tracked_data(struct ovn_northd_lb *,
> +                                                    struct
tracked_lb_data *,
> +                                                    bool health_checks);
> +static void add_deleted_lb_to_tracked_data(struct ovn_northd_lb *,
> +                                                  struct tracked_lb_data
*,
> +                                                  bool health_checks);
> +static struct crupdated_lb_group *
> +    add_crupdated_lb_group_to_tracked_data(struct ovn_lb_group *,
> +                                           struct tracked_lb_data *);
> +static void add_deleted_lb_group_to_tracked_data(
> +    struct ovn_lb_group *, struct tracked_lb_data *);
>
> +/* 'lb_data' engine node manages the NB load balancers and load balancer
> + * groups.  For each NB LB, it creates 'struct ovn_northd_lb' and
> + * for each NB LB group, it creates 'struct ovn_lb_group' and stores in
> + * the respective hmaps in it data (ed_type_lb_data).
> + */
>  void *
>  en_lb_data_init(struct engine_node *node OVS_UNUSED,
>                         struct engine_arg *arg OVS_UNUSED)
> @@ -60,6 +81,7 @@ en_lb_data_run(struct engine_node *node, void *data)
>      const struct nbrec_load_balancer_group_table *nb_lbg_table =
>          EN_OVSDB_GET(engine_get_input("NB_load_balancer_group", node));
>
> +    lb_data->tracked = false;
>      build_lbs(nb_lb_table, nb_lbg_table, &lb_data->lbs,
&lb_data->lb_groups);
>      engine_set_node_state(node, EN_UPDATED);
>  }
> @@ -71,12 +93,155 @@ en_lb_data_cleanup(void *data)
>      lb_data_destroy(lb_data);
>  }
>
> +void
> +en_lb_data_clear_tracked_data(void *data)
> +{
> +    struct ed_type_lb_data *lb_data = (struct ed_type_lb_data *) data;
> +    destroy_tracked_data(lb_data);
> +}
> +
> +
> +/* Handler functions. */
> +bool
> +lb_data_load_balancer_handler(struct engine_node *node, void *data)
> +{
> +    const struct nbrec_load_balancer_table *nb_lb_table =
> +        EN_OVSDB_GET(engine_get_input("NB_load_balancer", node));
> +
> +    struct ed_type_lb_data *lb_data = (struct ed_type_lb_data *) data;
> +
> +    lb_data->tracked = true;
> +    struct tracked_lb_data *trk_lb_data = &lb_data->tracked_lb_data;
> +
> +    const struct nbrec_load_balancer *tracked_lb;
> +    NBREC_LOAD_BALANCER_TABLE_FOR_EACH_TRACKED (tracked_lb, nb_lb_table)
{
> +        struct ovn_northd_lb *lb;
> +        if (nbrec_load_balancer_is_new(tracked_lb)) {
> +            /* New load balancer. */
> +            lb = ovn_northd_lb_create(tracked_lb);
> +            hmap_insert(&lb_data->lbs, &lb->hmap_node,
> +                        uuid_hash(&tracked_lb->header_.uuid));
> +            add_crupdated_lb_to_tracked_data(lb, trk_lb_data,
> +                                             lb->health_checks);
> +        } else if (nbrec_load_balancer_is_deleted(tracked_lb)) {
> +            lb = ovn_northd_lb_find(&lb_data->lbs,
> +                                    &tracked_lb->header_.uuid);
> +            ovs_assert(lb);
> +            hmap_remove(&lb_data->lbs, &lb->hmap_node);
> +            add_deleted_lb_to_tracked_data(lb, trk_lb_data,
> +                                           lb->health_checks);
> +        } else {
> +            /* Load balancer updated. */
> +            lb = ovn_northd_lb_find(&lb_data->lbs,
> +                                    &tracked_lb->header_.uuid);
> +            ovs_assert(lb);
> +            bool health_checks = lb->health_checks;
> +            ovn_northd_lb_reinit(lb, tracked_lb);
> +            health_checks |= lb->health_checks;
> +            add_crupdated_lb_to_tracked_data(lb, trk_lb_data,
health_checks);
> +        }
> +    }
> +
> +    engine_set_node_state(node, EN_UPDATED);
> +    return true;
> +}
> +
> +bool
> +lb_data_load_balancer_group_handler(struct engine_node *node, void *data)
> +{
> +    struct ed_type_lb_data *lb_data = (struct ed_type_lb_data *) data;
> +    const struct nbrec_load_balancer_group_table *nb_lbg_table =
> +        EN_OVSDB_GET(engine_get_input("NB_load_balancer_group", node));
> +
> +    lb_data->tracked = true;
> +    struct tracked_lb_data *trk_lb_data = &lb_data->tracked_lb_data;
> +    const struct nbrec_load_balancer_group *tracked_lb_group;
> +    NBREC_LOAD_BALANCER_GROUP_TABLE_FOR_EACH_TRACKED (tracked_lb_group,
> +                                                      nb_lbg_table) {
> +        if (nbrec_load_balancer_group_is_new(tracked_lb_group)) {
> +            struct ovn_lb_group *lb_group =
> +                create_lb_group(tracked_lb_group, &lb_data->lbs,
> +                                &lb_data->lb_groups);
> +            struct crupdated_lb_group *clbg =
> +                add_crupdated_lb_group_to_tracked_data(lb_group,
trk_lb_data);
> +            for (size_t i = 0; i < lb_group->n_lbs; i++) {
> +                hmapx_add(&clbg->assoc_lbs, lb_group->lbs[i]);
> +            }
> +        } else if
(nbrec_load_balancer_group_is_deleted(tracked_lb_group)) {
> +            struct ovn_lb_group *lb_group;
> +            lb_group = ovn_lb_group_find(&lb_data->lb_groups,
> +
&tracked_lb_group->header_.uuid);
> +            ovs_assert(lb_group);
> +            hmap_remove(&lb_data->lb_groups, &lb_group->hmap_node);
> +            add_deleted_lb_group_to_tracked_data(lb_group, trk_lb_data);
> +        } else {
> +
> +            struct ovn_lb_group *lb_group;
> +            lb_group = ovn_lb_group_find(&lb_data->lb_groups,
> +
&tracked_lb_group->header_.uuid);
> +            ovs_assert(lb_group);
> +
> +            /* Determine the lbs which are added or deleted for this
> +             * lb group and add them to tracked data.
> +             * Eg.  If an lb group lbg1 before the update had [lb1, lb2,
lb3]
> +             *      And in the update, lb2 was removed and lb4 got
added, then
> +             *      add lb2 and lb4 to the trk_lb_data->crupdated_lbs. */
> +            struct hmapx pre_update_lbs =
HMAPX_INITIALIZER(&pre_update_lbs);
> +            for (size_t i = 0; i < lb_group->n_lbs; i++) {
> +                hmapx_add(&pre_update_lbs, lb_group->lbs[i]);
> +            }
> +            ovn_lb_group_reinit(lb_group, tracked_lb_group,
&lb_data->lbs);
> +            for (size_t i = 0; i < lb_group->n_lbs; i++) {
> +                build_lrouter_lb_ips(lb_group->lb_ips, lb_group->lbs[i]);
> +            }
> +
> +            struct crupdated_lb_group *clbg =
> +                add_crupdated_lb_group_to_tracked_data(lb_group,
trk_lb_data);
> +
> +            for (size_t i = 0; i < lb_group->n_lbs; i++) {
> +                struct ovn_northd_lb *lb = lb_group->lbs[i];
> +                struct hmapx_node *hmapx_node =
hmapx_find(&pre_update_lbs,
> +                                                           lb);
> +                if (!hmapx_node) {
> +                    hmapx_add(&clbg->assoc_lbs, lb);
> +                } else {
> +                    hmapx_delete(&pre_update_lbs, hmapx_node);
> +                }
> +            }
> +
> +            struct hmapx_node *hmapx_node;
> +            HMAPX_FOR_EACH_SAFE (hmapx_node, &pre_update_lbs) {
> +                struct ovn_northd_lb *lb = hmapx_node->data;
> +                /* Check if the pre updated lb is actually deleted or
> +                 * just disassociated from the lb group. If it's just
> +                 * disassociated, then set
'has_dissassoc_lbs_from_lb_grops' to
> +                 * true.  Later if required we can add this 'lb' to an
hmapx of
> +                 * disassociated_lbs. */
> +                if (!hmapx_find(&trk_lb_data->deleted_lbs, lb)) {
> +                    trk_lb_data->has_dissassoc_lbs_from_lb_grops = true;
> +                }
> +                hmapx_delete(&pre_update_lbs, hmapx_node);
> +            }
> +            hmapx_destroy(&pre_update_lbs);
> +        }
> +    }
> +
> +    engine_set_node_state(node, EN_UPDATED);
> +    return true;
> +}
> +
>  /* static functions. */
>  static void
>  lb_data_init(struct ed_type_lb_data *lb_data)
>  {
>      hmap_init(&lb_data->lbs);
>      hmap_init(&lb_data->lb_groups);
> +
> +    struct tracked_lb_data *trk_lb_data = &lb_data->tracked_lb_data;
> +    hmap_init(&trk_lb_data->crupdated_lbs);
> +    hmapx_init(&trk_lb_data->deleted_lbs);
> +    hmap_init(&trk_lb_data->crupdated_lb_groups);
> +    hmapx_init(&trk_lb_data->deleted_lb_groups);
>  }
>
>  static void
> @@ -93,6 +258,12 @@ lb_data_destroy(struct ed_type_lb_data *lb_data)
>          ovn_lb_group_destroy(lb_group);
>      }
>      hmap_destroy(&lb_data->lb_groups);
> +
> +    destroy_tracked_data(lb_data);
> +    hmap_destroy(&lb_data->tracked_lb_data.crupdated_lbs);
> +    hmapx_destroy(&lb_data->tracked_lb_data.deleted_lbs);
> +    hmapx_destroy(&lb_data->tracked_lb_data.deleted_lb_groups);
> +    hmap_destroy(&lb_data->tracked_lb_data.crupdated_lb_groups);
>  }
>
>  static void
> @@ -100,12 +271,9 @@ build_lbs(const struct nbrec_load_balancer_table
*nbrec_load_balancer_table,
>            const struct nbrec_load_balancer_group_table
*nbrec_lb_group_table,
>            struct hmap *lbs, struct hmap *lb_groups)
>  {
> -    struct ovn_lb_group *lb_group;
> -    struct ovn_northd_lb *lb_nb;
> -
>      const struct nbrec_load_balancer *nbrec_lb;
>      NBREC_LOAD_BALANCER_TABLE_FOR_EACH (nbrec_lb,
nbrec_load_balancer_table) {
> -        lb_nb = ovn_northd_lb_create(nbrec_lb);
> +        struct ovn_northd_lb *lb_nb = ovn_northd_lb_create(nbrec_lb);
>          hmap_insert(lbs, &lb_nb->hmap_node,
>                      uuid_hash(&nbrec_lb->header_.uuid));
>      }
> @@ -113,13 +281,98 @@ build_lbs(const struct nbrec_load_balancer_table
*nbrec_load_balancer_table,
>      const struct nbrec_load_balancer_group *nbrec_lb_group;
>      NBREC_LOAD_BALANCER_GROUP_TABLE_FOR_EACH (nbrec_lb_group,
>                                                nbrec_lb_group_table) {
> -        lb_group = ovn_lb_group_create(nbrec_lb_group, lbs);
> +        create_lb_group(nbrec_lb_group, lbs, lb_groups);
> +    }
> +}
>
> -        for (size_t i = 0; i < lb_group->n_lbs; i++) {
> -            build_lrouter_lb_ips(lb_group->lb_ips, lb_group->lbs[i]);
> -        }
> +static struct ovn_lb_group *
> +create_lb_group(const struct nbrec_load_balancer_group *nbrec_lb_group,
> +                struct hmap *lbs, struct hmap *lb_groups)
> +{
> +    struct ovn_lb_group *lb_group = ovn_lb_group_create(nbrec_lb_group,
lbs);
>
> -        hmap_insert(lb_groups, &lb_group->hmap_node,
> -                    uuid_hash(&lb_group->uuid));
> +    for (size_t i = 0; i < lb_group->n_lbs; i++) {
> +        build_lrouter_lb_ips(lb_group->lb_ips, lb_group->lbs[i]);
>      }
> +
> +    hmap_insert(lb_groups, &lb_group->hmap_node,
> +                uuid_hash(&lb_group->uuid));
> +
> +    return lb_group;
> +}
> +
> +static void
> +destroy_tracked_data(struct ed_type_lb_data *lb_data)
> +{
> +    lb_data->tracked = false;
> +    lb_data->tracked_lb_data.has_health_checks = false;
> +    lb_data->tracked_lb_data.has_dissassoc_lbs_from_lb_grops = false;
> +
> +    struct hmapx_node *node;
> +    HMAPX_FOR_EACH_SAFE (node, &lb_data->tracked_lb_data.deleted_lbs) {
> +        ovn_northd_lb_destroy(node->data);
> +        hmapx_delete(&lb_data->tracked_lb_data.deleted_lbs, node);
> +    }
> +
> +    HMAPX_FOR_EACH_SAFE (node,
&lb_data->tracked_lb_data.deleted_lb_groups) {
> +        ovn_lb_group_destroy(node->data);
> +        hmapx_delete(&lb_data->tracked_lb_data.deleted_lb_groups, node);
> +    }
> +
> +    struct crupdated_lb *clb;
> +    HMAP_FOR_EACH_POP (clb, hmap_node,
> +                       &lb_data->tracked_lb_data.crupdated_lbs) {
> +        free(clb);
> +    }
> +
> +    struct crupdated_lb_group *crupdated_lbg;
> +    HMAP_FOR_EACH_POP (crupdated_lbg, hmap_node,
> +                       &lb_data->tracked_lb_data.crupdated_lb_groups) {
> +        hmapx_destroy(&crupdated_lbg->assoc_lbs);
> +        free(crupdated_lbg);
> +    }
> +}
> +
> +static void
> +add_crupdated_lb_to_tracked_data(struct ovn_northd_lb *lb,
> +                                 struct tracked_lb_data *tracked_lb_data,
> +                                 bool health_checks)
> +{
> +    struct crupdated_lb *clb = xzalloc(sizeof *clb);
> +    clb->lb = lb;
> +    hmap_insert(&tracked_lb_data->crupdated_lbs, &clb->hmap_node,
> +                uuid_hash(&lb->nlb->header_.uuid));
> +    if (health_checks) {
> +        tracked_lb_data->has_health_checks = true;
> +    }
> +}
> +
> +static void
> +add_deleted_lb_to_tracked_data(struct ovn_northd_lb *lb,
> +                               struct tracked_lb_data *tracked_lb_data,
> +                               bool health_checks)
> +{
> +    hmapx_add(&tracked_lb_data->deleted_lbs, lb);
> +    if (health_checks) {
> +        tracked_lb_data->has_health_checks = true;
> +    }
> +}
> +
> +static struct crupdated_lb_group *
> +add_crupdated_lb_group_to_tracked_data(struct ovn_lb_group *lbg,
> +                                       struct tracked_lb_data
*tracked_lb_data)
> +{
> +    struct crupdated_lb_group *clbg = xzalloc(sizeof *clbg);
> +    clbg->lbg = lbg;
> +    hmapx_init(&clbg->assoc_lbs);
> +    hmap_insert(&tracked_lb_data->crupdated_lb_groups, &clbg->hmap_node,
> +                uuid_hash(&lbg->uuid));
> +    return clbg;
> +}
> +
> +static void
> +add_deleted_lb_group_to_tracked_data(struct ovn_lb_group *lbg,
> +                                     struct tracked_lb_data
*tracked_lb_data)
> +{
> +    hmapx_add(&tracked_lb_data->deleted_lb_groups, lbg);
>  }
> diff --git a/northd/en-lb-data.h b/northd/en-lb-data.h
> index 96fb8c1f8d..2e9a024620 100644
> --- a/northd/en-lb-data.h
> +++ b/northd/en-lb-data.h
> @@ -4,9 +4,51 @@
>  #include <config.h>
>
>  #include "openvswitch/hmap.h"
> +#include "include/openvswitch/list.h"
> +#include "lib/hmapx.h"
>
>  #include "lib/inc-proc-eng.h"
>
> +struct ovn_northd_lb;
> +struct ovn_lb_group;
> +
> +struct crupdated_lb {
> +    struct hmap_node hmap_node;
> +
> +    struct ovn_northd_lb *lb;
> +};
> +
> +struct crupdated_lb_group {
> +    struct hmap_node hmap_node;
> +
> +    struct ovn_lb_group *lbg;
> +    /* hmapx of newly associated lbs to this lb group.
> +     * hmapx node is 'struct ovn_northd_lb *' */
> +    struct hmapx assoc_lbs;
> +};
> +
> +struct tracked_lb_data {
> +    /* Both created and updated lbs. hmapx node is 'struct crupdated_lb
*'. */
> +    struct hmap crupdated_lbs;
> +
> +    /* Deleted lbs. */
> +    struct hmapx deleted_lbs;
> +
> +    /* Both created and updated lb_groups. hmap node is
> +     * 'struct crupdated_lb_group'. */
> +    struct hmap crupdated_lb_groups;
> +
> +    /* Deleted lb_groups. hmapx node is  'struct ovn_lb_group *'. */
> +    struct hmapx deleted_lb_groups;
> +
> +    /* Indicates if any of the tracked lb has health checks enabled. */
> +    bool has_health_checks;
> +
> +    /* Indicates if any lb got disassociated from a lb group
> +     * but not deleted. */
> +    bool has_dissassoc_lbs_from_lb_grops;
> +};
> +
>  /* struct which maintains the data of the engine node lb_data. */
>  struct ed_type_lb_data {
>      /* hmap of load balancers.  hmap node is 'struct ovn_northd_lb *' */
> @@ -14,10 +56,18 @@ struct ed_type_lb_data {
>
>      /* hmap of load balancer groups.  hmap node is 'struct ovn_lb_group
*' */
>      struct hmap lb_groups;
> +
> +    /* tracked data*/
> +    bool tracked;
> +    struct tracked_lb_data tracked_lb_data;
>  };
>
>  void *en_lb_data_init(struct engine_node *, struct engine_arg *);
>  void en_lb_data_run(struct engine_node *, void *data);
>  void en_lb_data_cleanup(void *data);
> +void en_lb_data_clear_tracked_data(void *data);
> +
> +bool lb_data_load_balancer_handler(struct engine_node *, void *data);
> +bool lb_data_load_balancer_group_handler(struct engine_node *, void
*data);
>
>  #endif /* end of EN_NORTHD_LB_DATA_H */
> diff --git a/northd/en-northd.c b/northd/en-northd.c
> index a0dae65752..d59ef062df 100644
> --- a/northd/en-northd.c
> +++ b/northd/en-northd.c
> @@ -206,6 +206,47 @@ northd_sb_port_binding_handler(struct engine_node
*node,
>      return true;
>  }
>
> +bool
> +northd_lb_data_handler(struct engine_node *node, void *data)
> +{
> +    struct ed_type_lb_data *lb_data = engine_get_input_data("lb_data",
node);
> +
> +    if (!lb_data->tracked) {
> +        return false;
> +    }
> +
> +    if (lb_data->tracked_lb_data.has_health_checks) {
> +        /* Fall back to recompute since a tracked load balancer
> +         * has health checks configured as I-P is not yet supported
> +         * for such load balancers. */
> +        return false;
> +    }
> +
> +    /* Fall back to recompute if any load balancer was dissociated from
> +     * a load balancer group (but not deleted). */
> +    if (lb_data->tracked_lb_data.has_dissassoc_lbs_from_lb_grops) {
> +        return false;
> +    }
> +
> +    /* Fall back to recompute if load balancer groups are deleted. */
> +    if (!hmapx_is_empty(&lb_data->tracked_lb_data.deleted_lb_groups)) {
> +        return false;
> +    }
> +

nit: I'd prefer putting the above fallback logic into
northd_handle_lb_data_changes() so that it is more clear to have related
logic in one place. Otherwise, for example, we can't find any handling for
deleted_lb_groups in northd_handle_lb_data_changes(), which makes that
function look incomplete. Putting all fallback scenarios outside of the
function also makes the return value useless.

Other than this, the patch looks good to me:

Acked-by: Han Zhou <hzhou@ovn.org>

> +    struct northd_data *nd = data;
> +
> +    if (!northd_handle_lb_data_changes(&lb_data->tracked_lb_data,
> +                                       &nd->ls_datapaths,
> +                                       &nd->lr_datapaths,
> +                                       &nd->lb_datapaths_map,
> +                                       &nd->lb_group_datapaths_map)) {
> +        return false;
> +    }
> +
> +    engine_set_node_state(node, EN_UPDATED);
> +    return true;
> +}
> +
>  void
>  *en_northd_init(struct engine_node *node OVS_UNUSED,
>                  struct engine_arg *arg OVS_UNUSED)
> diff --git a/northd/en-northd.h b/northd/en-northd.h
> index 20cc77f108..3c77b64bb2 100644
> --- a/northd/en-northd.h
> +++ b/northd/en-northd.h
> @@ -17,5 +17,6 @@ void en_northd_clear_tracked_data(void *data);
>  bool northd_nb_nb_global_handler(struct engine_node *, void *data
OVS_UNUSED);
>  bool northd_nb_logical_switch_handler(struct engine_node *, void *data);
>  bool northd_sb_port_binding_handler(struct engine_node *, void *data);
> +bool northd_lb_data_handler(struct engine_node *, void *data);
>
>  #endif /* EN_NORTHD_H */
> diff --git a/northd/inc-proc-northd.c b/northd/inc-proc-northd.c
> index 0a99e87bc2..75d059645f 100644
> --- a/northd/inc-proc-northd.c
> +++ b/northd/inc-proc-northd.c
> @@ -141,13 +141,18 @@ static ENGINE_NODE(sync_to_sb_addr_set,
"sync_to_sb_addr_set");
>  static ENGINE_NODE(fdb_aging, "fdb_aging");
>  static ENGINE_NODE(fdb_aging_waker, "fdb_aging_waker");
>  static ENGINE_NODE(sync_to_sb_lb, "sync_to_sb_lb");
> -static ENGINE_NODE(lb_data, "lb_data");
> +static ENGINE_NODE_WITH_CLEAR_TRACK_DATA(lb_data, "lb_data");
>
>  void inc_proc_northd_init(struct ovsdb_idl_loop *nb,
>                            struct ovsdb_idl_loop *sb)
>  {
>      /* Define relationships between nodes where first argument is
dependent
>       * on the second argument */
> +    engine_add_input(&en_lb_data, &en_nb_load_balancer,
> +                     lb_data_load_balancer_handler);
> +    engine_add_input(&en_lb_data, &en_nb_load_balancer_group,
> +                     lb_data_load_balancer_group_handler);
> +
>      engine_add_input(&en_northd, &en_nb_port_group, NULL);
>      engine_add_input(&en_northd, &en_nb_acl, NULL);
>      engine_add_input(&en_northd, &en_nb_logical_router, NULL);
> @@ -175,13 +180,10 @@ void inc_proc_northd_init(struct ovsdb_idl_loop *nb,
>                       northd_sb_port_binding_handler);
>      engine_add_input(&en_northd, &en_nb_nb_global,
>                       northd_nb_nb_global_handler);
> +    engine_add_input(&en_northd, &en_lb_data, northd_lb_data_handler);
>      engine_add_input(&en_northd, &en_nb_logical_switch,
>                       northd_nb_logical_switch_handler);
>
> -    engine_add_input(&en_lb_data, &en_nb_load_balancer, NULL);
> -    engine_add_input(&en_lb_data, &en_nb_load_balancer_group, NULL);
> -    engine_add_input(&en_northd, &en_lb_data, NULL);
> -
>      engine_add_input(&en_mac_binding_aging, &en_nb_nb_global, NULL);
>      engine_add_input(&en_mac_binding_aging, &en_sb_mac_binding, NULL);
>      engine_add_input(&en_mac_binding_aging, &en_northd, NULL);
> diff --git a/northd/northd.c b/northd/northd.c
> index 78b82950e4..e9afa07177 100644
> --- a/northd/northd.c
> +++ b/northd/northd.c
> @@ -40,6 +40,7 @@
>  #include "lib/lb.h"
>  #include "memory.h"
>  #include "northd.h"
> +#include "en-lb-data.h"
>  #include "lib/ovn-parallel-hmap.h"
>  #include "ovn/actions.h"
>  #include "ovn/features.h"
> @@ -5362,6 +5363,64 @@ northd_handle_sb_port_binding_changes(
>      return true;
>  }
>
> +/* Handler for lb_data engine changes.  For every tracked lb_data
> + * it creates or deletes the ovn_lb_datapaths/ovn_lb_group_datapaths
> + * from the lb_datapaths hmap and lb_group_datapaths hmap. */
> +bool
> +northd_handle_lb_data_changes(struct tracked_lb_data *trk_lb_data,
> +                              struct ovn_datapaths *ls_datapaths,
> +                              struct ovn_datapaths *lr_datapaths,
> +                              struct hmap *lb_datapaths_map,
> +                              struct hmap *lb_group_datapaths_map)
> +{
> +    struct ovn_lb_datapaths *lb_dps;
> +    struct ovn_northd_lb *lb;
> +    struct hmapx_node *hmapx_node;
> +    HMAPX_FOR_EACH (hmapx_node, &trk_lb_data->deleted_lbs) {
> +        lb = hmapx_node->data;
> +        const struct uuid *lb_uuid = &lb->nlb->header_.uuid;
> +
> +        lb_dps = ovn_lb_datapaths_find(lb_datapaths_map, lb_uuid);
> +        ovs_assert(lb_dps);
> +        hmap_remove(lb_datapaths_map, &lb_dps->hmap_node);
> +        ovn_lb_datapaths_destroy(lb_dps);
> +    }
> +
> +    struct crupdated_lb *clb;
> +    HMAP_FOR_EACH (clb, hmap_node, &trk_lb_data->crupdated_lbs) {
> +        lb = clb->lb;
> +        const struct uuid *lb_uuid = &lb->nlb->header_.uuid;
> +
> +        lb_dps = ovn_lb_datapaths_find(lb_datapaths_map, lb_uuid);
> +        if (!lb_dps) {
> +            lb_dps = ovn_lb_datapaths_create(lb, ods_size(ls_datapaths),
> +                                             ods_size(lr_datapaths));
> +            hmap_insert(lb_datapaths_map, &lb_dps->hmap_node,
> +                        uuid_hash(lb_uuid));
> +        }
> +    }
> +
> +    struct ovn_lb_group_datapaths *lb_group_dps;
> +    struct ovn_lb_group *lbg;
> +    struct crupdated_lb_group *crupdated_lbg;
> +    HMAP_FOR_EACH (crupdated_lbg, hmap_node,
> +                   &trk_lb_data->crupdated_lb_groups) {
> +        lbg = crupdated_lbg->lbg;
> +        const struct uuid *lb_uuid = &lbg->uuid;
> +
> +        lb_group_dps =
ovn_lb_group_datapaths_find(lb_group_datapaths_map,
> +                                                   lb_uuid);
> +        if (!lb_group_dps) {
> +            lb_group_dps = ovn_lb_group_datapaths_create(
> +                lbg, ods_size(ls_datapaths), ods_size(lr_datapaths));
> +            hmap_insert(lb_group_datapaths_map, &lb_group_dps->hmap_node,
> +                        uuid_hash(lb_uuid));
> +        }
> +    }
> +
> +    return true;
> +}
> +
>  struct multicast_group {
>      const char *name;
>      uint16_t key;               /*
OVN_MIN_MULTICAST...OVN_MAX_MULTICAST. */
> diff --git a/northd/northd.h b/northd/northd.h
> index 7d92028c7d..7d17921fa2 100644
> --- a/northd/northd.h
> +++ b/northd/northd.h
> @@ -347,6 +347,13 @@ bool lflow_handle_northd_ls_changes(struct
ovsdb_idl_txn *ovnsb_txn,
>  bool northd_handle_sb_port_binding_changes(
>      const struct sbrec_port_binding_table *, struct hmap *ls_ports);
>
> +struct tracked_lb_data;
> +bool northd_handle_lb_data_changes(struct tracked_lb_data *,
> +                                   struct ovn_datapaths *ls_datapaths,
> +                                   struct ovn_datapaths *lr_datapaths,
> +                                   struct hmap *lb_datapaths_map,
> +                                   struct hmap *lb_group_datapaths_map);
> +
>  void build_bfd_table(struct ovsdb_idl_txn *ovnsb_txn,
>                       const struct nbrec_bfd_table *,
>                       const struct sbrec_bfd_table *,
> diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
> index 9e36f96182..213fd51d77 100644
> --- a/tests/ovn-northd.at
> +++ b/tests/ovn-northd.at
> @@ -9751,3 +9751,460 @@ AT_CHECK([grep "lr_in_gw_redirect" R1flows |sed
s'/table=../table=??/' |sort], [
>
>  AT_CLEANUP
>  ])
> +
> +OVN_FOR_EACH_NORTHD_NO_HV([
> +AT_SETUP([Load balancer incremental processing])
> +ovn_start
> +
> +check_engine_stats() {
> +  node=$1
> +  recompute=$2
> +  compute=$3
> +
> +  echo "__file__:__line__: Checking engine stats for node $node :
recompute - \
> +$recompute : compute - $compute"
> +
> +  node_stat=$(as northd ovn-appctl -t NORTHD_TYPE inc-engine/show-stats
$node)
> +  # node_stat will be of this format :
> +  #     - Node: lflow - recompute: 3 - compute: 0 - abort: 0
> +  node_recompute_ct=$(echo $node_stat | cut -d '-' -f2 | cut -d ':' -f2)
> +  node_compute_ct=$(echo $node_stat | cut -d '-' -f3 | cut -d ':' -f2)
> +
> +  if [[ "$recompute" == "norecompute" ]]; then
> +    # node should not be recomputed
> +    echo "Expecting $node recompute count - $node_recompute_ct to be 0"
> +    check test "$node_recompute_ct" -eq "0"
> +  else
> +    echo "Expecting $node recompute count - $node_recompute_ct not to be
0"
> +    check test "$node_recompute_ct" -ne "0"
> +  fi
> +
> +  if [[ "$compute" == "nocompute" ]]; then
> +    # node should not be computed
> +    echo "Expecting $node compute count - $node_compute_ct to be 0"
> +    check test "$node_compute_ct" -eq "0"
> +  else
> +    echo "Expecting $node compute count - $node_compute_ct not to be 0"
> +    check test "$node_compute_ct" -ne "0"
> +  fi
> +}
> +
> +# Test I-P for load balancers.
> +# Presently ovn-northd handles I-P for NB LBs in northd_lb_data engine
node
> +# only.
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl --wait=sb lb-add lb1 10.0.0.10:80 10.0.0.3:80
> +
> +check_engine_stats lb_data norecompute compute
> +check_engine_stats northd norecompute compute
> +check_engine_stats lflow recompute nocompute
> +
> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +
> +check ovn-nbctl --wait=sb set load_balancer .
ip_port_mappings:10.0.0.3=sw0-p1:10.0.0.2
> +check_engine_stats lb_data norecompute compute
> +check_engine_stats northd norecompute compute
> +check_engine_stats lflow recompute nocompute
> +
> +check ovn-nbctl --wait=sb set load_balancer . options:foo=bar
> +check_engine_stats lb_data norecompute compute
> +check_engine_stats northd norecompute compute
> +check_engine_stats lflow recompute nocompute
> +
> +check ovn-nbctl --wait=sb -- lb-add lb2 20.0.0.10:80 20.0.0.20:80 --
lb-add lb3 30.0.0.10:80 30.0.0.20:80
> +check_engine_stats lb_data norecompute compute
> +check_engine_stats northd norecompute compute
> +check_engine_stats lflow recompute nocompute
> +
> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +
> +check ovn-nbctl --wait=sb -- lb-del lb2 -- lb-del lb3
> +check_engine_stats lb_data norecompute compute
> +check_engine_stats northd norecompute compute
> +check_engine_stats lflow recompute nocompute
> +
> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +
> +AT_CHECK([ovn-nbctl --wait=sb \
> +          -- --id=@hc create Load_Balancer_Health_Check
vip="10.0.0.10\:80" \
> +             options:failure_count=100 \
> +          -- add Load_Balancer . health_check @hc | uuidfilt], [0], [<0>
> +])
> +check_engine_stats lb_data norecompute compute
> +check_engine_stats northd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +
> +# Any change to load balancer health check should also result in full
recompute
> +# of northd node (but not northd_lb_data node)
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl --wait=sb set load_balancer_health_check .
options:foo=bar1
> +check_engine_stats lb_data norecompute compute
> +check_engine_stats northd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +
> +# Delete the health check from the load balancer.  northd engine node
should do a full recompute.
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl --wait=sb clear Load_Balancer . health_check
> +check_engine_stats lb_data norecompute compute
> +check_engine_stats northd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl ls-add sw0
> +check ovn-nbctl --wait=sb lr-add lr0
> +ovn-nbctl lrp-add lr0 lr0-sw0 00:00:00:00:ff:01 10.0.0.1/24
> +ovn-nbctl lsp-add sw0 sw0-lr0
> +ovn-nbctl lsp-set-type sw0-lr0 router
> +ovn-nbctl lsp-set-addresses sw0-lr0 00:00:00:00:ff:01
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +ovn-nbctl --wait=sb lsp-set-options sw0-lr0 router-port=lr0-sw0
> +check_engine_stats lb_data norecompute nocompute
> +check_engine_stats northd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +
> +# Associate lb1 to sw0. There should be a full recompute of northd
engine node
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl --wait=sb ls-lb-add sw0 lb1
> +check_engine_stats lb_data norecompute nocompute
> +check_engine_stats northd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +
> +# Modify the backend of the lb1 vip
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl --wait=sb set load_balancer lb1 vips:'"10.0.0.10:80"'='"
10.0.0.100:80"'
> +check_engine_stats lb_data norecompute compute
> +check_engine_stats northd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> +
> +# Cleanup the vip of lb1.
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl --wait=sb clear load_Balancer lb1 vips
> +check_engine_stats lb_data norecompute compute
> +check_engine_stats northd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> +
> +# Set the vips of lb1 back
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl --wait=sb lb-add lb1 10.0.0.10:80 10.0.0.3:80
> +check_engine_stats lb_data norecompute compute
> +check_engine_stats northd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> +
> +# Add another vip to lb1
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl --wait=sb lb-add lb1 10.0.0.20:80 10.0.0.30:8080
> +check_engine_stats lb_data norecompute compute
> +check_engine_stats northd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> +
> +# Disassociate lb1 from sw0. There should be a full recompute of northd
engine node.
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl --wait=sb ls-lb-del sw0 lb1
> +check_engine_stats lb_data norecompute nocompute
> +check_engine_stats northd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> +
> +# Add lb1 to lr0 and then disassociate
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl --wait=sb lr-lb-add lr0 lb1
> +check_engine_stats lb_data norecompute nocompute
> +check_engine_stats northd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> +
> +# Modify the backend of the lb1 vip
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl --wait=sb set load_balancer lb1 vips:'"10.0.0.10:80"'='"
10.0.0.100:80"'
> +check_engine_stats lb_data norecompute compute
> +check_engine_stats northd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> +
> +# Cleanup the vip of lb1.
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl --wait=sb clear load_Balancer lb1 vips
> +check_engine_stats lb_data norecompute compute
> +check_engine_stats northd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> +
> +# Set the vips of lb1 back
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl --wait=sb lb-add lb1 10.0.0.10:80 10.0.0.3:80
> +check_engine_stats lb_data norecompute compute
> +check_engine_stats northd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> +
> +# Add another vip to lb1
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl --wait=sb lb-add lb1 10.0.0.20:80 10.0.0.30:8080
> +check_engine_stats lb_data norecompute compute
> +check_engine_stats northd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> +
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl --wait=sb lr-lb-del lr0 lb1
> +check_engine_stats lb_data norecompute nocompute
> +check_engine_stats northd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> +
> +# Test load balancer group now
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +lbg1_uuid=$(ovn-nbctl create load_balancer_group name=lbg1)
> +check_engine_stats lb_data norecompute compute
> +check_engine_stats northd norecompute compute
> +check_engine_stats lflow recompute nocompute
> +
> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +
> +lb1_uuid=$(fetch_column nb:Load_Balancer _uuid)
> +
> +# Add lb to the lbg1 group
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl add load_balancer_group . load_Balancer $lb1_uuid
> +check_engine_stats lb_data norecompute compute
> +check_engine_stats northd norecompute compute
> +check_engine_stats lflow recompute nocompute
> +
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl clear load_balancer_group . load_Balancer
> +check_engine_stats lb_data norecompute compute
> +check_engine_stats northd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +
> +# Add back lb to the lbg1 group
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl add load_balancer_group . load_Balancer $lb1_uuid
> +check_engine_stats lb_data norecompute compute
> +check_engine_stats northd norecompute compute
> +check_engine_stats lflow recompute nocompute
> +
> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> +
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl add logical_switch sw0 load_balancer_group $lbg1_uuid
> +check_engine_stats lb_data norecompute nocompute
> +check_engine_stats northd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +
> +# Update lb and this should result in recompute
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl --wait=sb set load_balancer . options:bar=foo
> +check_engine_stats lb_data norecompute compute
> +check_engine_stats northd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +
> +# Modify the backend of the lb1 vip
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl --wait=sb set load_balancer lb1 vips:'"10.0.0.10:80"'='"
10.0.0.100:80"'
> +check_engine_stats lb_data norecompute compute
> +check_engine_stats northd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> +
> +# Cleanup the vip of lb1.
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl --wait=sb clear load_Balancer lb1 vips
> +check_engine_stats lb_data norecompute compute
> +check_engine_stats northd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> +
> +# Set the vips of lb1 back
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl --wait=sb lb-add lb1 10.0.0.10:80 10.0.0.3:80
> +check_engine_stats lb_data norecompute compute
> +check_engine_stats northd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> +
> +# Add another vip to lb1
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl --wait=sb lb-add lb1 10.0.0.20:80 10.0.0.30:8080
> +check_engine_stats lb_data norecompute compute
> +check_engine_stats northd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> +
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl clear logical_switch sw0 load_balancer_group
> +check_engine_stats lb_data norecompute nocompute
> +check_engine_stats northd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl add logical_router lr0 load_balancer_group $lbg1_uuid
> +check_engine_stats lb_data norecompute nocompute
> +check_engine_stats northd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> +
> +# Modify the backend of the lb1 vip
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl --wait=sb set load_balancer lb1 vips:'"10.0.0.10:80"'='"
10.0.0.100:80"'
> +check_engine_stats lb_data norecompute compute
> +check_engine_stats northd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> +
> +# Cleanup the vip of lb1.
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl --wait=sb clear load_Balancer lb1 vips
> +check_engine_stats lb_data norecompute compute
> +check_engine_stats northd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> +
> +# Set the vips of lb1 back
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl --wait=sb lb-add lb1 10.0.0.10:80 10.0.0.3:80
> +check_engine_stats lb_data norecompute compute
> +check_engine_stats northd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> +
> +# Add another vip to lb1
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl --wait=sb lb-add lb1 10.0.0.20:80 10.0.0.30:8080
> +check_engine_stats lb_data norecompute compute
> +check_engine_stats northd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> +
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl clear logical_router lr0 load_balancer_group
> +check_engine_stats lb_data norecompute nocompute
> +check_engine_stats northd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +
> +# Add back lb group to logical switch and then delete it.
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl add logical_switch sw0 load_balancer_group $lbg1_uuid
> +check_engine_stats lb_data norecompute nocompute
> +check_engine_stats northd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl clear logical_switch sw0 load_balancer_group -- \
> +    destroy load_balancer_group $lbg1_uuid
> +check_engine_stats lb_data norecompute compute
> +check_engine_stats northd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +
> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> +
> +# Test the scenario where a load balancer is associated to
> +# a logical switch sw0 and also to a lb group lbg1 and lbg1
> +# is also associated to the logical switch sw0 and logical
> +# router lr1
> +
> +check ovn-nbctl lr-add lr1
> +check ovn-nbctl lb-add lb2 20.0.0.20:80 30.0.0.30:8080
> +check ovn-nbctl lb-add lb3 30.0.0.20:80 30.0.0.30:8080
> +check ovn-nbctl --wait=sb lb-add lb4 40.0.0.20:80 30.0.0.30:8080
> +
> +lb2_uuid=$(fetch_column nb:Load_Balancer _uuid name=lb2)
> +lb3_uuid=$(fetch_column nb:Load_Balancer _uuid name=lb3)
> +lb4_uuid=$(fetch_column nb:Load_Balancer _uuid name=lb4)
> +
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +lbg1_uuid=$(ovn-nbctl create load_balancer_group name=lbg1)
> +check_engine_stats lb_data norecompute compute
> +check_engine_stats northd norecompute compute
> +check_engine_stats lflow recompute nocompute
> +
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl set load_balancer_group .
load_balancer="$lb2_uuid,$lb3_uuid,$lb4_uuid"
> +check_engine_stats lb_data norecompute compute
> +check_engine_stats northd norecompute compute
> +check_engine_stats lflow recompute nocompute
> +
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl set logical_switch sw0 load_balancer_group=$lbg1_uuid
> +check_engine_stats lb_data norecompute nocompute
> +check_engine_stats northd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> +
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl set logical_router lr1 load_balancer_group=$lbg1_uuid
> +check_engine_stats lb_data norecompute nocompute
> +check_engine_stats northd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> +
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl --wait=sb ls-lb-add sw0 lb2
> +check_engine_stats lb_data norecompute nocompute
> +check_engine_stats northd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> +
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl --wait=sb ls-lb-add sw0 lb3
> +check_engine_stats lb_data norecompute nocompute
> +check_engine_stats northd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> +
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl --wait=sb lr-lb-add lr1 lb1
> +check ovn-nbctl --wait=sb lr-lb-add lr1 lb2
> +check_engine_stats lb_data norecompute nocompute
> +check_engine_stats northd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> +
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl --wait=sb ls-lb-del sw0 lb2
> +check_engine_stats lb_data norecompute nocompute
> +check_engine_stats northd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> +
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl --wait=sb lr-lb-del lr1 lb2
> +check_engine_stats lb_data norecompute nocompute
> +check_engine_stats northd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> +
> +# Deleting lb4 should not result in lflow recompute as it is
> +# only associated with logical switch sw0.
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl --wait=sb lb-del lb4
> +check_engine_stats lb_data norecompute compute
> +check_engine_stats northd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> +
> +# Deleting lb2 should result in lflow recompute as it is
> +# associated with logical router lr1 through lb group.
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl --wait=sb lb-del lb2
> +check_engine_stats lb_data norecompute compute
> +check_engine_stats northd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> +
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl --wait=sb remove load_balancer_group . load_balancer
$lb3_uuid
> +check_engine_stats lb_data norecompute compute
> +check_engine_stats northd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> +
> +AT_CLEANUP
> +])
> --
> 2.40.1
>
> _______________________________________________
> dev mailing list
> dev@openvswitch.org
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
Ales Musil Aug. 30, 2023, 12:08 p.m. UTC | #2
On Fri, Aug 18, 2023 at 10:58 AM <numans@ovn.org> wrote:

> From: Numan Siddique <numans@ovn.org>
>
> Any changes to load balancers and load balancer groups
> are handled incrementally in the newly added 'lb_data'
> engine node.  'lb_data' is input to 'northd' node
> and the handler - northd_lb_data_handler in 'northd'
> node handles the changes.
>
> If a load balancer or load balancer group is associated to
> a logical switch or router then 'northd' node falls back
> to full recompute.  Upcoming patch will handle this scenario.
>
> Signed-off-by: Numan Siddique <numans@ovn.org>
> ---
>  lib/lb.c                 |  82 +++++--
>  lib/lb.h                 |   9 +
>  northd/en-lb-data.c      | 273 ++++++++++++++++++++++-
>  northd/en-lb-data.h      |  50 +++++
>  northd/en-northd.c       |  41 ++++
>  northd/en-northd.h       |   1 +
>  northd/inc-proc-northd.c |  12 +-
>  northd/northd.c          |  59 +++++
>  northd/northd.h          |   7 +
>  tests/ovn-northd.at      | 457 +++++++++++++++++++++++++++++++++++++++
>  10 files changed, 960 insertions(+), 31 deletions(-)
>
> diff --git a/lib/lb.c b/lib/lb.c
> index e874680a3f..6fd67e2218 100644
> --- a/lib/lb.c
> +++ b/lib/lb.c
> @@ -606,13 +606,13 @@ ovn_lb_get_health_check(const struct
> nbrec_load_balancer *nbrec_lb,
>      return NULL;
>  }
>
> -struct ovn_northd_lb *
> -ovn_northd_lb_create(const struct nbrec_load_balancer *nbrec_lb)
> +static void
> +ovn_northd_lb_init(struct ovn_northd_lb *lb,
> +                   const struct nbrec_load_balancer *nbrec_lb)
>  {
>      bool template = smap_get_bool(&nbrec_lb->options, "template", false);
>      bool is_udp = nullable_string_is_equal(nbrec_lb->protocol, "udp");
>      bool is_sctp = nullable_string_is_equal(nbrec_lb->protocol, "sctp");
> -    struct ovn_northd_lb *lb = xzalloc(sizeof *lb);
>      int address_family = !strcmp(smap_get_def(&nbrec_lb->options,
>                                                "address-family", "ipv4"),
>                                   "ipv4")
> @@ -668,6 +668,10 @@ ovn_northd_lb_create(const struct nbrec_load_balancer
> *nbrec_lb)
>                                                    "reject", false);
>          ovn_northd_lb_vip_init(lb_vip_nb, lb_vip, nbrec_lb,
>                                 node->key, node->value, template);
> +        if (lb_vip_nb->lb_health_check) {
> +            lb->health_checks = true;
> +        }
> +
>          if (IN6_IS_ADDR_V4MAPPED(&lb_vip->vip)) {
>              sset_add(&lb->ips_v4, lb_vip->vip_str);
>          } else {
> @@ -711,6 +715,13 @@ ovn_northd_lb_create(const struct nbrec_load_balancer
> *nbrec_lb)
>          ds_chomp(&sel_fields, ',');
>          lb->selection_fields = ds_steal_cstr(&sel_fields);
>      }
> +}
> +
> +struct ovn_northd_lb *
> +ovn_northd_lb_create(const struct nbrec_load_balancer *nbrec_lb)
> +{
> +    struct ovn_northd_lb *lb = xzalloc(sizeof *lb);
> +    ovn_northd_lb_init(lb, nbrec_lb);
>      return lb;
>  }
>
> @@ -736,8 +747,8 @@ ovn_northd_lb_get_vips(const struct ovn_northd_lb *lb)
>      return &lb->nlb->vips;
>  }
>
> -void
> -ovn_northd_lb_destroy(struct ovn_northd_lb *lb)
> +static void
> +ovn_northd_lb_cleanup(struct ovn_northd_lb *lb)
>  {
>      for (size_t i = 0; i < lb->n_vips; i++) {
>          ovn_lb_vip_destroy(&lb->vips[i]);
> @@ -745,24 +756,36 @@ ovn_northd_lb_destroy(struct ovn_northd_lb *lb)
>      }
>      free(lb->vips);
>      free(lb->vips_nb);
> +    lb->vips = NULL;
> +    lb->vips_nb = NULL;
>      smap_destroy(&lb->template_vips);
>      sset_destroy(&lb->ips_v4);
>      sset_destroy(&lb->ips_v6);
>      free(lb->selection_fields);
> +    lb->selection_fields = NULL;
> +    lb->health_checks = false;
> +}
> +
> +void
> +ovn_northd_lb_destroy(struct ovn_northd_lb *lb)
> +{
> +    ovn_northd_lb_cleanup(lb);
>      free(lb);
>  }
>
> -/* Constructs a new 'struct ovn_lb_group' object from the Nb LB Group
> record
> - * and an array of 'struct ovn_northd_lb' objects for its associated
> - * load balancers. */
> -struct ovn_lb_group *
> -ovn_lb_group_create(const struct nbrec_load_balancer_group
> *nbrec_lb_group,
> -                    const struct hmap *lbs)
> +void
> +ovn_northd_lb_reinit(struct ovn_northd_lb *lb,
> +                     const struct nbrec_load_balancer *nbrec_lb)
>  {
> -    struct ovn_lb_group *lb_group;
> +    ovn_northd_lb_cleanup(lb);
> +    ovn_northd_lb_init(lb, nbrec_lb);
> +}
>
> -    lb_group = xzalloc(sizeof *lb_group);
> -    lb_group->uuid = nbrec_lb_group->header_.uuid;
> +static void
> +ovn_lb_group_init(struct ovn_lb_group *lb_group,
> +                  const struct nbrec_load_balancer_group *nbrec_lb_group,
> +                  const struct hmap *lbs)
> +{
>      lb_group->n_lbs = nbrec_lb_group->n_load_balancer;
>      lb_group->lbs = xmalloc(lb_group->n_lbs * sizeof *lb_group->lbs);
>      lb_group->lb_ips = ovn_lb_ip_set_create();
> @@ -772,10 +795,29 @@ ovn_lb_group_create(const struct
> nbrec_load_balancer_group *nbrec_lb_group,
>              &nbrec_lb_group->load_balancer[i]->header_.uuid;
>          lb_group->lbs[i] = ovn_northd_lb_find(lbs, lb_uuid);
>      }
> +}
>
> +/* Constructs a new 'struct ovn_lb_group' object from the Nb LB Group
> record
> + * and an array of 'struct ovn_northd_lb' objects for its associated
> + * load balancers. */
> +struct ovn_lb_group *
> +ovn_lb_group_create(const struct nbrec_load_balancer_group
> *nbrec_lb_group,
> +                    const struct hmap *lbs)
> +{
> +    struct ovn_lb_group *lb_group = xzalloc(sizeof *lb_group);
> +    lb_group->uuid = nbrec_lb_group->header_.uuid;
> +    ovn_lb_group_init(lb_group, nbrec_lb_group, lbs);
>      return lb_group;
>  }
>
> +static void
> +ovn_lb_group_cleanup(struct ovn_lb_group *lb_group)
> +{
> +    ovn_lb_ip_set_destroy(lb_group->lb_ips);
> +    lb_group->lb_ips = NULL;
> +    free(lb_group->lbs);
> +}
> +
>  void
>  ovn_lb_group_destroy(struct ovn_lb_group *lb_group)
>  {
> @@ -783,11 +825,19 @@ ovn_lb_group_destroy(struct ovn_lb_group *lb_group)
>          return;
>      }
>
> -    ovn_lb_ip_set_destroy(lb_group->lb_ips);
> -    free(lb_group->lbs);
> +    ovn_lb_group_cleanup(lb_group);
>      free(lb_group);
>  }
>
> +void
> +ovn_lb_group_reinit(struct ovn_lb_group *lb_group,
> +                    const struct nbrec_load_balancer_group
> *nbrec_lb_group,
> +                    const struct hmap *lbs)
> +{
> +    ovn_lb_group_cleanup(lb_group);
> +    ovn_lb_group_init(lb_group, nbrec_lb_group, lbs);
> +}
> +
>  struct ovn_lb_group *
>  ovn_lb_group_find(const struct hmap *lb_groups, const struct uuid *uuid)
>  {
> diff --git a/lib/lb.h b/lib/lb.h
> index 0339050cba..74905c73b7 100644
> --- a/lib/lb.h
> +++ b/lib/lb.h
> @@ -77,6 +77,9 @@ struct ovn_northd_lb {
>
>      struct sset ips_v4;
>      struct sset ips_v6;
> +
> +    /* Indicates if the load balancer has health checks configured. */
> +    bool health_checks;
>  };
>
>  struct ovn_lb_vip {
> @@ -130,6 +133,8 @@ struct ovn_northd_lb *ovn_northd_lb_find(const struct
> hmap *,
>                                           const struct uuid *);
>  const struct smap *ovn_northd_lb_get_vips(const struct ovn_northd_lb *);
>  void ovn_northd_lb_destroy(struct ovn_northd_lb *);
> +void ovn_northd_lb_reinit(struct ovn_northd_lb *,
> +                          const struct nbrec_load_balancer *);
>
>  void build_lrouter_lb_ips(struct ovn_lb_ip_set *,
>                            const struct ovn_northd_lb *);
> @@ -148,6 +153,10 @@ struct ovn_lb_group *ovn_lb_group_create(
>  void ovn_lb_group_destroy(struct ovn_lb_group *lb_group);
>  struct ovn_lb_group *ovn_lb_group_find(const struct hmap *lb_groups,
>                                         const struct uuid *);
> +void ovn_lb_group_reinit(
> +    struct ovn_lb_group *lb_group,
> +    const struct nbrec_load_balancer_group *,
> +    const struct hmap *lbs);
>
>  struct ovn_lb_datapaths {
>      struct hmap_node hmap_node;
> diff --git a/northd/en-lb-data.c b/northd/en-lb-data.c
> index 04d8d3e5d7..328c003675 100644
> --- a/northd/en-lb-data.c
> +++ b/northd/en-lb-data.c
> @@ -19,6 +19,7 @@
>  #include <stdio.h>
>
>  /* OVS includes */
> +#include "include/openvswitch/hmap.h"
>  #include "openvswitch/util.h"
>  #include "openvswitch/vlog.h"
>
> @@ -38,7 +39,27 @@ static void lb_data_destroy(struct ed_type_lb_data *);
>  static void build_lbs(const struct nbrec_load_balancer_table *,
>                        const struct nbrec_load_balancer_group_table *,
>                        struct hmap *lbs, struct hmap *lb_groups);
> +static struct ovn_lb_group *create_lb_group(
> +    const struct nbrec_load_balancer_group *, struct hmap *lbs,
> +    struct hmap *lb_groups);
> +static void destroy_tracked_data(struct ed_type_lb_data *);
> +static void add_crupdated_lb_to_tracked_data(struct ovn_northd_lb *,
> +                                                    struct
> tracked_lb_data *,
> +                                                    bool health_checks);
> +static void add_deleted_lb_to_tracked_data(struct ovn_northd_lb *,
> +                                                  struct tracked_lb_data
> *,
> +                                                  bool health_checks);
> +static struct crupdated_lb_group *
> +    add_crupdated_lb_group_to_tracked_data(struct ovn_lb_group *,
> +                                           struct tracked_lb_data *);
> +static void add_deleted_lb_group_to_tracked_data(
> +    struct ovn_lb_group *, struct tracked_lb_data *);
>
> +/* 'lb_data' engine node manages the NB load balancers and load balancer
> + * groups.  For each NB LB, it creates 'struct ovn_northd_lb' and
> + * for each NB LB group, it creates 'struct ovn_lb_group' and stores in
> + * the respective hmaps in it data (ed_type_lb_data).
> + */
>  void *
>  en_lb_data_init(struct engine_node *node OVS_UNUSED,
>                         struct engine_arg *arg OVS_UNUSED)
> @@ -60,6 +81,7 @@ en_lb_data_run(struct engine_node *node, void *data)
>      const struct nbrec_load_balancer_group_table *nb_lbg_table =
>          EN_OVSDB_GET(engine_get_input("NB_load_balancer_group", node));
>
> +    lb_data->tracked = false;
>      build_lbs(nb_lb_table, nb_lbg_table, &lb_data->lbs,
> &lb_data->lb_groups);
>      engine_set_node_state(node, EN_UPDATED);
>  }
> @@ -71,12 +93,155 @@ en_lb_data_cleanup(void *data)
>      lb_data_destroy(lb_data);
>  }
>
> +void
> +en_lb_data_clear_tracked_data(void *data)
> +{
> +    struct ed_type_lb_data *lb_data = (struct ed_type_lb_data *) data;
> +    destroy_tracked_data(lb_data);
> +}
> +
> +
> +/* Handler functions. */
> +bool
> +lb_data_load_balancer_handler(struct engine_node *node, void *data)
> +{
> +    const struct nbrec_load_balancer_table *nb_lb_table =
> +        EN_OVSDB_GET(engine_get_input("NB_load_balancer", node));
> +
> +    struct ed_type_lb_data *lb_data = (struct ed_type_lb_data *) data;
> +
> +    lb_data->tracked = true;
> +    struct tracked_lb_data *trk_lb_data = &lb_data->tracked_lb_data;
> +
> +    const struct nbrec_load_balancer *tracked_lb;
> +    NBREC_LOAD_BALANCER_TABLE_FOR_EACH_TRACKED (tracked_lb, nb_lb_table) {
> +        struct ovn_northd_lb *lb;
> +        if (nbrec_load_balancer_is_new(tracked_lb)) {
> +            /* New load balancer. */
> +            lb = ovn_northd_lb_create(tracked_lb);
> +            hmap_insert(&lb_data->lbs, &lb->hmap_node,
> +                        uuid_hash(&tracked_lb->header_.uuid));
> +            add_crupdated_lb_to_tracked_data(lb, trk_lb_data,
> +                                             lb->health_checks);
> +        } else if (nbrec_load_balancer_is_deleted(tracked_lb)) {
> +            lb = ovn_northd_lb_find(&lb_data->lbs,
> +                                    &tracked_lb->header_.uuid);
> +            ovs_assert(lb);
> +            hmap_remove(&lb_data->lbs, &lb->hmap_node);
> +            add_deleted_lb_to_tracked_data(lb, trk_lb_data,
> +                                           lb->health_checks);
> +        } else {
> +            /* Load balancer updated. */
> +            lb = ovn_northd_lb_find(&lb_data->lbs,
> +                                    &tracked_lb->header_.uuid);
> +            ovs_assert(lb);
> +            bool health_checks = lb->health_checks;
> +            ovn_northd_lb_reinit(lb, tracked_lb);
> +            health_checks |= lb->health_checks;
> +            add_crupdated_lb_to_tracked_data(lb, trk_lb_data,
> health_checks);
> +        }
> +    }
> +
> +    engine_set_node_state(node, EN_UPDATED);
> +    return true;
> +}
> +
> +bool
> +lb_data_load_balancer_group_handler(struct engine_node *node, void *data)
> +{
> +    struct ed_type_lb_data *lb_data = (struct ed_type_lb_data *) data;
> +    const struct nbrec_load_balancer_group_table *nb_lbg_table =
> +        EN_OVSDB_GET(engine_get_input("NB_load_balancer_group", node));
> +
> +    lb_data->tracked = true;
> +    struct tracked_lb_data *trk_lb_data = &lb_data->tracked_lb_data;
> +    const struct nbrec_load_balancer_group *tracked_lb_group;
> +    NBREC_LOAD_BALANCER_GROUP_TABLE_FOR_EACH_TRACKED (tracked_lb_group,
> +                                                      nb_lbg_table) {
> +        if (nbrec_load_balancer_group_is_new(tracked_lb_group)) {
> +            struct ovn_lb_group *lb_group =
> +                create_lb_group(tracked_lb_group, &lb_data->lbs,
> +                                &lb_data->lb_groups);
> +            struct crupdated_lb_group *clbg =
> +                add_crupdated_lb_group_to_tracked_data(lb_group,
> trk_lb_data);
> +            for (size_t i = 0; i < lb_group->n_lbs; i++) {
> +                hmapx_add(&clbg->assoc_lbs, lb_group->lbs[i]);
> +            }
> +        } else if
> (nbrec_load_balancer_group_is_deleted(tracked_lb_group)) {
> +            struct ovn_lb_group *lb_group;
> +            lb_group = ovn_lb_group_find(&lb_data->lb_groups,
> +                                         &tracked_lb_group->header_.uuid);
> +            ovs_assert(lb_group);
> +            hmap_remove(&lb_data->lb_groups, &lb_group->hmap_node);
> +            add_deleted_lb_group_to_tracked_data(lb_group, trk_lb_data);
> +        } else {
> +
> +            struct ovn_lb_group *lb_group;
> +            lb_group = ovn_lb_group_find(&lb_data->lb_groups,
> +                                         &tracked_lb_group->header_.uuid);
> +            ovs_assert(lb_group);
> +
> +            /* Determine the lbs which are added or deleted for this
> +             * lb group and add them to tracked data.
> +             * Eg.  If an lb group lbg1 before the update had [lb1, lb2,
> lb3]
> +             *      And in the update, lb2 was removed and lb4 got added,
> then
> +             *      add lb2 and lb4 to the trk_lb_data->crupdated_lbs. */
> +            struct hmapx pre_update_lbs =
> HMAPX_INITIALIZER(&pre_update_lbs);
> +            for (size_t i = 0; i < lb_group->n_lbs; i++) {
> +                hmapx_add(&pre_update_lbs, lb_group->lbs[i]);
> +            }
> +            ovn_lb_group_reinit(lb_group, tracked_lb_group,
> &lb_data->lbs);
> +            for (size_t i = 0; i < lb_group->n_lbs; i++) {
> +                build_lrouter_lb_ips(lb_group->lb_ips, lb_group->lbs[i]);
> +            }
> +
> +            struct crupdated_lb_group *clbg =
> +                add_crupdated_lb_group_to_tracked_data(lb_group,
> trk_lb_data);
> +
> +            for (size_t i = 0; i < lb_group->n_lbs; i++) {
> +                struct ovn_northd_lb *lb = lb_group->lbs[i];
> +                struct hmapx_node *hmapx_node =
> hmapx_find(&pre_update_lbs,
> +                                                           lb);
> +                if (!hmapx_node) {
> +                    hmapx_add(&clbg->assoc_lbs, lb);
> +                } else {
> +                    hmapx_delete(&pre_update_lbs, hmapx_node);
> +                }
> +            }
> +
> +            struct hmapx_node *hmapx_node;
> +            HMAPX_FOR_EACH_SAFE (hmapx_node, &pre_update_lbs) {
> +                struct ovn_northd_lb *lb = hmapx_node->data;
> +                /* Check if the pre updated lb is actually deleted or
> +                 * just disassociated from the lb group. If it's just
> +                 * disassociated, then set
> 'has_dissassoc_lbs_from_lb_grops' to
> +                 * true.  Later if required we can add this 'lb' to an
> hmapx of
> +                 * disassociated_lbs. */
> +                if (!hmapx_find(&trk_lb_data->deleted_lbs, lb)) {
> +                    trk_lb_data->has_dissassoc_lbs_from_lb_grops = true;
> +                }
> +                hmapx_delete(&pre_update_lbs, hmapx_node);
> +            }
> +            hmapx_destroy(&pre_update_lbs);
> +        }
> +    }
> +
> +    engine_set_node_state(node, EN_UPDATED);
> +    return true;
> +}
> +
>  /* static functions. */
>  static void
>  lb_data_init(struct ed_type_lb_data *lb_data)
>  {
>      hmap_init(&lb_data->lbs);
>      hmap_init(&lb_data->lb_groups);
> +
> +    struct tracked_lb_data *trk_lb_data = &lb_data->tracked_lb_data;
> +    hmap_init(&trk_lb_data->crupdated_lbs);
> +    hmapx_init(&trk_lb_data->deleted_lbs);
> +    hmap_init(&trk_lb_data->crupdated_lb_groups);
> +    hmapx_init(&trk_lb_data->deleted_lb_groups);
>  }
>
>  static void
> @@ -93,6 +258,12 @@ lb_data_destroy(struct ed_type_lb_data *lb_data)
>          ovn_lb_group_destroy(lb_group);
>      }
>      hmap_destroy(&lb_data->lb_groups);
> +
> +    destroy_tracked_data(lb_data);
> +    hmap_destroy(&lb_data->tracked_lb_data.crupdated_lbs);
> +    hmapx_destroy(&lb_data->tracked_lb_data.deleted_lbs);
> +    hmapx_destroy(&lb_data->tracked_lb_data.deleted_lb_groups);
> +    hmap_destroy(&lb_data->tracked_lb_data.crupdated_lb_groups);
>  }
>
>  static void
> @@ -100,12 +271,9 @@ build_lbs(const struct nbrec_load_balancer_table
> *nbrec_load_balancer_table,
>            const struct nbrec_load_balancer_group_table
> *nbrec_lb_group_table,
>            struct hmap *lbs, struct hmap *lb_groups)
>  {
> -    struct ovn_lb_group *lb_group;
> -    struct ovn_northd_lb *lb_nb;
> -
>      const struct nbrec_load_balancer *nbrec_lb;
>      NBREC_LOAD_BALANCER_TABLE_FOR_EACH (nbrec_lb,
> nbrec_load_balancer_table) {
> -        lb_nb = ovn_northd_lb_create(nbrec_lb);
> +        struct ovn_northd_lb *lb_nb = ovn_northd_lb_create(nbrec_lb);
>          hmap_insert(lbs, &lb_nb->hmap_node,
>                      uuid_hash(&nbrec_lb->header_.uuid));
>      }
> @@ -113,13 +281,98 @@ build_lbs(const struct nbrec_load_balancer_table
> *nbrec_load_balancer_table,
>      const struct nbrec_load_balancer_group *nbrec_lb_group;
>      NBREC_LOAD_BALANCER_GROUP_TABLE_FOR_EACH (nbrec_lb_group,
>                                                nbrec_lb_group_table) {
> -        lb_group = ovn_lb_group_create(nbrec_lb_group, lbs);
> +        create_lb_group(nbrec_lb_group, lbs, lb_groups);
> +    }
> +}
>
> -        for (size_t i = 0; i < lb_group->n_lbs; i++) {
> -            build_lrouter_lb_ips(lb_group->lb_ips, lb_group->lbs[i]);
> -        }
> +static struct ovn_lb_group *
> +create_lb_group(const struct nbrec_load_balancer_group *nbrec_lb_group,
> +                struct hmap *lbs, struct hmap *lb_groups)
> +{
> +    struct ovn_lb_group *lb_group = ovn_lb_group_create(nbrec_lb_group,
> lbs);
>
> -        hmap_insert(lb_groups, &lb_group->hmap_node,
> -                    uuid_hash(&lb_group->uuid));
> +    for (size_t i = 0; i < lb_group->n_lbs; i++) {
> +        build_lrouter_lb_ips(lb_group->lb_ips, lb_group->lbs[i]);
>      }
> +
> +    hmap_insert(lb_groups, &lb_group->hmap_node,
> +                uuid_hash(&lb_group->uuid));
> +
> +    return lb_group;
> +}
> +
> +static void
> +destroy_tracked_data(struct ed_type_lb_data *lb_data)
> +{
> +    lb_data->tracked = false;
> +    lb_data->tracked_lb_data.has_health_checks = false;
> +    lb_data->tracked_lb_data.has_dissassoc_lbs_from_lb_grops = false;
> +
> +    struct hmapx_node *node;
> +    HMAPX_FOR_EACH_SAFE (node, &lb_data->tracked_lb_data.deleted_lbs) {
> +        ovn_northd_lb_destroy(node->data);
> +        hmapx_delete(&lb_data->tracked_lb_data.deleted_lbs, node);
> +    }
> +
> +    HMAPX_FOR_EACH_SAFE (node,
> &lb_data->tracked_lb_data.deleted_lb_groups) {
> +        ovn_lb_group_destroy(node->data);
> +        hmapx_delete(&lb_data->tracked_lb_data.deleted_lb_groups, node);
> +    }
> +
> +    struct crupdated_lb *clb;
> +    HMAP_FOR_EACH_POP (clb, hmap_node,
> +                       &lb_data->tracked_lb_data.crupdated_lbs) {
> +        free(clb);
> +    }
> +
> +    struct crupdated_lb_group *crupdated_lbg;
> +    HMAP_FOR_EACH_POP (crupdated_lbg, hmap_node,
> +                       &lb_data->tracked_lb_data.crupdated_lb_groups) {
> +        hmapx_destroy(&crupdated_lbg->assoc_lbs);
> +        free(crupdated_lbg);
> +    }
> +}
> +
> +static void
> +add_crupdated_lb_to_tracked_data(struct ovn_northd_lb *lb,
> +                                 struct tracked_lb_data *tracked_lb_data,
> +                                 bool health_checks)
> +{
> +    struct crupdated_lb *clb = xzalloc(sizeof *clb);
> +    clb->lb = lb;
> +    hmap_insert(&tracked_lb_data->crupdated_lbs, &clb->hmap_node,
> +                uuid_hash(&lb->nlb->header_.uuid));
> +    if (health_checks) {
> +        tracked_lb_data->has_health_checks = true;
> +    }
> +}
> +
> +static void
> +add_deleted_lb_to_tracked_data(struct ovn_northd_lb *lb,
> +                               struct tracked_lb_data *tracked_lb_data,
> +                               bool health_checks)
> +{
> +    hmapx_add(&tracked_lb_data->deleted_lbs, lb);
> +    if (health_checks) {
> +        tracked_lb_data->has_health_checks = true;
> +    }
> +}
> +
> +static struct crupdated_lb_group *
> +add_crupdated_lb_group_to_tracked_data(struct ovn_lb_group *lbg,
> +                                       struct tracked_lb_data
> *tracked_lb_data)
> +{
> +    struct crupdated_lb_group *clbg = xzalloc(sizeof *clbg);
> +    clbg->lbg = lbg;
> +    hmapx_init(&clbg->assoc_lbs);
> +    hmap_insert(&tracked_lb_data->crupdated_lb_groups, &clbg->hmap_node,
> +                uuid_hash(&lbg->uuid));
> +    return clbg;
> +}
> +
> +static void
> +add_deleted_lb_group_to_tracked_data(struct ovn_lb_group *lbg,
> +                                     struct tracked_lb_data
> *tracked_lb_data)
> +{
> +    hmapx_add(&tracked_lb_data->deleted_lb_groups, lbg);
>  }
> diff --git a/northd/en-lb-data.h b/northd/en-lb-data.h
> index 96fb8c1f8d..2e9a024620 100644
> --- a/northd/en-lb-data.h
> +++ b/northd/en-lb-data.h
> @@ -4,9 +4,51 @@
>  #include <config.h>
>
>  #include "openvswitch/hmap.h"
> +#include "include/openvswitch/list.h"
> +#include "lib/hmapx.h"
>
>  #include "lib/inc-proc-eng.h"
>
> +struct ovn_northd_lb;
> +struct ovn_lb_group;
> +
> +struct crupdated_lb {
> +    struct hmap_node hmap_node;
> +
> +    struct ovn_northd_lb *lb;
> +};
> +
> +struct crupdated_lb_group {
> +    struct hmap_node hmap_node;
> +
> +    struct ovn_lb_group *lbg;
> +    /* hmapx of newly associated lbs to this lb group.
> +     * hmapx node is 'struct ovn_northd_lb *' */
> +    struct hmapx assoc_lbs;
> +};
> +
> +struct tracked_lb_data {
> +    /* Both created and updated lbs. hmapx node is 'struct crupdated_lb
> *'. */
> +    struct hmap crupdated_lbs;
> +
> +    /* Deleted lbs. */
> +    struct hmapx deleted_lbs;
> +
> +    /* Both created and updated lb_groups. hmap node is
> +     * 'struct crupdated_lb_group'. */
> +    struct hmap crupdated_lb_groups;
> +
> +    /* Deleted lb_groups. hmapx node is  'struct ovn_lb_group *'. */
> +    struct hmapx deleted_lb_groups;
> +
> +    /* Indicates if any of the tracked lb has health checks enabled. */
> +    bool has_health_checks;
> +
> +    /* Indicates if any lb got disassociated from a lb group
> +     * but not deleted. */
> +    bool has_dissassoc_lbs_from_lb_grops;
> +};
> +
>  /* struct which maintains the data of the engine node lb_data. */
>  struct ed_type_lb_data {
>      /* hmap of load balancers.  hmap node is 'struct ovn_northd_lb *' */
> @@ -14,10 +56,18 @@ struct ed_type_lb_data {
>
>      /* hmap of load balancer groups.  hmap node is 'struct ovn_lb_group
> *' */
>      struct hmap lb_groups;
> +
> +    /* tracked data*/
> +    bool tracked;
> +    struct tracked_lb_data tracked_lb_data;
>  };
>
>  void *en_lb_data_init(struct engine_node *, struct engine_arg *);
>  void en_lb_data_run(struct engine_node *, void *data);
>  void en_lb_data_cleanup(void *data);
> +void en_lb_data_clear_tracked_data(void *data);
> +
> +bool lb_data_load_balancer_handler(struct engine_node *, void *data);
> +bool lb_data_load_balancer_group_handler(struct engine_node *, void
> *data);
>
>  #endif /* end of EN_NORTHD_LB_DATA_H */
> diff --git a/northd/en-northd.c b/northd/en-northd.c
> index a0dae65752..d59ef062df 100644
> --- a/northd/en-northd.c
> +++ b/northd/en-northd.c
> @@ -206,6 +206,47 @@ northd_sb_port_binding_handler(struct engine_node
> *node,
>      return true;
>  }
>
> +bool
> +northd_lb_data_handler(struct engine_node *node, void *data)
> +{
> +    struct ed_type_lb_data *lb_data = engine_get_input_data("lb_data",
> node);
> +
> +    if (!lb_data->tracked) {
> +        return false;
> +    }
> +
> +    if (lb_data->tracked_lb_data.has_health_checks) {
> +        /* Fall back to recompute since a tracked load balancer
> +         * has health checks configured as I-P is not yet supported
> +         * for such load balancers. */
> +        return false;
> +    }
> +
> +    /* Fall back to recompute if any load balancer was dissociated from
> +     * a load balancer group (but not deleted). */
> +    if (lb_data->tracked_lb_data.has_dissassoc_lbs_from_lb_grops) {
> +        return false;
> +    }
> +
> +    /* Fall back to recompute if load balancer groups are deleted. */
> +    if (!hmapx_is_empty(&lb_data->tracked_lb_data.deleted_lb_groups)) {
> +        return false;
> +    }
> +
> +    struct northd_data *nd = data;
> +
> +    if (!northd_handle_lb_data_changes(&lb_data->tracked_lb_data,
> +                                       &nd->ls_datapaths,
> +                                       &nd->lr_datapaths,
> +                                       &nd->lb_datapaths_map,
> +                                       &nd->lb_group_datapaths_map)) {
> +        return false;
> +    }
> +
> +    engine_set_node_state(node, EN_UPDATED);
> +    return true;
> +}
> +
>  void
>  *en_northd_init(struct engine_node *node OVS_UNUSED,
>                  struct engine_arg *arg OVS_UNUSED)
> diff --git a/northd/en-northd.h b/northd/en-northd.h
> index 20cc77f108..3c77b64bb2 100644
> --- a/northd/en-northd.h
> +++ b/northd/en-northd.h
> @@ -17,5 +17,6 @@ void en_northd_clear_tracked_data(void *data);
>  bool northd_nb_nb_global_handler(struct engine_node *, void *data
> OVS_UNUSED);
>  bool northd_nb_logical_switch_handler(struct engine_node *, void *data);
>  bool northd_sb_port_binding_handler(struct engine_node *, void *data);
> +bool northd_lb_data_handler(struct engine_node *, void *data);
>
>  #endif /* EN_NORTHD_H */
> diff --git a/northd/inc-proc-northd.c b/northd/inc-proc-northd.c
> index 0a99e87bc2..75d059645f 100644
> --- a/northd/inc-proc-northd.c
> +++ b/northd/inc-proc-northd.c
> @@ -141,13 +141,18 @@ static ENGINE_NODE(sync_to_sb_addr_set,
> "sync_to_sb_addr_set");
>  static ENGINE_NODE(fdb_aging, "fdb_aging");
>  static ENGINE_NODE(fdb_aging_waker, "fdb_aging_waker");
>  static ENGINE_NODE(sync_to_sb_lb, "sync_to_sb_lb");
> -static ENGINE_NODE(lb_data, "lb_data");
> +static ENGINE_NODE_WITH_CLEAR_TRACK_DATA(lb_data, "lb_data");
>
>  void inc_proc_northd_init(struct ovsdb_idl_loop *nb,
>                            struct ovsdb_idl_loop *sb)
>  {
>      /* Define relationships between nodes where first argument is
> dependent
>       * on the second argument */
> +    engine_add_input(&en_lb_data, &en_nb_load_balancer,
> +                     lb_data_load_balancer_handler);
> +    engine_add_input(&en_lb_data, &en_nb_load_balancer_group,
> +                     lb_data_load_balancer_group_handler);
> +
>      engine_add_input(&en_northd, &en_nb_port_group, NULL);
>      engine_add_input(&en_northd, &en_nb_acl, NULL);
>      engine_add_input(&en_northd, &en_nb_logical_router, NULL);
> @@ -175,13 +180,10 @@ void inc_proc_northd_init(struct ovsdb_idl_loop *nb,
>                       northd_sb_port_binding_handler);
>      engine_add_input(&en_northd, &en_nb_nb_global,
>                       northd_nb_nb_global_handler);
> +    engine_add_input(&en_northd, &en_lb_data, northd_lb_data_handler);
>      engine_add_input(&en_northd, &en_nb_logical_switch,
>                       northd_nb_logical_switch_handler);
>
> -    engine_add_input(&en_lb_data, &en_nb_load_balancer, NULL);
> -    engine_add_input(&en_lb_data, &en_nb_load_balancer_group, NULL);
> -    engine_add_input(&en_northd, &en_lb_data, NULL);
> -
>      engine_add_input(&en_mac_binding_aging, &en_nb_nb_global, NULL);
>      engine_add_input(&en_mac_binding_aging, &en_sb_mac_binding, NULL);
>      engine_add_input(&en_mac_binding_aging, &en_northd, NULL);
> diff --git a/northd/northd.c b/northd/northd.c
> index 78b82950e4..e9afa07177 100644
> --- a/northd/northd.c
> +++ b/northd/northd.c
> @@ -40,6 +40,7 @@
>  #include "lib/lb.h"
>  #include "memory.h"
>  #include "northd.h"
> +#include "en-lb-data.h"
>  #include "lib/ovn-parallel-hmap.h"
>  #include "ovn/actions.h"
>  #include "ovn/features.h"
> @@ -5362,6 +5363,64 @@ northd_handle_sb_port_binding_changes(
>      return true;
>  }
>
> +/* Handler for lb_data engine changes.  For every tracked lb_data
> + * it creates or deletes the ovn_lb_datapaths/ovn_lb_group_datapaths
> + * from the lb_datapaths hmap and lb_group_datapaths hmap. */
> +bool
> +northd_handle_lb_data_changes(struct tracked_lb_data *trk_lb_data,
> +                              struct ovn_datapaths *ls_datapaths,
> +                              struct ovn_datapaths *lr_datapaths,
> +                              struct hmap *lb_datapaths_map,
> +                              struct hmap *lb_group_datapaths_map)
> +{
> +    struct ovn_lb_datapaths *lb_dps;
> +    struct ovn_northd_lb *lb;
> +    struct hmapx_node *hmapx_node;
> +    HMAPX_FOR_EACH (hmapx_node, &trk_lb_data->deleted_lbs) {
> +        lb = hmapx_node->data;
> +        const struct uuid *lb_uuid = &lb->nlb->header_.uuid;
> +
> +        lb_dps = ovn_lb_datapaths_find(lb_datapaths_map, lb_uuid);
> +        ovs_assert(lb_dps);
> +        hmap_remove(lb_datapaths_map, &lb_dps->hmap_node);
> +        ovn_lb_datapaths_destroy(lb_dps);
> +    }
> +
> +    struct crupdated_lb *clb;
> +    HMAP_FOR_EACH (clb, hmap_node, &trk_lb_data->crupdated_lbs) {
> +        lb = clb->lb;
> +        const struct uuid *lb_uuid = &lb->nlb->header_.uuid;
> +
> +        lb_dps = ovn_lb_datapaths_find(lb_datapaths_map, lb_uuid);
> +        if (!lb_dps) {
> +            lb_dps = ovn_lb_datapaths_create(lb, ods_size(ls_datapaths),
> +                                             ods_size(lr_datapaths));
> +            hmap_insert(lb_datapaths_map, &lb_dps->hmap_node,
> +                        uuid_hash(lb_uuid));
> +        }
> +    }
> +
> +    struct ovn_lb_group_datapaths *lb_group_dps;
> +    struct ovn_lb_group *lbg;
> +    struct crupdated_lb_group *crupdated_lbg;
> +    HMAP_FOR_EACH (crupdated_lbg, hmap_node,
> +                   &trk_lb_data->crupdated_lb_groups) {
> +        lbg = crupdated_lbg->lbg;
> +        const struct uuid *lb_uuid = &lbg->uuid;
> +
> +        lb_group_dps = ovn_lb_group_datapaths_find(lb_group_datapaths_map,
> +                                                   lb_uuid);
> +        if (!lb_group_dps) {
> +            lb_group_dps = ovn_lb_group_datapaths_create(
> +                lbg, ods_size(ls_datapaths), ods_size(lr_datapaths));
> +            hmap_insert(lb_group_datapaths_map, &lb_group_dps->hmap_node,
> +                        uuid_hash(lb_uuid));
> +        }
> +    }
> +
> +    return true;
> +}
> +
>  struct multicast_group {
>      const char *name;
>      uint16_t key;               /* OVN_MIN_MULTICAST...OVN_MAX_MULTICAST.
> */
> diff --git a/northd/northd.h b/northd/northd.h
> index 7d92028c7d..7d17921fa2 100644
> --- a/northd/northd.h
> +++ b/northd/northd.h
> @@ -347,6 +347,13 @@ bool lflow_handle_northd_ls_changes(struct
> ovsdb_idl_txn *ovnsb_txn,
>  bool northd_handle_sb_port_binding_changes(
>      const struct sbrec_port_binding_table *, struct hmap *ls_ports);
>
> +struct tracked_lb_data;
> +bool northd_handle_lb_data_changes(struct tracked_lb_data *,
> +                                   struct ovn_datapaths *ls_datapaths,
> +                                   struct ovn_datapaths *lr_datapaths,
> +                                   struct hmap *lb_datapaths_map,
> +                                   struct hmap *lb_group_datapaths_map);
> +
>  void build_bfd_table(struct ovsdb_idl_txn *ovnsb_txn,
>                       const struct nbrec_bfd_table *,
>                       const struct sbrec_bfd_table *,
> diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
> index 9e36f96182..213fd51d77 100644
> --- a/tests/ovn-northd.at
> +++ b/tests/ovn-northd.at
> @@ -9751,3 +9751,460 @@ AT_CHECK([grep "lr_in_gw_redirect" R1flows |sed
> s'/table=../table=??/' |sort], [
>
>  AT_CLEANUP
>  ])
> +
> +OVN_FOR_EACH_NORTHD_NO_HV([
> +AT_SETUP([Load balancer incremental processing])
> +ovn_start
> +
> +check_engine_stats() {
> +  node=$1
> +  recompute=$2
> +  compute=$3
> +
> +  echo "__file__:__line__: Checking engine stats for node $node :
> recompute - \
> +$recompute : compute - $compute"
> +
> +  node_stat=$(as northd ovn-appctl -t NORTHD_TYPE inc-engine/show-stats
> $node)
> +  # node_stat will be of this format :
> +  #     - Node: lflow - recompute: 3 - compute: 0 - abort: 0
> +  node_recompute_ct=$(echo $node_stat | cut -d '-' -f2 | cut -d ':' -f2)
> +  node_compute_ct=$(echo $node_stat | cut -d '-' -f3 | cut -d ':' -f2)
> +
> +  if [[ "$recompute" == "norecompute" ]]; then
> +    # node should not be recomputed
> +    echo "Expecting $node recompute count - $node_recompute_ct to be 0"
> +    check test "$node_recompute_ct" -eq "0"
> +  else
> +    echo "Expecting $node recompute count - $node_recompute_ct not to be
> 0"
> +    check test "$node_recompute_ct" -ne "0"
> +  fi
> +
> +  if [[ "$compute" == "nocompute" ]]; then
> +    # node should not be computed
> +    echo "Expecting $node compute count - $node_compute_ct to be 0"
> +    check test "$node_compute_ct" -eq "0"
> +  else
> +    echo "Expecting $node compute count - $node_compute_ct not to be 0"
> +    check test "$node_compute_ct" -ne "0"
> +  fi
> +}
> +
> +# Test I-P for load balancers.
> +# Presently ovn-northd handles I-P for NB LBs in northd_lb_data engine
> node
> +# only.
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl --wait=sb lb-add lb1 10.0.0.10:80 10.0.0.3:80
> +
> +check_engine_stats lb_data norecompute compute
> +check_engine_stats northd norecompute compute
> +check_engine_stats lflow recompute nocompute
> +
> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +
> +check ovn-nbctl --wait=sb set load_balancer .
> ip_port_mappings:10.0.0.3=sw0-p1:10.0.0.2
> +check_engine_stats lb_data norecompute compute
> +check_engine_stats northd norecompute compute
> +check_engine_stats lflow recompute nocompute
> +
> +check ovn-nbctl --wait=sb set load_balancer . options:foo=bar
> +check_engine_stats lb_data norecompute compute
> +check_engine_stats northd norecompute compute
> +check_engine_stats lflow recompute nocompute
> +
> +check ovn-nbctl --wait=sb -- lb-add lb2 20.0.0.10:80 20.0.0.20:80 --
> lb-add lb3 30.0.0.10:80 30.0.0.20:80
> +check_engine_stats lb_data norecompute compute
> +check_engine_stats northd norecompute compute
> +check_engine_stats lflow recompute nocompute
> +
> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +
> +check ovn-nbctl --wait=sb -- lb-del lb2 -- lb-del lb3
> +check_engine_stats lb_data norecompute compute
> +check_engine_stats northd norecompute compute
> +check_engine_stats lflow recompute nocompute
> +
> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +
> +AT_CHECK([ovn-nbctl --wait=sb \
> +          -- --id=@hc create Load_Balancer_Health_Check
> vip="10.0.0.10\:80" \
> +             options:failure_count=100 \
> +          -- add Load_Balancer . health_check @hc | uuidfilt], [0], [<0>
> +])
> +check_engine_stats lb_data norecompute compute
> +check_engine_stats northd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +
> +# Any change to load balancer health check should also result in full
> recompute
> +# of northd node (but not northd_lb_data node)
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl --wait=sb set load_balancer_health_check .
> options:foo=bar1
> +check_engine_stats lb_data norecompute compute
> +check_engine_stats northd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +
> +# Delete the health check from the load balancer.  northd engine node
> should do a full recompute.
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl --wait=sb clear Load_Balancer . health_check
> +check_engine_stats lb_data norecompute compute
> +check_engine_stats northd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl ls-add sw0
> +check ovn-nbctl --wait=sb lr-add lr0
> +ovn-nbctl lrp-add lr0 lr0-sw0 00:00:00:00:ff:01 10.0.0.1/24
> +ovn-nbctl lsp-add sw0 sw0-lr0
> +ovn-nbctl lsp-set-type sw0-lr0 router
> +ovn-nbctl lsp-set-addresses sw0-lr0 00:00:00:00:ff:01
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +ovn-nbctl --wait=sb lsp-set-options sw0-lr0 router-port=lr0-sw0
> +check_engine_stats lb_data norecompute nocompute
> +check_engine_stats northd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +
> +# Associate lb1 to sw0. There should be a full recompute of northd engine
> node
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl --wait=sb ls-lb-add sw0 lb1
> +check_engine_stats lb_data norecompute nocompute
> +check_engine_stats northd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +
> +# Modify the backend of the lb1 vip
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl --wait=sb set load_balancer lb1 vips:'"10.0.0.10:80"'='"
> 10.0.0.100:80"'
> +check_engine_stats lb_data norecompute compute
> +check_engine_stats northd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> +
> +# Cleanup the vip of lb1.
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl --wait=sb clear load_Balancer lb1 vips
> +check_engine_stats lb_data norecompute compute
> +check_engine_stats northd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> +
> +# Set the vips of lb1 back
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl --wait=sb lb-add lb1 10.0.0.10:80 10.0.0.3:80
> +check_engine_stats lb_data norecompute compute
> +check_engine_stats northd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> +
> +# Add another vip to lb1
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl --wait=sb lb-add lb1 10.0.0.20:80 10.0.0.30:8080
> +check_engine_stats lb_data norecompute compute
> +check_engine_stats northd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> +
> +# Disassociate lb1 from sw0. There should be a full recompute of northd
> engine node.
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl --wait=sb ls-lb-del sw0 lb1
> +check_engine_stats lb_data norecompute nocompute
> +check_engine_stats northd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> +
> +# Add lb1 to lr0 and then disassociate
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl --wait=sb lr-lb-add lr0 lb1
> +check_engine_stats lb_data norecompute nocompute
> +check_engine_stats northd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> +
> +# Modify the backend of the lb1 vip
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl --wait=sb set load_balancer lb1 vips:'"10.0.0.10:80"'='"
> 10.0.0.100:80"'
> +check_engine_stats lb_data norecompute compute
> +check_engine_stats northd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> +
> +# Cleanup the vip of lb1.
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl --wait=sb clear load_Balancer lb1 vips
> +check_engine_stats lb_data norecompute compute
> +check_engine_stats northd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> +
> +# Set the vips of lb1 back
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl --wait=sb lb-add lb1 10.0.0.10:80 10.0.0.3:80
> +check_engine_stats lb_data norecompute compute
> +check_engine_stats northd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> +
> +# Add another vip to lb1
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl --wait=sb lb-add lb1 10.0.0.20:80 10.0.0.30:8080
> +check_engine_stats lb_data norecompute compute
> +check_engine_stats northd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> +
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl --wait=sb lr-lb-del lr0 lb1
> +check_engine_stats lb_data norecompute nocompute
> +check_engine_stats northd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> +
> +# Test load balancer group now
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +lbg1_uuid=$(ovn-nbctl create load_balancer_group name=lbg1)
> +check_engine_stats lb_data norecompute compute
> +check_engine_stats northd norecompute compute
> +check_engine_stats lflow recompute nocompute
> +
> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +
> +lb1_uuid=$(fetch_column nb:Load_Balancer _uuid)
> +
> +# Add lb to the lbg1 group
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl add load_balancer_group . load_Balancer $lb1_uuid
> +check_engine_stats lb_data norecompute compute
> +check_engine_stats northd norecompute compute
> +check_engine_stats lflow recompute nocompute
> +
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl clear load_balancer_group . load_Balancer
> +check_engine_stats lb_data norecompute compute
> +check_engine_stats northd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +
> +# Add back lb to the lbg1 group
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl add load_balancer_group . load_Balancer $lb1_uuid
> +check_engine_stats lb_data norecompute compute
> +check_engine_stats northd norecompute compute
> +check_engine_stats lflow recompute nocompute
> +
> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> +
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl add logical_switch sw0 load_balancer_group $lbg1_uuid
> +check_engine_stats lb_data norecompute nocompute
> +check_engine_stats northd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +
> +# Update lb and this should result in recompute
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl --wait=sb set load_balancer . options:bar=foo
> +check_engine_stats lb_data norecompute compute
> +check_engine_stats northd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +
> +# Modify the backend of the lb1 vip
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl --wait=sb set load_balancer lb1 vips:'"10.0.0.10:80"'='"
> 10.0.0.100:80"'
> +check_engine_stats lb_data norecompute compute
> +check_engine_stats northd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> +
> +# Cleanup the vip of lb1.
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl --wait=sb clear load_Balancer lb1 vips
> +check_engine_stats lb_data norecompute compute
> +check_engine_stats northd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> +
> +# Set the vips of lb1 back
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl --wait=sb lb-add lb1 10.0.0.10:80 10.0.0.3:80
> +check_engine_stats lb_data norecompute compute
> +check_engine_stats northd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> +
> +# Add another vip to lb1
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl --wait=sb lb-add lb1 10.0.0.20:80 10.0.0.30:8080
> +check_engine_stats lb_data norecompute compute
> +check_engine_stats northd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> +
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl clear logical_switch sw0 load_balancer_group
> +check_engine_stats lb_data norecompute nocompute
> +check_engine_stats northd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl add logical_router lr0 load_balancer_group $lbg1_uuid
> +check_engine_stats lb_data norecompute nocompute
> +check_engine_stats northd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> +
> +# Modify the backend of the lb1 vip
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl --wait=sb set load_balancer lb1 vips:'"10.0.0.10:80"'='"
> 10.0.0.100:80"'
> +check_engine_stats lb_data norecompute compute
> +check_engine_stats northd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> +
> +# Cleanup the vip of lb1.
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl --wait=sb clear load_Balancer lb1 vips
> +check_engine_stats lb_data norecompute compute
> +check_engine_stats northd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> +
> +# Set the vips of lb1 back
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl --wait=sb lb-add lb1 10.0.0.10:80 10.0.0.3:80
> +check_engine_stats lb_data norecompute compute
> +check_engine_stats northd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> +
> +# Add another vip to lb1
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl --wait=sb lb-add lb1 10.0.0.20:80 10.0.0.30:8080
> +check_engine_stats lb_data norecompute compute
> +check_engine_stats northd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> +
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl clear logical_router lr0 load_balancer_group
> +check_engine_stats lb_data norecompute nocompute
> +check_engine_stats northd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +
> +# Add back lb group to logical switch and then delete it.
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl add logical_switch sw0 load_balancer_group $lbg1_uuid
> +check_engine_stats lb_data norecompute nocompute
> +check_engine_stats northd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl clear logical_switch sw0 load_balancer_group -- \
> +    destroy load_balancer_group $lbg1_uuid
> +check_engine_stats lb_data norecompute compute
> +check_engine_stats northd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +
> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> +
> +# Test the scenario where a load balancer is associated to
> +# a logical switch sw0 and also to a lb group lbg1 and lbg1
> +# is also associated to the logical switch sw0 and logical
> +# router lr1
> +
> +check ovn-nbctl lr-add lr1
> +check ovn-nbctl lb-add lb2 20.0.0.20:80 30.0.0.30:8080
> +check ovn-nbctl lb-add lb3 30.0.0.20:80 30.0.0.30:8080
> +check ovn-nbctl --wait=sb lb-add lb4 40.0.0.20:80 30.0.0.30:8080
> +
> +lb2_uuid=$(fetch_column nb:Load_Balancer _uuid name=lb2)
> +lb3_uuid=$(fetch_column nb:Load_Balancer _uuid name=lb3)
> +lb4_uuid=$(fetch_column nb:Load_Balancer _uuid name=lb4)
> +
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +lbg1_uuid=$(ovn-nbctl create load_balancer_group name=lbg1)
> +check_engine_stats lb_data norecompute compute
> +check_engine_stats northd norecompute compute
> +check_engine_stats lflow recompute nocompute
> +
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl set load_balancer_group .
> load_balancer="$lb2_uuid,$lb3_uuid,$lb4_uuid"
> +check_engine_stats lb_data norecompute compute
> +check_engine_stats northd norecompute compute
> +check_engine_stats lflow recompute nocompute
> +
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl set logical_switch sw0 load_balancer_group=$lbg1_uuid
> +check_engine_stats lb_data norecompute nocompute
> +check_engine_stats northd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> +
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl set logical_router lr1 load_balancer_group=$lbg1_uuid
> +check_engine_stats lb_data norecompute nocompute
> +check_engine_stats northd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> +
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl --wait=sb ls-lb-add sw0 lb2
> +check_engine_stats lb_data norecompute nocompute
> +check_engine_stats northd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> +
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl --wait=sb ls-lb-add sw0 lb3
> +check_engine_stats lb_data norecompute nocompute
> +check_engine_stats northd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> +
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl --wait=sb lr-lb-add lr1 lb1
> +check ovn-nbctl --wait=sb lr-lb-add lr1 lb2
> +check_engine_stats lb_data norecompute nocompute
> +check_engine_stats northd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> +
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl --wait=sb ls-lb-del sw0 lb2
> +check_engine_stats lb_data norecompute nocompute
> +check_engine_stats northd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> +
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl --wait=sb lr-lb-del lr1 lb2
> +check_engine_stats lb_data norecompute nocompute
> +check_engine_stats northd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> +
> +# Deleting lb4 should not result in lflow recompute as it is
> +# only associated with logical switch sw0.
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl --wait=sb lb-del lb4
> +check_engine_stats lb_data norecompute compute
> +check_engine_stats northd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> +
> +# Deleting lb2 should result in lflow recompute as it is
> +# associated with logical router lr1 through lb group.
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl --wait=sb lb-del lb2
> +check_engine_stats lb_data norecompute compute
> +check_engine_stats northd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> +
> +check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
> +check ovn-nbctl --wait=sb remove load_balancer_group . load_balancer
> $lb3_uuid
> +check_engine_stats lb_data norecompute compute
> +check_engine_stats northd recompute nocompute
> +check_engine_stats lflow recompute nocompute
> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
> +
> +AT_CLEANUP
> +])
> --
> 2.40.1
>
> _______________________________________________
> dev mailing list
> dev@openvswitch.org
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>
>
Looks good to me, thanks.

Reviewed-by: Ales Musil <amusil@redhat.com>
diff mbox series

Patch

diff --git a/lib/lb.c b/lib/lb.c
index e874680a3f..6fd67e2218 100644
--- a/lib/lb.c
+++ b/lib/lb.c
@@ -606,13 +606,13 @@  ovn_lb_get_health_check(const struct nbrec_load_balancer *nbrec_lb,
     return NULL;
 }
 
-struct ovn_northd_lb *
-ovn_northd_lb_create(const struct nbrec_load_balancer *nbrec_lb)
+static void
+ovn_northd_lb_init(struct ovn_northd_lb *lb,
+                   const struct nbrec_load_balancer *nbrec_lb)
 {
     bool template = smap_get_bool(&nbrec_lb->options, "template", false);
     bool is_udp = nullable_string_is_equal(nbrec_lb->protocol, "udp");
     bool is_sctp = nullable_string_is_equal(nbrec_lb->protocol, "sctp");
-    struct ovn_northd_lb *lb = xzalloc(sizeof *lb);
     int address_family = !strcmp(smap_get_def(&nbrec_lb->options,
                                               "address-family", "ipv4"),
                                  "ipv4")
@@ -668,6 +668,10 @@  ovn_northd_lb_create(const struct nbrec_load_balancer *nbrec_lb)
                                                   "reject", false);
         ovn_northd_lb_vip_init(lb_vip_nb, lb_vip, nbrec_lb,
                                node->key, node->value, template);
+        if (lb_vip_nb->lb_health_check) {
+            lb->health_checks = true;
+        }
+
         if (IN6_IS_ADDR_V4MAPPED(&lb_vip->vip)) {
             sset_add(&lb->ips_v4, lb_vip->vip_str);
         } else {
@@ -711,6 +715,13 @@  ovn_northd_lb_create(const struct nbrec_load_balancer *nbrec_lb)
         ds_chomp(&sel_fields, ',');
         lb->selection_fields = ds_steal_cstr(&sel_fields);
     }
+}
+
+struct ovn_northd_lb *
+ovn_northd_lb_create(const struct nbrec_load_balancer *nbrec_lb)
+{
+    struct ovn_northd_lb *lb = xzalloc(sizeof *lb);
+    ovn_northd_lb_init(lb, nbrec_lb);
     return lb;
 }
 
@@ -736,8 +747,8 @@  ovn_northd_lb_get_vips(const struct ovn_northd_lb *lb)
     return &lb->nlb->vips;
 }
 
-void
-ovn_northd_lb_destroy(struct ovn_northd_lb *lb)
+static void
+ovn_northd_lb_cleanup(struct ovn_northd_lb *lb)
 {
     for (size_t i = 0; i < lb->n_vips; i++) {
         ovn_lb_vip_destroy(&lb->vips[i]);
@@ -745,24 +756,36 @@  ovn_northd_lb_destroy(struct ovn_northd_lb *lb)
     }
     free(lb->vips);
     free(lb->vips_nb);
+    lb->vips = NULL;
+    lb->vips_nb = NULL;
     smap_destroy(&lb->template_vips);
     sset_destroy(&lb->ips_v4);
     sset_destroy(&lb->ips_v6);
     free(lb->selection_fields);
+    lb->selection_fields = NULL;
+    lb->health_checks = false;
+}
+
+void
+ovn_northd_lb_destroy(struct ovn_northd_lb *lb)
+{
+    ovn_northd_lb_cleanup(lb);
     free(lb);
 }
 
-/* Constructs a new 'struct ovn_lb_group' object from the Nb LB Group record
- * and an array of 'struct ovn_northd_lb' objects for its associated
- * load balancers. */
-struct ovn_lb_group *
-ovn_lb_group_create(const struct nbrec_load_balancer_group *nbrec_lb_group,
-                    const struct hmap *lbs)
+void
+ovn_northd_lb_reinit(struct ovn_northd_lb *lb,
+                     const struct nbrec_load_balancer *nbrec_lb)
 {
-    struct ovn_lb_group *lb_group;
+    ovn_northd_lb_cleanup(lb);
+    ovn_northd_lb_init(lb, nbrec_lb);
+}
 
-    lb_group = xzalloc(sizeof *lb_group);
-    lb_group->uuid = nbrec_lb_group->header_.uuid;
+static void
+ovn_lb_group_init(struct ovn_lb_group *lb_group,
+                  const struct nbrec_load_balancer_group *nbrec_lb_group,
+                  const struct hmap *lbs)
+{
     lb_group->n_lbs = nbrec_lb_group->n_load_balancer;
     lb_group->lbs = xmalloc(lb_group->n_lbs * sizeof *lb_group->lbs);
     lb_group->lb_ips = ovn_lb_ip_set_create();
@@ -772,10 +795,29 @@  ovn_lb_group_create(const struct nbrec_load_balancer_group *nbrec_lb_group,
             &nbrec_lb_group->load_balancer[i]->header_.uuid;
         lb_group->lbs[i] = ovn_northd_lb_find(lbs, lb_uuid);
     }
+}
 
+/* Constructs a new 'struct ovn_lb_group' object from the Nb LB Group record
+ * and an array of 'struct ovn_northd_lb' objects for its associated
+ * load balancers. */
+struct ovn_lb_group *
+ovn_lb_group_create(const struct nbrec_load_balancer_group *nbrec_lb_group,
+                    const struct hmap *lbs)
+{
+    struct ovn_lb_group *lb_group = xzalloc(sizeof *lb_group);
+    lb_group->uuid = nbrec_lb_group->header_.uuid;
+    ovn_lb_group_init(lb_group, nbrec_lb_group, lbs);
     return lb_group;
 }
 
+static void
+ovn_lb_group_cleanup(struct ovn_lb_group *lb_group)
+{
+    ovn_lb_ip_set_destroy(lb_group->lb_ips);
+    lb_group->lb_ips = NULL;
+    free(lb_group->lbs);
+}
+
 void
 ovn_lb_group_destroy(struct ovn_lb_group *lb_group)
 {
@@ -783,11 +825,19 @@  ovn_lb_group_destroy(struct ovn_lb_group *lb_group)
         return;
     }
 
-    ovn_lb_ip_set_destroy(lb_group->lb_ips);
-    free(lb_group->lbs);
+    ovn_lb_group_cleanup(lb_group);
     free(lb_group);
 }
 
+void
+ovn_lb_group_reinit(struct ovn_lb_group *lb_group,
+                    const struct nbrec_load_balancer_group *nbrec_lb_group,
+                    const struct hmap *lbs)
+{
+    ovn_lb_group_cleanup(lb_group);
+    ovn_lb_group_init(lb_group, nbrec_lb_group, lbs);
+}
+
 struct ovn_lb_group *
 ovn_lb_group_find(const struct hmap *lb_groups, const struct uuid *uuid)
 {
diff --git a/lib/lb.h b/lib/lb.h
index 0339050cba..74905c73b7 100644
--- a/lib/lb.h
+++ b/lib/lb.h
@@ -77,6 +77,9 @@  struct ovn_northd_lb {
 
     struct sset ips_v4;
     struct sset ips_v6;
+
+    /* Indicates if the load balancer has health checks configured. */
+    bool health_checks;
 };
 
 struct ovn_lb_vip {
@@ -130,6 +133,8 @@  struct ovn_northd_lb *ovn_northd_lb_find(const struct hmap *,
                                          const struct uuid *);
 const struct smap *ovn_northd_lb_get_vips(const struct ovn_northd_lb *);
 void ovn_northd_lb_destroy(struct ovn_northd_lb *);
+void ovn_northd_lb_reinit(struct ovn_northd_lb *,
+                          const struct nbrec_load_balancer *);
 
 void build_lrouter_lb_ips(struct ovn_lb_ip_set *,
                           const struct ovn_northd_lb *);
@@ -148,6 +153,10 @@  struct ovn_lb_group *ovn_lb_group_create(
 void ovn_lb_group_destroy(struct ovn_lb_group *lb_group);
 struct ovn_lb_group *ovn_lb_group_find(const struct hmap *lb_groups,
                                        const struct uuid *);
+void ovn_lb_group_reinit(
+    struct ovn_lb_group *lb_group,
+    const struct nbrec_load_balancer_group *,
+    const struct hmap *lbs);
 
 struct ovn_lb_datapaths {
     struct hmap_node hmap_node;
diff --git a/northd/en-lb-data.c b/northd/en-lb-data.c
index 04d8d3e5d7..328c003675 100644
--- a/northd/en-lb-data.c
+++ b/northd/en-lb-data.c
@@ -19,6 +19,7 @@ 
 #include <stdio.h>
 
 /* OVS includes */
+#include "include/openvswitch/hmap.h"
 #include "openvswitch/util.h"
 #include "openvswitch/vlog.h"
 
@@ -38,7 +39,27 @@  static void lb_data_destroy(struct ed_type_lb_data *);
 static void build_lbs(const struct nbrec_load_balancer_table *,
                       const struct nbrec_load_balancer_group_table *,
                       struct hmap *lbs, struct hmap *lb_groups);
+static struct ovn_lb_group *create_lb_group(
+    const struct nbrec_load_balancer_group *, struct hmap *lbs,
+    struct hmap *lb_groups);
+static void destroy_tracked_data(struct ed_type_lb_data *);
+static void add_crupdated_lb_to_tracked_data(struct ovn_northd_lb *,
+                                                    struct tracked_lb_data *,
+                                                    bool health_checks);
+static void add_deleted_lb_to_tracked_data(struct ovn_northd_lb *,
+                                                  struct tracked_lb_data *,
+                                                  bool health_checks);
+static struct crupdated_lb_group *
+    add_crupdated_lb_group_to_tracked_data(struct ovn_lb_group *,
+                                           struct tracked_lb_data *);
+static void add_deleted_lb_group_to_tracked_data(
+    struct ovn_lb_group *, struct tracked_lb_data *);
 
+/* 'lb_data' engine node manages the NB load balancers and load balancer
+ * groups.  For each NB LB, it creates 'struct ovn_northd_lb' and
+ * for each NB LB group, it creates 'struct ovn_lb_group' and stores in
+ * the respective hmaps in it data (ed_type_lb_data).
+ */
 void *
 en_lb_data_init(struct engine_node *node OVS_UNUSED,
                        struct engine_arg *arg OVS_UNUSED)
@@ -60,6 +81,7 @@  en_lb_data_run(struct engine_node *node, void *data)
     const struct nbrec_load_balancer_group_table *nb_lbg_table =
         EN_OVSDB_GET(engine_get_input("NB_load_balancer_group", node));
 
+    lb_data->tracked = false;
     build_lbs(nb_lb_table, nb_lbg_table, &lb_data->lbs, &lb_data->lb_groups);
     engine_set_node_state(node, EN_UPDATED);
 }
@@ -71,12 +93,155 @@  en_lb_data_cleanup(void *data)
     lb_data_destroy(lb_data);
 }
 
+void
+en_lb_data_clear_tracked_data(void *data)
+{
+    struct ed_type_lb_data *lb_data = (struct ed_type_lb_data *) data;
+    destroy_tracked_data(lb_data);
+}
+
+
+/* Handler functions. */
+bool
+lb_data_load_balancer_handler(struct engine_node *node, void *data)
+{
+    const struct nbrec_load_balancer_table *nb_lb_table =
+        EN_OVSDB_GET(engine_get_input("NB_load_balancer", node));
+
+    struct ed_type_lb_data *lb_data = (struct ed_type_lb_data *) data;
+
+    lb_data->tracked = true;
+    struct tracked_lb_data *trk_lb_data = &lb_data->tracked_lb_data;
+
+    const struct nbrec_load_balancer *tracked_lb;
+    NBREC_LOAD_BALANCER_TABLE_FOR_EACH_TRACKED (tracked_lb, nb_lb_table) {
+        struct ovn_northd_lb *lb;
+        if (nbrec_load_balancer_is_new(tracked_lb)) {
+            /* New load balancer. */
+            lb = ovn_northd_lb_create(tracked_lb);
+            hmap_insert(&lb_data->lbs, &lb->hmap_node,
+                        uuid_hash(&tracked_lb->header_.uuid));
+            add_crupdated_lb_to_tracked_data(lb, trk_lb_data,
+                                             lb->health_checks);
+        } else if (nbrec_load_balancer_is_deleted(tracked_lb)) {
+            lb = ovn_northd_lb_find(&lb_data->lbs,
+                                    &tracked_lb->header_.uuid);
+            ovs_assert(lb);
+            hmap_remove(&lb_data->lbs, &lb->hmap_node);
+            add_deleted_lb_to_tracked_data(lb, trk_lb_data,
+                                           lb->health_checks);
+        } else {
+            /* Load balancer updated. */
+            lb = ovn_northd_lb_find(&lb_data->lbs,
+                                    &tracked_lb->header_.uuid);
+            ovs_assert(lb);
+            bool health_checks = lb->health_checks;
+            ovn_northd_lb_reinit(lb, tracked_lb);
+            health_checks |= lb->health_checks;
+            add_crupdated_lb_to_tracked_data(lb, trk_lb_data, health_checks);
+        }
+    }
+
+    engine_set_node_state(node, EN_UPDATED);
+    return true;
+}
+
+bool
+lb_data_load_balancer_group_handler(struct engine_node *node, void *data)
+{
+    struct ed_type_lb_data *lb_data = (struct ed_type_lb_data *) data;
+    const struct nbrec_load_balancer_group_table *nb_lbg_table =
+        EN_OVSDB_GET(engine_get_input("NB_load_balancer_group", node));
+
+    lb_data->tracked = true;
+    struct tracked_lb_data *trk_lb_data = &lb_data->tracked_lb_data;
+    const struct nbrec_load_balancer_group *tracked_lb_group;
+    NBREC_LOAD_BALANCER_GROUP_TABLE_FOR_EACH_TRACKED (tracked_lb_group,
+                                                      nb_lbg_table) {
+        if (nbrec_load_balancer_group_is_new(tracked_lb_group)) {
+            struct ovn_lb_group *lb_group =
+                create_lb_group(tracked_lb_group, &lb_data->lbs,
+                                &lb_data->lb_groups);
+            struct crupdated_lb_group *clbg =
+                add_crupdated_lb_group_to_tracked_data(lb_group, trk_lb_data);
+            for (size_t i = 0; i < lb_group->n_lbs; i++) {
+                hmapx_add(&clbg->assoc_lbs, lb_group->lbs[i]);
+            }
+        } else if (nbrec_load_balancer_group_is_deleted(tracked_lb_group)) {
+            struct ovn_lb_group *lb_group;
+            lb_group = ovn_lb_group_find(&lb_data->lb_groups,
+                                         &tracked_lb_group->header_.uuid);
+            ovs_assert(lb_group);
+            hmap_remove(&lb_data->lb_groups, &lb_group->hmap_node);
+            add_deleted_lb_group_to_tracked_data(lb_group, trk_lb_data);
+        } else {
+
+            struct ovn_lb_group *lb_group;
+            lb_group = ovn_lb_group_find(&lb_data->lb_groups,
+                                         &tracked_lb_group->header_.uuid);
+            ovs_assert(lb_group);
+
+            /* Determine the lbs which are added or deleted for this
+             * lb group and add them to tracked data.
+             * Eg.  If an lb group lbg1 before the update had [lb1, lb2, lb3]
+             *      And in the update, lb2 was removed and lb4 got added, then
+             *      add lb2 and lb4 to the trk_lb_data->crupdated_lbs. */
+            struct hmapx pre_update_lbs = HMAPX_INITIALIZER(&pre_update_lbs);
+            for (size_t i = 0; i < lb_group->n_lbs; i++) {
+                hmapx_add(&pre_update_lbs, lb_group->lbs[i]);
+            }
+            ovn_lb_group_reinit(lb_group, tracked_lb_group, &lb_data->lbs);
+            for (size_t i = 0; i < lb_group->n_lbs; i++) {
+                build_lrouter_lb_ips(lb_group->lb_ips, lb_group->lbs[i]);
+            }
+
+            struct crupdated_lb_group *clbg =
+                add_crupdated_lb_group_to_tracked_data(lb_group, trk_lb_data);
+
+            for (size_t i = 0; i < lb_group->n_lbs; i++) {
+                struct ovn_northd_lb *lb = lb_group->lbs[i];
+                struct hmapx_node *hmapx_node = hmapx_find(&pre_update_lbs,
+                                                           lb);
+                if (!hmapx_node) {
+                    hmapx_add(&clbg->assoc_lbs, lb);
+                } else {
+                    hmapx_delete(&pre_update_lbs, hmapx_node);
+                }
+            }
+
+            struct hmapx_node *hmapx_node;
+            HMAPX_FOR_EACH_SAFE (hmapx_node, &pre_update_lbs) {
+                struct ovn_northd_lb *lb = hmapx_node->data;
+                /* Check if the pre updated lb is actually deleted or
+                 * just disassociated from the lb group. If it's just
+                 * disassociated, then set 'has_dissassoc_lbs_from_lb_grops' to
+                 * true.  Later if required we can add this 'lb' to an hmapx of
+                 * disassociated_lbs. */
+                if (!hmapx_find(&trk_lb_data->deleted_lbs, lb)) {
+                    trk_lb_data->has_dissassoc_lbs_from_lb_grops = true;
+                }
+                hmapx_delete(&pre_update_lbs, hmapx_node);
+            }
+            hmapx_destroy(&pre_update_lbs);
+        }
+    }
+
+    engine_set_node_state(node, EN_UPDATED);
+    return true;
+}
+
 /* static functions. */
 static void
 lb_data_init(struct ed_type_lb_data *lb_data)
 {
     hmap_init(&lb_data->lbs);
     hmap_init(&lb_data->lb_groups);
+
+    struct tracked_lb_data *trk_lb_data = &lb_data->tracked_lb_data;
+    hmap_init(&trk_lb_data->crupdated_lbs);
+    hmapx_init(&trk_lb_data->deleted_lbs);
+    hmap_init(&trk_lb_data->crupdated_lb_groups);
+    hmapx_init(&trk_lb_data->deleted_lb_groups);
 }
 
 static void
@@ -93,6 +258,12 @@  lb_data_destroy(struct ed_type_lb_data *lb_data)
         ovn_lb_group_destroy(lb_group);
     }
     hmap_destroy(&lb_data->lb_groups);
+
+    destroy_tracked_data(lb_data);
+    hmap_destroy(&lb_data->tracked_lb_data.crupdated_lbs);
+    hmapx_destroy(&lb_data->tracked_lb_data.deleted_lbs);
+    hmapx_destroy(&lb_data->tracked_lb_data.deleted_lb_groups);
+    hmap_destroy(&lb_data->tracked_lb_data.crupdated_lb_groups);
 }
 
 static void
@@ -100,12 +271,9 @@  build_lbs(const struct nbrec_load_balancer_table *nbrec_load_balancer_table,
           const struct nbrec_load_balancer_group_table *nbrec_lb_group_table,
           struct hmap *lbs, struct hmap *lb_groups)
 {
-    struct ovn_lb_group *lb_group;
-    struct ovn_northd_lb *lb_nb;
-
     const struct nbrec_load_balancer *nbrec_lb;
     NBREC_LOAD_BALANCER_TABLE_FOR_EACH (nbrec_lb, nbrec_load_balancer_table) {
-        lb_nb = ovn_northd_lb_create(nbrec_lb);
+        struct ovn_northd_lb *lb_nb = ovn_northd_lb_create(nbrec_lb);
         hmap_insert(lbs, &lb_nb->hmap_node,
                     uuid_hash(&nbrec_lb->header_.uuid));
     }
@@ -113,13 +281,98 @@  build_lbs(const struct nbrec_load_balancer_table *nbrec_load_balancer_table,
     const struct nbrec_load_balancer_group *nbrec_lb_group;
     NBREC_LOAD_BALANCER_GROUP_TABLE_FOR_EACH (nbrec_lb_group,
                                               nbrec_lb_group_table) {
-        lb_group = ovn_lb_group_create(nbrec_lb_group, lbs);
+        create_lb_group(nbrec_lb_group, lbs, lb_groups);
+    }
+}
 
-        for (size_t i = 0; i < lb_group->n_lbs; i++) {
-            build_lrouter_lb_ips(lb_group->lb_ips, lb_group->lbs[i]);
-        }
+static struct ovn_lb_group *
+create_lb_group(const struct nbrec_load_balancer_group *nbrec_lb_group,
+                struct hmap *lbs, struct hmap *lb_groups)
+{
+    struct ovn_lb_group *lb_group = ovn_lb_group_create(nbrec_lb_group, lbs);
 
-        hmap_insert(lb_groups, &lb_group->hmap_node,
-                    uuid_hash(&lb_group->uuid));
+    for (size_t i = 0; i < lb_group->n_lbs; i++) {
+        build_lrouter_lb_ips(lb_group->lb_ips, lb_group->lbs[i]);
     }
+
+    hmap_insert(lb_groups, &lb_group->hmap_node,
+                uuid_hash(&lb_group->uuid));
+
+    return lb_group;
+}
+
+static void
+destroy_tracked_data(struct ed_type_lb_data *lb_data)
+{
+    lb_data->tracked = false;
+    lb_data->tracked_lb_data.has_health_checks = false;
+    lb_data->tracked_lb_data.has_dissassoc_lbs_from_lb_grops = false;
+
+    struct hmapx_node *node;
+    HMAPX_FOR_EACH_SAFE (node, &lb_data->tracked_lb_data.deleted_lbs) {
+        ovn_northd_lb_destroy(node->data);
+        hmapx_delete(&lb_data->tracked_lb_data.deleted_lbs, node);
+    }
+
+    HMAPX_FOR_EACH_SAFE (node, &lb_data->tracked_lb_data.deleted_lb_groups) {
+        ovn_lb_group_destroy(node->data);
+        hmapx_delete(&lb_data->tracked_lb_data.deleted_lb_groups, node);
+    }
+
+    struct crupdated_lb *clb;
+    HMAP_FOR_EACH_POP (clb, hmap_node,
+                       &lb_data->tracked_lb_data.crupdated_lbs) {
+        free(clb);
+    }
+
+    struct crupdated_lb_group *crupdated_lbg;
+    HMAP_FOR_EACH_POP (crupdated_lbg, hmap_node,
+                       &lb_data->tracked_lb_data.crupdated_lb_groups) {
+        hmapx_destroy(&crupdated_lbg->assoc_lbs);
+        free(crupdated_lbg);
+    }
+}
+
+static void
+add_crupdated_lb_to_tracked_data(struct ovn_northd_lb *lb,
+                                 struct tracked_lb_data *tracked_lb_data,
+                                 bool health_checks)
+{
+    struct crupdated_lb *clb = xzalloc(sizeof *clb);
+    clb->lb = lb;
+    hmap_insert(&tracked_lb_data->crupdated_lbs, &clb->hmap_node,
+                uuid_hash(&lb->nlb->header_.uuid));
+    if (health_checks) {
+        tracked_lb_data->has_health_checks = true;
+    }
+}
+
+static void
+add_deleted_lb_to_tracked_data(struct ovn_northd_lb *lb,
+                               struct tracked_lb_data *tracked_lb_data,
+                               bool health_checks)
+{
+    hmapx_add(&tracked_lb_data->deleted_lbs, lb);
+    if (health_checks) {
+        tracked_lb_data->has_health_checks = true;
+    }
+}
+
+static struct crupdated_lb_group *
+add_crupdated_lb_group_to_tracked_data(struct ovn_lb_group *lbg,
+                                       struct tracked_lb_data *tracked_lb_data)
+{
+    struct crupdated_lb_group *clbg = xzalloc(sizeof *clbg);
+    clbg->lbg = lbg;
+    hmapx_init(&clbg->assoc_lbs);
+    hmap_insert(&tracked_lb_data->crupdated_lb_groups, &clbg->hmap_node,
+                uuid_hash(&lbg->uuid));
+    return clbg;
+}
+
+static void
+add_deleted_lb_group_to_tracked_data(struct ovn_lb_group *lbg,
+                                     struct tracked_lb_data *tracked_lb_data)
+{
+    hmapx_add(&tracked_lb_data->deleted_lb_groups, lbg);
 }
diff --git a/northd/en-lb-data.h b/northd/en-lb-data.h
index 96fb8c1f8d..2e9a024620 100644
--- a/northd/en-lb-data.h
+++ b/northd/en-lb-data.h
@@ -4,9 +4,51 @@ 
 #include <config.h>
 
 #include "openvswitch/hmap.h"
+#include "include/openvswitch/list.h"
+#include "lib/hmapx.h"
 
 #include "lib/inc-proc-eng.h"
 
+struct ovn_northd_lb;
+struct ovn_lb_group;
+
+struct crupdated_lb {
+    struct hmap_node hmap_node;
+
+    struct ovn_northd_lb *lb;
+};
+
+struct crupdated_lb_group {
+    struct hmap_node hmap_node;
+
+    struct ovn_lb_group *lbg;
+    /* hmapx of newly associated lbs to this lb group.
+     * hmapx node is 'struct ovn_northd_lb *' */
+    struct hmapx assoc_lbs;
+};
+
+struct tracked_lb_data {
+    /* Both created and updated lbs. hmapx node is 'struct crupdated_lb *'. */
+    struct hmap crupdated_lbs;
+
+    /* Deleted lbs. */
+    struct hmapx deleted_lbs;
+
+    /* Both created and updated lb_groups. hmap node is
+     * 'struct crupdated_lb_group'. */
+    struct hmap crupdated_lb_groups;
+
+    /* Deleted lb_groups. hmapx node is  'struct ovn_lb_group *'. */
+    struct hmapx deleted_lb_groups;
+
+    /* Indicates if any of the tracked lb has health checks enabled. */
+    bool has_health_checks;
+
+    /* Indicates if any lb got disassociated from a lb group
+     * but not deleted. */
+    bool has_dissassoc_lbs_from_lb_grops;
+};
+
 /* struct which maintains the data of the engine node lb_data. */
 struct ed_type_lb_data {
     /* hmap of load balancers.  hmap node is 'struct ovn_northd_lb *' */
@@ -14,10 +56,18 @@  struct ed_type_lb_data {
 
     /* hmap of load balancer groups.  hmap node is 'struct ovn_lb_group *' */
     struct hmap lb_groups;
+
+    /* tracked data*/
+    bool tracked;
+    struct tracked_lb_data tracked_lb_data;
 };
 
 void *en_lb_data_init(struct engine_node *, struct engine_arg *);
 void en_lb_data_run(struct engine_node *, void *data);
 void en_lb_data_cleanup(void *data);
+void en_lb_data_clear_tracked_data(void *data);
+
+bool lb_data_load_balancer_handler(struct engine_node *, void *data);
+bool lb_data_load_balancer_group_handler(struct engine_node *, void *data);
 
 #endif /* end of EN_NORTHD_LB_DATA_H */
diff --git a/northd/en-northd.c b/northd/en-northd.c
index a0dae65752..d59ef062df 100644
--- a/northd/en-northd.c
+++ b/northd/en-northd.c
@@ -206,6 +206,47 @@  northd_sb_port_binding_handler(struct engine_node *node,
     return true;
 }
 
+bool
+northd_lb_data_handler(struct engine_node *node, void *data)
+{
+    struct ed_type_lb_data *lb_data = engine_get_input_data("lb_data", node);
+
+    if (!lb_data->tracked) {
+        return false;
+    }
+
+    if (lb_data->tracked_lb_data.has_health_checks) {
+        /* Fall back to recompute since a tracked load balancer
+         * has health checks configured as I-P is not yet supported
+         * for such load balancers. */
+        return false;
+    }
+
+    /* Fall back to recompute if any load balancer was dissociated from
+     * a load balancer group (but not deleted). */
+    if (lb_data->tracked_lb_data.has_dissassoc_lbs_from_lb_grops) {
+        return false;
+    }
+
+    /* Fall back to recompute if load balancer groups are deleted. */
+    if (!hmapx_is_empty(&lb_data->tracked_lb_data.deleted_lb_groups)) {
+        return false;
+    }
+
+    struct northd_data *nd = data;
+
+    if (!northd_handle_lb_data_changes(&lb_data->tracked_lb_data,
+                                       &nd->ls_datapaths,
+                                       &nd->lr_datapaths,
+                                       &nd->lb_datapaths_map,
+                                       &nd->lb_group_datapaths_map)) {
+        return false;
+    }
+
+    engine_set_node_state(node, EN_UPDATED);
+    return true;
+}
+
 void
 *en_northd_init(struct engine_node *node OVS_UNUSED,
                 struct engine_arg *arg OVS_UNUSED)
diff --git a/northd/en-northd.h b/northd/en-northd.h
index 20cc77f108..3c77b64bb2 100644
--- a/northd/en-northd.h
+++ b/northd/en-northd.h
@@ -17,5 +17,6 @@  void en_northd_clear_tracked_data(void *data);
 bool northd_nb_nb_global_handler(struct engine_node *, void *data OVS_UNUSED);
 bool northd_nb_logical_switch_handler(struct engine_node *, void *data);
 bool northd_sb_port_binding_handler(struct engine_node *, void *data);
+bool northd_lb_data_handler(struct engine_node *, void *data);
 
 #endif /* EN_NORTHD_H */
diff --git a/northd/inc-proc-northd.c b/northd/inc-proc-northd.c
index 0a99e87bc2..75d059645f 100644
--- a/northd/inc-proc-northd.c
+++ b/northd/inc-proc-northd.c
@@ -141,13 +141,18 @@  static ENGINE_NODE(sync_to_sb_addr_set, "sync_to_sb_addr_set");
 static ENGINE_NODE(fdb_aging, "fdb_aging");
 static ENGINE_NODE(fdb_aging_waker, "fdb_aging_waker");
 static ENGINE_NODE(sync_to_sb_lb, "sync_to_sb_lb");
-static ENGINE_NODE(lb_data, "lb_data");
+static ENGINE_NODE_WITH_CLEAR_TRACK_DATA(lb_data, "lb_data");
 
 void inc_proc_northd_init(struct ovsdb_idl_loop *nb,
                           struct ovsdb_idl_loop *sb)
 {
     /* Define relationships between nodes where first argument is dependent
      * on the second argument */
+    engine_add_input(&en_lb_data, &en_nb_load_balancer,
+                     lb_data_load_balancer_handler);
+    engine_add_input(&en_lb_data, &en_nb_load_balancer_group,
+                     lb_data_load_balancer_group_handler);
+
     engine_add_input(&en_northd, &en_nb_port_group, NULL);
     engine_add_input(&en_northd, &en_nb_acl, NULL);
     engine_add_input(&en_northd, &en_nb_logical_router, NULL);
@@ -175,13 +180,10 @@  void inc_proc_northd_init(struct ovsdb_idl_loop *nb,
                      northd_sb_port_binding_handler);
     engine_add_input(&en_northd, &en_nb_nb_global,
                      northd_nb_nb_global_handler);
+    engine_add_input(&en_northd, &en_lb_data, northd_lb_data_handler);
     engine_add_input(&en_northd, &en_nb_logical_switch,
                      northd_nb_logical_switch_handler);
 
-    engine_add_input(&en_lb_data, &en_nb_load_balancer, NULL);
-    engine_add_input(&en_lb_data, &en_nb_load_balancer_group, NULL);
-    engine_add_input(&en_northd, &en_lb_data, NULL);
-
     engine_add_input(&en_mac_binding_aging, &en_nb_nb_global, NULL);
     engine_add_input(&en_mac_binding_aging, &en_sb_mac_binding, NULL);
     engine_add_input(&en_mac_binding_aging, &en_northd, NULL);
diff --git a/northd/northd.c b/northd/northd.c
index 78b82950e4..e9afa07177 100644
--- a/northd/northd.c
+++ b/northd/northd.c
@@ -40,6 +40,7 @@ 
 #include "lib/lb.h"
 #include "memory.h"
 #include "northd.h"
+#include "en-lb-data.h"
 #include "lib/ovn-parallel-hmap.h"
 #include "ovn/actions.h"
 #include "ovn/features.h"
@@ -5362,6 +5363,64 @@  northd_handle_sb_port_binding_changes(
     return true;
 }
 
+/* Handler for lb_data engine changes.  For every tracked lb_data
+ * it creates or deletes the ovn_lb_datapaths/ovn_lb_group_datapaths
+ * from the lb_datapaths hmap and lb_group_datapaths hmap. */
+bool
+northd_handle_lb_data_changes(struct tracked_lb_data *trk_lb_data,
+                              struct ovn_datapaths *ls_datapaths,
+                              struct ovn_datapaths *lr_datapaths,
+                              struct hmap *lb_datapaths_map,
+                              struct hmap *lb_group_datapaths_map)
+{
+    struct ovn_lb_datapaths *lb_dps;
+    struct ovn_northd_lb *lb;
+    struct hmapx_node *hmapx_node;
+    HMAPX_FOR_EACH (hmapx_node, &trk_lb_data->deleted_lbs) {
+        lb = hmapx_node->data;
+        const struct uuid *lb_uuid = &lb->nlb->header_.uuid;
+
+        lb_dps = ovn_lb_datapaths_find(lb_datapaths_map, lb_uuid);
+        ovs_assert(lb_dps);
+        hmap_remove(lb_datapaths_map, &lb_dps->hmap_node);
+        ovn_lb_datapaths_destroy(lb_dps);
+    }
+
+    struct crupdated_lb *clb;
+    HMAP_FOR_EACH (clb, hmap_node, &trk_lb_data->crupdated_lbs) {
+        lb = clb->lb;
+        const struct uuid *lb_uuid = &lb->nlb->header_.uuid;
+
+        lb_dps = ovn_lb_datapaths_find(lb_datapaths_map, lb_uuid);
+        if (!lb_dps) {
+            lb_dps = ovn_lb_datapaths_create(lb, ods_size(ls_datapaths),
+                                             ods_size(lr_datapaths));
+            hmap_insert(lb_datapaths_map, &lb_dps->hmap_node,
+                        uuid_hash(lb_uuid));
+        }
+    }
+
+    struct ovn_lb_group_datapaths *lb_group_dps;
+    struct ovn_lb_group *lbg;
+    struct crupdated_lb_group *crupdated_lbg;
+    HMAP_FOR_EACH (crupdated_lbg, hmap_node,
+                   &trk_lb_data->crupdated_lb_groups) {
+        lbg = crupdated_lbg->lbg;
+        const struct uuid *lb_uuid = &lbg->uuid;
+
+        lb_group_dps = ovn_lb_group_datapaths_find(lb_group_datapaths_map,
+                                                   lb_uuid);
+        if (!lb_group_dps) {
+            lb_group_dps = ovn_lb_group_datapaths_create(
+                lbg, ods_size(ls_datapaths), ods_size(lr_datapaths));
+            hmap_insert(lb_group_datapaths_map, &lb_group_dps->hmap_node,
+                        uuid_hash(lb_uuid));
+        }
+    }
+
+    return true;
+}
+
 struct multicast_group {
     const char *name;
     uint16_t key;               /* OVN_MIN_MULTICAST...OVN_MAX_MULTICAST. */
diff --git a/northd/northd.h b/northd/northd.h
index 7d92028c7d..7d17921fa2 100644
--- a/northd/northd.h
+++ b/northd/northd.h
@@ -347,6 +347,13 @@  bool lflow_handle_northd_ls_changes(struct ovsdb_idl_txn *ovnsb_txn,
 bool northd_handle_sb_port_binding_changes(
     const struct sbrec_port_binding_table *, struct hmap *ls_ports);
 
+struct tracked_lb_data;
+bool northd_handle_lb_data_changes(struct tracked_lb_data *,
+                                   struct ovn_datapaths *ls_datapaths,
+                                   struct ovn_datapaths *lr_datapaths,
+                                   struct hmap *lb_datapaths_map,
+                                   struct hmap *lb_group_datapaths_map);
+
 void build_bfd_table(struct ovsdb_idl_txn *ovnsb_txn,
                      const struct nbrec_bfd_table *,
                      const struct sbrec_bfd_table *,
diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
index 9e36f96182..213fd51d77 100644
--- a/tests/ovn-northd.at
+++ b/tests/ovn-northd.at
@@ -9751,3 +9751,460 @@  AT_CHECK([grep "lr_in_gw_redirect" R1flows |sed s'/table=../table=??/' |sort], [
 
 AT_CLEANUP
 ])
+
+OVN_FOR_EACH_NORTHD_NO_HV([
+AT_SETUP([Load balancer incremental processing])
+ovn_start
+
+check_engine_stats() {
+  node=$1
+  recompute=$2
+  compute=$3
+
+  echo "__file__:__line__: Checking engine stats for node $node : recompute - \
+$recompute : compute - $compute"
+
+  node_stat=$(as northd ovn-appctl -t NORTHD_TYPE inc-engine/show-stats $node)
+  # node_stat will be of this format :
+  #     - Node: lflow - recompute: 3 - compute: 0 - abort: 0
+  node_recompute_ct=$(echo $node_stat | cut -d '-' -f2 | cut -d ':' -f2)
+  node_compute_ct=$(echo $node_stat | cut -d '-' -f3 | cut -d ':' -f2)
+
+  if [[ "$recompute" == "norecompute" ]]; then
+    # node should not be recomputed
+    echo "Expecting $node recompute count - $node_recompute_ct to be 0"
+    check test "$node_recompute_ct" -eq "0"
+  else
+    echo "Expecting $node recompute count - $node_recompute_ct not to be 0"
+    check test "$node_recompute_ct" -ne "0"
+  fi
+
+  if [[ "$compute" == "nocompute" ]]; then
+    # node should not be computed
+    echo "Expecting $node compute count - $node_compute_ct to be 0"
+    check test "$node_compute_ct" -eq "0"
+  else
+    echo "Expecting $node compute count - $node_compute_ct not to be 0"
+    check test "$node_compute_ct" -ne "0"
+  fi
+}
+
+# Test I-P for load balancers.
+# Presently ovn-northd handles I-P for NB LBs in northd_lb_data engine node
+# only.
+check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
+check ovn-nbctl --wait=sb lb-add lb1 10.0.0.10:80 10.0.0.3:80
+
+check_engine_stats lb_data norecompute compute
+check_engine_stats northd norecompute compute
+check_engine_stats lflow recompute nocompute
+
+CHECK_NO_CHANGE_AFTER_RECOMPUTE
+check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
+
+check ovn-nbctl --wait=sb set load_balancer . ip_port_mappings:10.0.0.3=sw0-p1:10.0.0.2
+check_engine_stats lb_data norecompute compute
+check_engine_stats northd norecompute compute
+check_engine_stats lflow recompute nocompute
+
+check ovn-nbctl --wait=sb set load_balancer . options:foo=bar
+check_engine_stats lb_data norecompute compute
+check_engine_stats northd norecompute compute
+check_engine_stats lflow recompute nocompute
+
+check ovn-nbctl --wait=sb -- lb-add lb2 20.0.0.10:80 20.0.0.20:80 -- lb-add lb3 30.0.0.10:80 30.0.0.20:80
+check_engine_stats lb_data norecompute compute
+check_engine_stats northd norecompute compute
+check_engine_stats lflow recompute nocompute
+
+CHECK_NO_CHANGE_AFTER_RECOMPUTE
+check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
+
+check ovn-nbctl --wait=sb -- lb-del lb2 -- lb-del lb3
+check_engine_stats lb_data norecompute compute
+check_engine_stats northd norecompute compute
+check_engine_stats lflow recompute nocompute
+
+CHECK_NO_CHANGE_AFTER_RECOMPUTE
+check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
+
+AT_CHECK([ovn-nbctl --wait=sb \
+          -- --id=@hc create Load_Balancer_Health_Check vip="10.0.0.10\:80" \
+             options:failure_count=100 \
+          -- add Load_Balancer . health_check @hc | uuidfilt], [0], [<0>
+])
+check_engine_stats lb_data norecompute compute
+check_engine_stats northd recompute nocompute
+check_engine_stats lflow recompute nocompute
+
+# Any change to load balancer health check should also result in full recompute
+# of northd node (but not northd_lb_data node)
+check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
+check ovn-nbctl --wait=sb set load_balancer_health_check . options:foo=bar1
+check_engine_stats lb_data norecompute compute
+check_engine_stats northd recompute nocompute
+check_engine_stats lflow recompute nocompute
+
+# Delete the health check from the load balancer.  northd engine node should do a full recompute.
+check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
+check ovn-nbctl --wait=sb clear Load_Balancer . health_check
+check_engine_stats lb_data norecompute compute
+check_engine_stats northd recompute nocompute
+check_engine_stats lflow recompute nocompute
+
+check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
+check ovn-nbctl ls-add sw0
+check ovn-nbctl --wait=sb lr-add lr0
+ovn-nbctl lrp-add lr0 lr0-sw0 00:00:00:00:ff:01 10.0.0.1/24
+ovn-nbctl lsp-add sw0 sw0-lr0
+ovn-nbctl lsp-set-type sw0-lr0 router
+ovn-nbctl lsp-set-addresses sw0-lr0 00:00:00:00:ff:01
+check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
+ovn-nbctl --wait=sb lsp-set-options sw0-lr0 router-port=lr0-sw0
+check_engine_stats lb_data norecompute nocompute
+check_engine_stats northd recompute nocompute
+check_engine_stats lflow recompute nocompute
+
+# Associate lb1 to sw0. There should be a full recompute of northd engine node
+check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
+check ovn-nbctl --wait=sb ls-lb-add sw0 lb1
+check_engine_stats lb_data norecompute nocompute
+check_engine_stats northd recompute nocompute
+check_engine_stats lflow recompute nocompute
+
+# Modify the backend of the lb1 vip
+check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
+check ovn-nbctl --wait=sb set load_balancer lb1 vips:'"10.0.0.10:80"'='"10.0.0.100:80"'
+check_engine_stats lb_data norecompute compute
+check_engine_stats northd recompute nocompute
+check_engine_stats lflow recompute nocompute
+CHECK_NO_CHANGE_AFTER_RECOMPUTE
+
+# Cleanup the vip of lb1.
+check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
+check ovn-nbctl --wait=sb clear load_Balancer lb1 vips
+check_engine_stats lb_data norecompute compute
+check_engine_stats northd recompute nocompute
+check_engine_stats lflow recompute nocompute
+CHECK_NO_CHANGE_AFTER_RECOMPUTE
+
+# Set the vips of lb1 back
+check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
+check ovn-nbctl --wait=sb lb-add lb1 10.0.0.10:80 10.0.0.3:80
+check_engine_stats lb_data norecompute compute
+check_engine_stats northd recompute nocompute
+check_engine_stats lflow recompute nocompute
+CHECK_NO_CHANGE_AFTER_RECOMPUTE
+
+# Add another vip to lb1
+check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
+check ovn-nbctl --wait=sb lb-add lb1 10.0.0.20:80 10.0.0.30:8080
+check_engine_stats lb_data norecompute compute
+check_engine_stats northd recompute nocompute
+check_engine_stats lflow recompute nocompute
+CHECK_NO_CHANGE_AFTER_RECOMPUTE
+
+# Disassociate lb1 from sw0. There should be a full recompute of northd engine node.
+check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
+check ovn-nbctl --wait=sb ls-lb-del sw0 lb1
+check_engine_stats lb_data norecompute nocompute
+check_engine_stats northd recompute nocompute
+check_engine_stats lflow recompute nocompute
+CHECK_NO_CHANGE_AFTER_RECOMPUTE
+
+# Add lb1 to lr0 and then disassociate
+check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
+check ovn-nbctl --wait=sb lr-lb-add lr0 lb1
+check_engine_stats lb_data norecompute nocompute
+check_engine_stats northd recompute nocompute
+check_engine_stats lflow recompute nocompute
+CHECK_NO_CHANGE_AFTER_RECOMPUTE
+
+# Modify the backend of the lb1 vip
+check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
+check ovn-nbctl --wait=sb set load_balancer lb1 vips:'"10.0.0.10:80"'='"10.0.0.100:80"'
+check_engine_stats lb_data norecompute compute
+check_engine_stats northd recompute nocompute
+check_engine_stats lflow recompute nocompute
+CHECK_NO_CHANGE_AFTER_RECOMPUTE
+
+# Cleanup the vip of lb1.
+check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
+check ovn-nbctl --wait=sb clear load_Balancer lb1 vips
+check_engine_stats lb_data norecompute compute
+check_engine_stats northd recompute nocompute
+check_engine_stats lflow recompute nocompute
+CHECK_NO_CHANGE_AFTER_RECOMPUTE
+
+# Set the vips of lb1 back
+check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
+check ovn-nbctl --wait=sb lb-add lb1 10.0.0.10:80 10.0.0.3:80
+check_engine_stats lb_data norecompute compute
+check_engine_stats northd recompute nocompute
+check_engine_stats lflow recompute nocompute
+CHECK_NO_CHANGE_AFTER_RECOMPUTE
+
+# Add another vip to lb1
+check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
+check ovn-nbctl --wait=sb lb-add lb1 10.0.0.20:80 10.0.0.30:8080
+check_engine_stats lb_data norecompute compute
+check_engine_stats northd recompute nocompute
+check_engine_stats lflow recompute nocompute
+CHECK_NO_CHANGE_AFTER_RECOMPUTE
+
+check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
+check ovn-nbctl --wait=sb lr-lb-del lr0 lb1
+check_engine_stats lb_data norecompute nocompute
+check_engine_stats northd recompute nocompute
+check_engine_stats lflow recompute nocompute
+CHECK_NO_CHANGE_AFTER_RECOMPUTE
+
+# Test load balancer group now
+check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
+lbg1_uuid=$(ovn-nbctl create load_balancer_group name=lbg1)
+check_engine_stats lb_data norecompute compute
+check_engine_stats northd norecompute compute
+check_engine_stats lflow recompute nocompute
+
+CHECK_NO_CHANGE_AFTER_RECOMPUTE
+check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
+
+lb1_uuid=$(fetch_column nb:Load_Balancer _uuid)
+
+# Add lb to the lbg1 group
+check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
+check ovn-nbctl add load_balancer_group . load_Balancer $lb1_uuid
+check_engine_stats lb_data norecompute compute
+check_engine_stats northd norecompute compute
+check_engine_stats lflow recompute nocompute
+
+check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
+check ovn-nbctl clear load_balancer_group . load_Balancer
+check_engine_stats lb_data norecompute compute
+check_engine_stats northd recompute nocompute
+check_engine_stats lflow recompute nocompute
+
+# Add back lb to the lbg1 group
+check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
+check ovn-nbctl add load_balancer_group . load_Balancer $lb1_uuid
+check_engine_stats lb_data norecompute compute
+check_engine_stats northd norecompute compute
+check_engine_stats lflow recompute nocompute
+
+CHECK_NO_CHANGE_AFTER_RECOMPUTE
+
+check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
+check ovn-nbctl add logical_switch sw0 load_balancer_group $lbg1_uuid
+check_engine_stats lb_data norecompute nocompute
+check_engine_stats northd recompute nocompute
+check_engine_stats lflow recompute nocompute
+
+# Update lb and this should result in recompute
+check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
+check ovn-nbctl --wait=sb set load_balancer . options:bar=foo
+check_engine_stats lb_data norecompute compute
+check_engine_stats northd recompute nocompute
+check_engine_stats lflow recompute nocompute
+
+# Modify the backend of the lb1 vip
+check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
+check ovn-nbctl --wait=sb set load_balancer lb1 vips:'"10.0.0.10:80"'='"10.0.0.100:80"'
+check_engine_stats lb_data norecompute compute
+check_engine_stats northd recompute nocompute
+check_engine_stats lflow recompute nocompute
+CHECK_NO_CHANGE_AFTER_RECOMPUTE
+
+# Cleanup the vip of lb1.
+check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
+check ovn-nbctl --wait=sb clear load_Balancer lb1 vips
+check_engine_stats lb_data norecompute compute
+check_engine_stats northd recompute nocompute
+check_engine_stats lflow recompute nocompute
+CHECK_NO_CHANGE_AFTER_RECOMPUTE
+
+# Set the vips of lb1 back
+check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
+check ovn-nbctl --wait=sb lb-add lb1 10.0.0.10:80 10.0.0.3:80
+check_engine_stats lb_data norecompute compute
+check_engine_stats northd recompute nocompute
+check_engine_stats lflow recompute nocompute
+CHECK_NO_CHANGE_AFTER_RECOMPUTE
+
+# Add another vip to lb1
+check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
+check ovn-nbctl --wait=sb lb-add lb1 10.0.0.20:80 10.0.0.30:8080
+check_engine_stats lb_data norecompute compute
+check_engine_stats northd recompute nocompute
+check_engine_stats lflow recompute nocompute
+CHECK_NO_CHANGE_AFTER_RECOMPUTE
+
+check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
+check ovn-nbctl clear logical_switch sw0 load_balancer_group
+check_engine_stats lb_data norecompute nocompute
+check_engine_stats northd recompute nocompute
+check_engine_stats lflow recompute nocompute
+
+check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
+check ovn-nbctl add logical_router lr0 load_balancer_group $lbg1_uuid
+check_engine_stats lb_data norecompute nocompute
+check_engine_stats northd recompute nocompute
+check_engine_stats lflow recompute nocompute
+CHECK_NO_CHANGE_AFTER_RECOMPUTE
+
+# Modify the backend of the lb1 vip
+check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
+check ovn-nbctl --wait=sb set load_balancer lb1 vips:'"10.0.0.10:80"'='"10.0.0.100:80"'
+check_engine_stats lb_data norecompute compute
+check_engine_stats northd recompute nocompute
+check_engine_stats lflow recompute nocompute
+CHECK_NO_CHANGE_AFTER_RECOMPUTE
+
+# Cleanup the vip of lb1.
+check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
+check ovn-nbctl --wait=sb clear load_Balancer lb1 vips
+check_engine_stats lb_data norecompute compute
+check_engine_stats northd recompute nocompute
+check_engine_stats lflow recompute nocompute
+CHECK_NO_CHANGE_AFTER_RECOMPUTE
+
+# Set the vips of lb1 back
+check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
+check ovn-nbctl --wait=sb lb-add lb1 10.0.0.10:80 10.0.0.3:80
+check_engine_stats lb_data norecompute compute
+check_engine_stats northd recompute nocompute
+check_engine_stats lflow recompute nocompute
+CHECK_NO_CHANGE_AFTER_RECOMPUTE
+
+# Add another vip to lb1
+check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
+check ovn-nbctl --wait=sb lb-add lb1 10.0.0.20:80 10.0.0.30:8080
+check_engine_stats lb_data norecompute compute
+check_engine_stats northd recompute nocompute
+check_engine_stats lflow recompute nocompute
+CHECK_NO_CHANGE_AFTER_RECOMPUTE
+
+check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
+check ovn-nbctl clear logical_router lr0 load_balancer_group
+check_engine_stats lb_data norecompute nocompute
+check_engine_stats northd recompute nocompute
+check_engine_stats lflow recompute nocompute
+
+# Add back lb group to logical switch and then delete it.
+check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
+check ovn-nbctl add logical_switch sw0 load_balancer_group $lbg1_uuid
+check_engine_stats lb_data norecompute nocompute
+check_engine_stats northd recompute nocompute
+check_engine_stats lflow recompute nocompute
+
+check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
+check ovn-nbctl clear logical_switch sw0 load_balancer_group -- \
+    destroy load_balancer_group $lbg1_uuid
+check_engine_stats lb_data norecompute compute
+check_engine_stats northd recompute nocompute
+check_engine_stats lflow recompute nocompute
+
+CHECK_NO_CHANGE_AFTER_RECOMPUTE
+
+# Test the scenario where a load balancer is associated to
+# a logical switch sw0 and also to a lb group lbg1 and lbg1
+# is also associated to the logical switch sw0 and logical
+# router lr1
+
+check ovn-nbctl lr-add lr1
+check ovn-nbctl lb-add lb2 20.0.0.20:80 30.0.0.30:8080
+check ovn-nbctl lb-add lb3 30.0.0.20:80 30.0.0.30:8080
+check ovn-nbctl --wait=sb lb-add lb4 40.0.0.20:80 30.0.0.30:8080
+
+lb2_uuid=$(fetch_column nb:Load_Balancer _uuid name=lb2)
+lb3_uuid=$(fetch_column nb:Load_Balancer _uuid name=lb3)
+lb4_uuid=$(fetch_column nb:Load_Balancer _uuid name=lb4)
+
+check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
+lbg1_uuid=$(ovn-nbctl create load_balancer_group name=lbg1)
+check_engine_stats lb_data norecompute compute
+check_engine_stats northd norecompute compute
+check_engine_stats lflow recompute nocompute
+
+check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
+check ovn-nbctl set load_balancer_group . load_balancer="$lb2_uuid,$lb3_uuid,$lb4_uuid"
+check_engine_stats lb_data norecompute compute
+check_engine_stats northd norecompute compute
+check_engine_stats lflow recompute nocompute
+
+check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
+check ovn-nbctl set logical_switch sw0 load_balancer_group=$lbg1_uuid
+check_engine_stats lb_data norecompute nocompute
+check_engine_stats northd recompute nocompute
+check_engine_stats lflow recompute nocompute
+CHECK_NO_CHANGE_AFTER_RECOMPUTE
+
+check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
+check ovn-nbctl set logical_router lr1 load_balancer_group=$lbg1_uuid
+check_engine_stats lb_data norecompute nocompute
+check_engine_stats northd recompute nocompute
+check_engine_stats lflow recompute nocompute
+CHECK_NO_CHANGE_AFTER_RECOMPUTE
+
+check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
+check ovn-nbctl --wait=sb ls-lb-add sw0 lb2
+check_engine_stats lb_data norecompute nocompute
+check_engine_stats northd recompute nocompute
+check_engine_stats lflow recompute nocompute
+CHECK_NO_CHANGE_AFTER_RECOMPUTE
+
+check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
+check ovn-nbctl --wait=sb ls-lb-add sw0 lb3
+check_engine_stats lb_data norecompute nocompute
+check_engine_stats northd recompute nocompute
+check_engine_stats lflow recompute nocompute
+CHECK_NO_CHANGE_AFTER_RECOMPUTE
+
+check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
+check ovn-nbctl --wait=sb lr-lb-add lr1 lb1
+check ovn-nbctl --wait=sb lr-lb-add lr1 lb2
+check_engine_stats lb_data norecompute nocompute
+check_engine_stats northd recompute nocompute
+check_engine_stats lflow recompute nocompute
+CHECK_NO_CHANGE_AFTER_RECOMPUTE
+
+check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
+check ovn-nbctl --wait=sb ls-lb-del sw0 lb2
+check_engine_stats lb_data norecompute nocompute
+check_engine_stats northd recompute nocompute
+check_engine_stats lflow recompute nocompute
+CHECK_NO_CHANGE_AFTER_RECOMPUTE
+
+check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
+check ovn-nbctl --wait=sb lr-lb-del lr1 lb2
+check_engine_stats lb_data norecompute nocompute
+check_engine_stats northd recompute nocompute
+check_engine_stats lflow recompute nocompute
+CHECK_NO_CHANGE_AFTER_RECOMPUTE
+
+# Deleting lb4 should not result in lflow recompute as it is
+# only associated with logical switch sw0.
+check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
+check ovn-nbctl --wait=sb lb-del lb4
+check_engine_stats lb_data norecompute compute
+check_engine_stats northd recompute nocompute
+check_engine_stats lflow recompute nocompute
+CHECK_NO_CHANGE_AFTER_RECOMPUTE
+
+# Deleting lb2 should result in lflow recompute as it is
+# associated with logical router lr1 through lb group.
+check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
+check ovn-nbctl --wait=sb lb-del lb2
+check_engine_stats lb_data norecompute compute
+check_engine_stats northd recompute nocompute
+check_engine_stats lflow recompute nocompute
+CHECK_NO_CHANGE_AFTER_RECOMPUTE
+
+check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
+check ovn-nbctl --wait=sb remove load_balancer_group . load_balancer $lb3_uuid
+check_engine_stats lb_data norecompute compute
+check_engine_stats northd recompute nocompute
+check_engine_stats lflow recompute nocompute
+CHECK_NO_CHANGE_AFTER_RECOMPUTE
+
+AT_CLEANUP
+])