On Thu, Jul 2, 2026 at 10:55 AM Dmitrii Shcherbakov via dev
<[email protected]> wrote:
>
> When a load balancer with options:distributed=true is attached to a
> logical router that has a chassis-redirect port, the incremental
> engine path in northd_handle_lb_data_changes calls
> handle_od_lb_datapath_modes, which sets od->is_distributed=true on
> that LR datapath. However, the LRP-level lr_in_admission flows
> generated by build_adm_ctrl_flows_for_lrouter_port check
> od_is_centralized(op->od) (== !od->is_distributed) via
> consider_l3dgw_port_is_centralized, and those flows are not rebuilt
> on LB-association changes. The result is that lr_in_admission keeps a
> stale is_chassis_resident("cr-lrp-X") guard on the gateway-LRP MAC
> match, so ingress traffic for the LB VIP landing on any chassis other
> than the cr-port host gets dropped at admission (priority-50/55
> flows).
>
> Toggling options:distributed=false then back to true masks the bug
> because the second update path in en_lb_data returns EN_UNHANDLED
> (lb_data_load_balancer_handler), forcing a full recompute which
> re-runs the LRP-level admission flow generator with the now-correct
> od->is_distributed.
>
> With this change, when has_distributed_lb is set,
> northd_handle_lb_data_changes collects a snapshot of each
> affected LR's is_distributed state before incremental
> processing, then after processing recomputes the flag per LR
> using the per-LR LB list (lr_lb_map) and the
> lb_group->has_distributed_lb bit. If any LR's flag changed,
> fall back to full recompute; otherwise the incremental path
> is safe.
>
> Also fix has_routable_lb and has_distributed_lb tracking in
> handle_od_lb_changes (guard prevented flags beyond the first LB) and
> add has_distributed_lb reset in destroy_tracked_data (replacing a
> duplicate has_health_checks reset).
>
> Reproducer:
>   ovn-nbctl lb-add lb0 VIP:80 BE1:8080,BE2:8080 tcp
>   ovn-nbctl set load_balancer lb0 options:distributed=true
>   ovn-nbctl lr-lb-add ROUTER_WITH_DGP lb0
>   ovn-nbctl ls-lb-add LS lb0
>
> Without this fix the priority-50/55 admission flows on the DGP LRP
> retain the is_chassis_resident("cr-lrp-...") guard and ingress LB
> traffic from non-gateway chassis is dropped.
>
> Fixes: 7b0eb4d9ed05 ("northd: Add distributed load balancer support.")
> Signed-off-by: Dmitrii Shcherbakov <[email protected]>
> ---
>  northd/en-lb-data.c     |  22 +--
>  northd/en-lb-data.h     |   3 +
>  northd/en-lr-stateful.c |   2 +
>  northd/en-northd.c      |   1 +
>  northd/lb.c             |   2 +
>  northd/lb.h             |   1 +
>  northd/northd.c         | 189 ++++++++++++++++++-
>  northd/northd.h         |   1 +
>  tests/ovn-northd.at     | 409 ++++++++++++++++++++++++++++++++++++++++
>  9 files changed, 613 insertions(+), 17 deletions(-)
>
> diff --git a/northd/en-lb-data.c b/northd/en-lb-data.c
> index 04ef20bcc..3bc2e0443 100644
> --- a/northd/en-lb-data.c
> +++ b/northd/en-lb-data.c
> @@ -45,8 +45,6 @@ static void build_lbs(const struct 
> nbrec_load_balancer_table *,
>  static void build_od_lb_map(const struct ovn_synced_logical_switch_map *,
>                              const struct ovn_synced_logical_router_map *,
>                              struct hmap *ls_lb_map, struct hmap *lr_lb_map);
> -static struct od_lb_data *find_od_lb_data(struct hmap *od_lb_map,
> -                                          const struct uuid *od_uuid);
>  static void destroy_od_lb_data(struct od_lb_data *od_lb_data);
>  static struct od_lb_data *create_od_lb_data(struct hmap *od_lb_map,
>                                              const struct uuid *od_uuid);
> @@ -270,6 +268,7 @@ lb_data_load_balancer_group_handler(struct engine_node 
> *node, void *data)
>
>              trk_lb_data->has_health_checks |= lb_group->has_health_checks;
>              trk_lb_data->has_routable_lb |= lb_group->has_routable_lb;
> +            trk_lb_data->has_distributed_lb |= lb_group->has_distributed_lb;
>              continue;
>          }
>
> @@ -286,6 +285,7 @@ lb_data_load_balancer_group_handler(struct engine_node 
> *node, void *data)
>              add_deleted_lbgrp_to_tracked_data(lb_group, trk_lb_data);
>              trk_lb_data->has_health_checks |= lb_group->has_health_checks;
>              trk_lb_data->has_routable_lb |= lb_group->has_routable_lb;
> +            trk_lb_data->has_distributed_lb |= lb_group->has_distributed_lb;
>          } else {
>              /* Determine the lbs which are added or deleted for this
>               * lb group and add them to tracked data.
> @@ -303,6 +303,7 @@ lb_data_load_balancer_group_handler(struct engine_node 
> *node, void *data)
>
>              trk_lb_data->has_health_checks |= lb_group->has_health_checks;
>              trk_lb_data->has_routable_lb |= lb_group->has_routable_lb;
> +            trk_lb_data->has_distributed_lb |= lb_group->has_distributed_lb;
>              struct crupdated_lbgrp *clbg =
>                  add_crupdated_lbgrp_to_tracked_data(lb_group, trk_lb_data);
>
> @@ -647,8 +648,8 @@ create_od_lb_data(struct hmap *od_lb_map, const struct 
> uuid *od_uuid)
>      return od_lb_data;
>  }
>
> -static struct od_lb_data *
> -find_od_lb_data(struct hmap *od_lb_map, const struct uuid *od_uuid)
> +struct od_lb_data *
> +find_od_lb_data(const struct hmap *od_lb_map, const struct uuid *od_uuid)
>  {
>      struct od_lb_data *od_lb_data;
>      HMAP_FOR_EACH_WITH_HASH (od_lb_data, hmap_node, uuid_hash(od_uuid),
> @@ -698,10 +699,8 @@ handle_od_lb_changes(struct nbrec_load_balancer 
> **nbrec_lbs,
>              ovs_assert(lb);
>
>              trk_lb_data->has_health_checks |= lb->health_checks;
> -            if (!trk_lb_data->has_routable_lb) {
> -                trk_lb_data->has_routable_lb |= lb->routable;
> -                trk_lb_data->has_distributed_lb |= lb->is_distributed;
> -            }
> +            trk_lb_data->has_routable_lb |= lb->routable;
> +            trk_lb_data->has_distributed_lb |= lb->is_distributed;
>          }
>
>          if (unode) {
> @@ -740,9 +739,8 @@ handle_od_lbgrp_changes(struct nbrec_load_balancer_group 
> **nbrec_lbgrps,
>              ovs_assert(lbgrp);
>
>              trk_lb_data->has_health_checks |= lbgrp->has_health_checks;
> -            if (!trk_lb_data->has_routable_lb) {
> -                trk_lb_data->has_routable_lb |= lbgrp->has_routable_lb;
> -            }
> +            trk_lb_data->has_routable_lb |= lbgrp->has_routable_lb;
> +            trk_lb_data->has_distributed_lb |= lbgrp->has_distributed_lb;
>          }
>      }
>
> @@ -762,8 +760,8 @@ destroy_tracked_data(struct ed_type_lb_data *lb_data)
>      lb_data->tracked_lb_data.has_dissassoc_lbs_from_lbgrps = false;
>      lb_data->tracked_lb_data.has_dissassoc_lbs_from_od = false;
>      lb_data->tracked_lb_data.has_dissassoc_lbgrps_from_od = false;
> -    lb_data->tracked_lb_data.has_health_checks = false;
>      lb_data->tracked_lb_data.has_routable_lb = false;
> +    lb_data->tracked_lb_data.has_distributed_lb = false;
>
>      struct hmapx_node *node;
>      HMAPX_FOR_EACH_SAFE (node, &lb_data->tracked_lb_data.deleted_lbs) {
> diff --git a/northd/en-lb-data.h b/northd/en-lb-data.h
> index 90e85b8c4..31d930995 100644
> --- a/northd/en-lb-data.h
> +++ b/northd/en-lb-data.h
> @@ -95,6 +95,9 @@ struct od_lb_data {
>      struct uuidset *lbgrps;
>  };
>
> +struct od_lb_data *find_od_lb_data(const struct hmap *od_lb_map,
> +                                   const struct uuid *od_uuid);
> +
>  /* 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 *' */
> diff --git a/northd/en-lr-stateful.c b/northd/en-lr-stateful.c
> index 695c1adab..f9f6586e5 100644
> --- a/northd/en-lr-stateful.c
> +++ b/northd/en-lr-stateful.c
> @@ -237,6 +237,8 @@ lr_stateful_lb_data_handler(struct engine_node *node, 
> void *data_)
>              }
>          }
>
> +        lr_stateful_rec->has_distributed_lb = od->is_distributed;
> +
>          /* Add the lr_stateful_rec rec to the tracking data. */
>          hmapx_add(&data->trk_data.crupdated, lr_stateful_rec);
>      }
> diff --git a/northd/en-northd.c b/northd/en-northd.c
> index c34818dba..cadcf7241 100644
> --- a/northd/en-northd.c
> +++ b/northd/en-northd.c
> @@ -229,6 +229,7 @@ northd_lb_data_handler(struct engine_node *node, void 
> *data)
>                                         &nd->lr_datapaths,
>                                         &nd->lb_datapaths_map,
>                                         &nd->lb_group_datapaths_map,
> +                                       &lb_data->lr_lb_map,
>                                         &nd->trk_data)) {
>          return EN_UNHANDLED;
>      }
> diff --git a/northd/lb.c b/northd/lb.c
> index fab122c9f..5ff9d1fad 100644
> --- a/northd/lb.c
> +++ b/northd/lb.c
> @@ -581,6 +581,7 @@ ovn_lb_group_init(struct ovn_lb_group *lb_group,
>          lb_group->lbs[i] = ovn_northd_lb_find(lbs, lb_uuid);
>          lb_group->has_health_checks |= lb_group->lbs[i]->health_checks;
>          lb_group->has_routable_lb |= lb_group->lbs[i]->routable;
> +        lb_group->has_distributed_lb |= lb_group->lbs[i]->is_distributed;
>      }
>  }
>
> @@ -604,6 +605,7 @@ ovn_lb_group_cleanup(struct ovn_lb_group *lb_group)
>      lb_group->lb_ips = NULL;
>      lb_group->has_health_checks = false;
>      lb_group->has_routable_lb = false;
> +    lb_group->has_distributed_lb = false;
>      free(lb_group->lbs);
>  }
>
> diff --git a/northd/lb.h b/northd/lb.h
> index db665b1d0..7a98c2f55 100644
> --- a/northd/lb.h
> +++ b/northd/lb.h
> @@ -137,6 +137,7 @@ struct ovn_lb_group {
>      struct ovn_lb_ip_set *lb_ips;
>      bool has_health_checks;
>      bool has_routable_lb;
> +    bool has_distributed_lb;
>  };
>
>  struct ovn_lb_group *ovn_lb_group_create(
> diff --git a/northd/northd.c b/northd/northd.c
> index cd613f690..0a0a7e425 100644
> --- a/northd/northd.c
> +++ b/northd/northd.c
> @@ -3727,6 +3727,15 @@ build_lb_datapaths(const struct hmap *lbs, const 
> struct hmap *lb_groups,
>              ovn_lb_datapaths_add_lr(lb_dps, vector_len(&lb_group_dps->lr),
>                                      vector_get_array(&lb_group_dps->lr),
>                                      ods_size(lr_datapaths));
> +
> +            struct ovn_datapath *grp_od;
> +            VECTOR_FOR_EACH (&lb_group_dps->lr, grp_od) {
> +                handle_od_lb_datapath_modes(grp_od, lb_dps);
> +            }
> +
> +            VECTOR_FOR_EACH (&lb_group_dps->ls, grp_od) {
> +                handle_od_lb_datapath_modes(grp_od, lb_dps);
> +            }
>          }
>      }
>  }
> @@ -5682,13 +5691,148 @@ northd_handle_sb_port_binding_changes(
>   *    the logical switch datapath is added to the load balancer (represented
>   *    by 'struct ovn_lb_datapaths') by calling ovn_lb_datapaths_add_ls().
>   * */
> +
> +struct lr_distributed_snapshot {
> +    struct hmap_node hmap_node;
> +    struct uuid lr_uuid;
> +    bool was_distributed;
> +};

I wasn't sure about the best place to put this comment, so I figured
right here below the definition of struct lr_distributed_snapshot
would work :)

I think the method of checking old vs. new is_distributed can be
simplified a bit. is_distributed is boolean, meaning that we only need
a single bit to indicate its truthiness. Logical routers all have a
unique integer index value that does not change during the period
where you are examining the is_distributed value.

So a simpler method would be to use a bitmap to track the snapshots.
You would start with a bitmap full of zeroes. Each LR that has
is_distributed set to "true" would have the bit corresponding to its
index set to 1. Then you recalculate all of the LR is_distributed
values. Iterate over each LR, and compare its current is_distributed
value to its bit in the bitmap. If the value is changed, then we need
to recompute so that the logical flows end up correct.

Doing it this way eliminates the need for the lr_distributed_snapshot
structure, eliminates many dynamic memory allocations, and likely
operates quicker.
> +
> +static void
> +lr_distributed_snapshot_insert_if_new(struct hmap *lr_snapshots,
> +                                      struct ovn_datapath *od)
> +{
> +    struct lr_distributed_snapshot *existing;
> +    HMAP_FOR_EACH_WITH_HASH (existing, hmap_node, uuid_hash(&od->key),
> +                             lr_snapshots) {
> +        if (uuid_equals(&existing->lr_uuid, &od->key)) {
> +            return;
> +        }
> +    }
> +    struct lr_distributed_snapshot *s = xmalloc(sizeof *s);
> +    s->lr_uuid = od->key;
> +    s->was_distributed = od->is_distributed;
> +    hmap_insert(lr_snapshots, &s->hmap_node, uuid_hash(&s->lr_uuid));
> +}
> +
> +static bool
> +lr_has_distributed_lb(const struct od_lb_data *lr_lb,
> +                      const struct hmap *lb_datapaths_map,
> +                      const struct hmap *lbgrp_datapaths_map)
> +{
> +    struct uuidset_node *un;
> +    UUIDSET_FOR_EACH (un, lr_lb->lbs) {
> +        struct ovn_lb_datapaths *lb_dps =
> +            ovn_lb_datapaths_find(lb_datapaths_map, &un->uuid);
> +        if (lb_dps && lb_dps->lb->is_distributed) {
> +            return true;
> +        }
> +    }
> +    UUIDSET_FOR_EACH (un, lr_lb->lbgrps) {
> +        struct ovn_lb_group_datapaths *lbgrp_dps =
> +            ovn_lb_group_datapaths_find(lbgrp_datapaths_map, &un->uuid);
> +        if (lbgrp_dps && lbgrp_dps->lb_group->has_distributed_lb) {
> +            return true;
> +        }
> +    }
> +    return false;
> +}
> +
> +static bool
> +lr_distributed_snapshot_check(struct hmap *lr_snapshots,
> +                               const struct hmap *lr_lb_map,
> +                               struct ovn_datapaths *lr_datapaths,
> +                               struct hmap *lb_datapaths_map,
> +                               struct hmap *lbgrp_datapaths_map)
> +{
> +    bool transition = false;
> +    struct lr_distributed_snapshot *snap;
> +    HMAP_FOR_EACH (snap, hmap_node, lr_snapshots) {
> +        struct ovn_datapath *snap_od = ovn_datapath_find_(
> +            &lr_datapaths->datapaths, &snap->lr_uuid);
> +        if (!snap_od) {
> +            continue;
> +        }
> +
> +        snap_od->is_distributed = false;
> +
> +        const struct od_lb_data *lr_lb =
> +            find_od_lb_data(lr_lb_map, &snap->lr_uuid);
> +        if (lr_lb) {
> +            snap_od->is_distributed = lr_has_distributed_lb(
> +                lr_lb, lb_datapaths_map, lbgrp_datapaths_map);
> +        }
> +
> +        if (snap_od->is_distributed != snap->was_distributed) {
> +            transition = true;
> +            break;
> +        }
> +    }
> +
> +    struct lr_distributed_snapshot *s, *next;
> +    HMAP_FOR_EACH_SAFE (s, next, hmap_node, lr_snapshots) {
> +        hmap_remove(lr_snapshots, &s->hmap_node);
> +        free(s);
> +    }
> +    hmap_destroy(lr_snapshots);
> +
> +    return transition;
> +}
> +
> +static void
> +lr_distributed_snapshot_collect(struct hmap *lr_snapshots,
> +                                struct tracked_lb_data *trk_lb_data,
> +                                struct ovn_datapaths *lr_datapaths,
> +                                struct hmap *lb_datapaths_map,
> +                                struct hmap *lbgrp_datapaths_map)
> +{
> +    struct crupdated_od_lb_data *codlb;
> +    LIST_FOR_EACH (codlb, list_node, &trk_lb_data->crupdated_lr_lbs) {
> +        struct ovn_datapath *od = ovn_datapath_find_(
> +            &lr_datapaths->datapaths, &codlb->od_uuid);
> +        ovs_assert(od);
> +        lr_distributed_snapshot_insert_if_new(lr_snapshots, od);
> +    }
> +
> +    struct crupdated_lb *clb;
> +    HMAP_FOR_EACH (clb, hmap_node, &trk_lb_data->crupdated_lbs) {
> +        struct ovn_lb_datapaths *lb_dps = ovn_lb_datapaths_find(
> +            lb_datapaths_map, &clb->lb->nlb->header_.uuid);
> +        if (!lb_dps) {
> +            continue;
> +        }
> +        size_t lr_idx;
> +        DYNAMIC_BITMAP_FOR_EACH_1 (lr_idx, &lb_dps->nb_lr_map) {
> +            struct ovn_datapath *lr_od =
> +                sparse_array_get(&lr_datapaths->dps, lr_idx);

It's a minor thing, but if you don't need the lr_idx for anything
other than getting the LR from the sparse_array, you can use
SPARSE_ARRAY_FOR_EACH() to iterate over all of the LRs in the
sparse_array.

> +            lr_distributed_snapshot_insert_if_new(lr_snapshots, lr_od);
> +        }
> +    }
> +
> +    struct crupdated_lbgrp *crupdated_lbgrp;
> +    HMAP_FOR_EACH (crupdated_lbgrp, hmap_node,
> +                   &trk_lb_data->crupdated_lbgrps) {
> +        struct ovn_lb_group_datapaths *lbgrp_dps =
> +            ovn_lb_group_datapaths_find(lbgrp_datapaths_map,
> +                                        &crupdated_lbgrp->lbgrp->uuid);
> +        if (!lbgrp_dps) {
> +            continue;
> +        }
> +        struct ovn_datapath *grp_od;
> +        VECTOR_FOR_EACH (&lbgrp_dps->lr, grp_od) {
> +            lr_distributed_snapshot_insert_if_new(lr_snapshots, grp_od);
> +        }
> +    }
> +}
> +
>  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 *lbgrp_datapaths_map,
> -                              struct northd_tracked_data *nd_changes)
> +                               struct ovn_datapaths *ls_datapaths,
> +                               struct ovn_datapaths *lr_datapaths,
> +                               struct hmap *lb_datapaths_map,
> +                               struct hmap *lbgrp_datapaths_map,
> +                               const struct hmap *lr_lb_map,
> +                               struct northd_tracked_data *nd_changes)

On my monitor it appears that the changes to indentation here are not
correct. It appears there was a single space removed from the
indentation.

>  {
>      if (trk_lb_data->has_health_checks) {
>          /* Fall back to recompute since a tracked load balancer
> @@ -5730,6 +5874,29 @@ northd_handle_lb_data_changes(struct tracked_lb_data 
> *trk_lb_data,
>          return false;
>      }
>
> +    /* If any deleted LB was distributed, fall back to recompute.
> +     * The deleted-lb path removes lb_dps from lb_datapaths_map before
> +     * the transition check runs, so the post-check would not see the
> +     * removed LB and could miss an is_distributed false transition. */
> +    if (trk_lb_data->has_distributed_lb
> +        && !hmapx_is_empty(&trk_lb_data->deleted_lbs)) {
> +        struct hmapx_node *hmapx_node;
> +        HMAPX_FOR_EACH (hmapx_node, &trk_lb_data->deleted_lbs) {
> +            struct ovn_northd_lb *lb = hmapx_node->data;
> +            if (lb->is_distributed) {
> +                return false;
> +            }
> +        }
> +    }
> +
> +    struct hmap lr_snapshots = HMAP_INITIALIZER(&lr_snapshots);
> +
> +    if (trk_lb_data->has_distributed_lb) {
> +        lr_distributed_snapshot_collect(&lr_snapshots, trk_lb_data,
> +                                       lr_datapaths, lb_datapaths_map,
> +                                       lbgrp_datapaths_map);
> +    }
> +
>      struct ovn_lb_datapaths *lb_dps;
>      struct ovn_northd_lb *lb;
>      struct ovn_datapath *od;
> @@ -5819,6 +5986,7 @@ northd_handle_lb_data_changes(struct tracked_lb_data 
> *trk_lb_data,
>                  ovs_assert(lb_dps);
>                  ovn_lb_datapaths_add_ls(lb_dps, 1, &od,
>                                          ods_size(ls_datapaths));
> +                handle_od_lb_datapath_modes(od, lb_dps);
>
>                  /* Add the lb to the northd tracked data. */
>                  hmapx_add(&nd_changes->trk_lbs.crupdated, lb_dps);
> @@ -5858,6 +6026,7 @@ northd_handle_lb_data_changes(struct tracked_lb_data 
> *trk_lb_data,
>                  ovs_assert(lb_dps);
>                  ovn_lb_datapaths_add_lr(lb_dps, 1, &od,
>                                          ods_size(lr_datapaths));
> +                handle_od_lb_datapath_modes(od, lb_dps);
>
>                  /* Add the lb to the northd tracked data. */
>                  hmapx_add(&nd_changes->trk_lbs.crupdated, lb_dps);
> @@ -5899,11 +6068,13 @@ northd_handle_lb_data_changes(struct tracked_lb_data 
> *trk_lb_data,
>              VECTOR_FOR_EACH (&lbgrp_dps->lr, od) {
>                  ovn_lb_datapaths_add_lr(lb_dps, 1, &od,
>                                          ods_size(lr_datapaths));
> +                handle_od_lb_datapath_modes(od, lb_dps);
>              }
>
>              VECTOR_FOR_EACH (&lbgrp_dps->ls, od) {
>                  ovn_lb_datapaths_add_ls(lb_dps, 1, &od,
>                                          ods_size(ls_datapaths));
> +                handle_od_lb_datapath_modes(od, lb_dps);
>
>                  /* Add the ls datapath to the northd tracked data. */
>                  hmapx_add(&nd_changes->ls_with_changed_lbs, od);
> @@ -5923,6 +6094,14 @@ northd_handle_lb_data_changes(struct tracked_lb_data 
> *trk_lb_data,
>          nd_changes->type |= NORTHD_TRACKED_LS_LBS;
>      }
>
> +    if (trk_lb_data->has_distributed_lb) {
> +        if (lr_distributed_snapshot_check(&lr_snapshots, lr_lb_map,
> +                                          lr_datapaths, lb_datapaths_map,
> +                                          lbgrp_datapaths_map)) {
> +            return false;
> +        }
> +    }
> +
>      return true;
>  }
>
> diff --git a/northd/northd.h b/northd/northd.h
> index bb86d3dac..c524bc2ae 100644
> --- a/northd/northd.h
> +++ b/northd/northd.h
> @@ -995,6 +995,7 @@ bool northd_handle_lb_data_changes(struct tracked_lb_data 
> *,
>                                     struct ovn_datapaths *lr_datapaths,
>                                     struct hmap *lb_datapaths_map,
>                                     struct hmap *lbgrp_datapaths_map,
> +                                   const struct hmap *lr_lb_map,
>                                     struct northd_tracked_data *);
>
>  void build_route_policies(struct ovn_datapath *, const struct hmap *,
> diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
> index 0c3d5ecfd..df5876d93 100644
> --- a/tests/ovn-northd.at
> +++ b/tests/ovn-northd.at
> @@ -19255,6 +19255,415 @@ OVN_CLEANUP_NORTHD
>  AT_CLEANUP
>  ])
>
> +OVN_FOR_EACH_NORTHD_NO_HV([
> +AT_SETUP([lr_in_admission cr-port guard on distributed LB attach/detach])
> +ovn_start
> +
> +check ovn-nbctl lr-add lr1
> +check ovn-nbctl lrp-add lr1 lrp1 02:ac:10:01:00:01 172.16.1.1/24
> +# Set gateway_mtu so build_gateway_mtu_flow() emits both the priority-50
> +# (unicast, with check_pkt_larger) and priority-55 (ARP) lr_in_admission
> +# flows on the LRP. Both code paths gate their is_chassis_resident
> +# guard on consider_l3dgw_port_is_centralized().
> +check ovn-nbctl set logical_router_port lrp1 options:gateway_mtu=1400
> +check ovn-nbctl ls-add ls1
> +check ovn-nbctl lsp-add-router-port ls1 ls1-lrp1 lrp1
> +check ovn-nbctl lrp-set-gateway-chassis lrp1 ch1
> +check ovn-nbctl --wait=sb sync
> +
> +# Create a distributed LB before attaching it to the router.
> +check ovn-nbctl lb-add lb1 1.1.1.1:80 192.168.0.1:8080
> +check ovn-nbctl set load_balancer lb1 options:distributed=true
> +
> +# --- Attach distributed LB to the DGP LR ---
> +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
> +check ovn-nbctl --wait=sb lr-lb-add lr1 lb1
> +
> +# The lb_data handler must handle this incrementally (setting
> +# has_distributed_lb in tracked data, not falling back to recompute).
> +check_engine_compute lb_data incremental
> +# northd recomputes on has_distributed_lb changes because LRP-level
> +# lr_in_admission flows need rebuilding.
> +check_engine_compute northd recompute
> +
> +ovn-sbctl lflow-list lr1 > lr1_lflows
> +
> +# Unicast (priority-50) and ARP (priority-55) admission flows on the
> +# DGP LRP must not be gated by is_chassis_resident("cr-lrp1") once a
> +# distributed LB is attached.
> +AT_CHECK([grep lr_in_admission lr1_lflows | grep -E 'priority=5[[05]]' | 
> grep '02:ac:10:01:00:01' | grep -v eth.mcast | ovn_strip_lflows], [0], [dnl
> +  table=??(lr_in_admission    ), priority=50   , match=(eth.dst == 
> 02:ac:10:01:00:01 && inport == "lrp1"), action=(reg9[[1]] = 
> check_pkt_larger(1414); xreg0[[0..47]] = 02:ac:10:01:00:01; next;)
> +  table=??(lr_in_admission    ), priority=55   , match=(eth.dst == 
> 02:ac:10:01:00:01 && inport == "lrp1" && (arp)), action=(xreg0[[0..47]] = 
> 02:ac:10:01:00:01; next;)
> +])
> +
> +# The cr-port's always-redirect option must be off, because
> +# has_distributed_lb on the LR's lr_stateful record is now true.
> +AT_CHECK([ovn-sbctl --bare --columns options find Port_Binding 
> logical_port=cr-lrp1 | tr ' ' '\n' | sort], [0], [dnl
> +distributed-port=lrp1
> +])
> +
> +# --- Detach distributed LB from the DGP LR ---
> +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
> +check ovn-nbctl --wait=sb lr-lb-del lr1 lb1
> +
> +check_engine_compute lb_data incremental
> +check_engine_compute northd recompute
> +
> +ovn-sbctl lflow-list lr1 > lr1_lflows
> +
> +# The admission flows must now be gated by is_chassis_resident("cr-lrp1")
> +# again, because no distributed LB is attached.
> +AT_CHECK([grep lr_in_admission lr1_lflows | grep -E 'priority=5[[05]]' | 
> grep '02:ac:10:01:00:01' | grep -v eth.mcast | ovn_strip_lflows], [0], [dnl
> +  table=??(lr_in_admission    ), priority=50   , match=(eth.dst == 
> 02:ac:10:01:00:01 && inport == "lrp1" && is_chassis_resident("cr-lrp1")), 
> action=(reg9[[1]] = check_pkt_larger(1414); xreg0[[0..47]] = 
> 02:ac:10:01:00:01; next;)
> +  table=??(lr_in_admission    ), priority=55   , match=(eth.dst == 
> 02:ac:10:01:00:01 && inport == "lrp1" && is_chassis_resident("cr-lrp1") && 
> (arp)), action=(xreg0[[0..47]] = 02:ac:10:01:00:01; next;)
> +])
> +
> +# The cr-port's always-redirect option must be back.
> +wait_row_count Port_Binding 1 logical_port=cr-lrp1 
> options:always-redirect="true"
> +
> +OVN_CLEANUP_NORTHD
> +AT_CLEANUP
> +])
> +
> +OVN_FOR_EACH_NORTHD_NO_HV([
> +AT_SETUP([lr_in_admission cr-port guard on distributed LB group 
> attach/detach])
> +ovn_start
> +
> +check ovn-nbctl lr-add lr1
> +check ovn-nbctl lrp-add lr1 lrp1 02:ac:10:01:00:01 172.16.1.1/24
> +check ovn-nbctl set logical_router_port lrp1 options:gateway_mtu=1400
> +check ovn-nbctl ls-add ls1
> +check ovn-nbctl lsp-add-router-port ls1 ls1-lrp1 lrp1
> +check ovn-nbctl lrp-set-gateway-chassis lrp1 ch1
> +check ovn-nbctl --wait=sb sync
> +
> +check ovn-nbctl lb-add lb1 1.1.1.1:80 192.168.0.1:8080
> +check ovn-nbctl set load_balancer lb1 options:distributed=true
> +lbg=$(ovn-nbctl create load_balancer_group name=lbg \
> +  -- add load_balancer_group lbg load_balancer $(fetch_column 
> nb:load_balancer _uuid name=lb1))
> +
> +# --- Attach distributed LB group to the DGP LR ---
> +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
> +check ovn-nbctl --wait=sb add logical_router lr1 load_balancer_group $lbg
> +
> +check_engine_compute lb_data incremental
> +check_engine_compute northd recompute
> +
> +ovn-sbctl lflow-list lr1 > lr1_lflows
> +
> +AT_CHECK([grep lr_in_admission lr1_lflows | grep -E 'priority=5[[05]]' | 
> grep '02:ac:10:01:00:01' | grep -v eth.mcast | ovn_strip_lflows], [0], [dnl
> +  table=??(lr_in_admission    ), priority=50   , match=(eth.dst == 
> 02:ac:10:01:00:01 && inport == "lrp1"), action=(reg9[[1]] = 
> check_pkt_larger(1414); xreg0[[0..47]] = 02:ac:10:01:00:01; next;)
> +  table=??(lr_in_admission    ), priority=55   , match=(eth.dst == 
> 02:ac:10:01:00:01 && inport == "lrp1" && (arp)), action=(xreg0[[0..47]] = 
> 02:ac:10:01:00:01; next;)
> +])
> +
> +AT_CHECK([ovn-sbctl --bare --columns options find Port_Binding 
> logical_port=cr-lrp1 | tr ' ' '\n' | sort], [0], [dnl
> +distributed-port=lrp1
> +])
> +
> +# --- Detach distributed LB group from the DGP LR ---
> +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
> +check ovn-nbctl --wait=sb remove logical_router lr1 load_balancer_group $lbg
> +
> +check_engine_compute lb_data incremental
> +check_engine_compute northd recompute
> +
> +ovn-sbctl lflow-list lr1 > lr1_lflows
> +
> +AT_CHECK([grep lr_in_admission lr1_lflows | grep -E 'priority=5[[05]]' | 
> grep '02:ac:10:01:00:01' | grep -v eth.mcast | ovn_strip_lflows], [0], [dnl
> +  table=??(lr_in_admission    ), priority=50   , match=(eth.dst == 
> 02:ac:10:01:00:01 && inport == "lrp1" && is_chassis_resident("cr-lrp1")), 
> action=(reg9[[1]] = check_pkt_larger(1414); xreg0[[0..47]] = 
> 02:ac:10:01:00:01; next;)
> +  table=??(lr_in_admission    ), priority=55   , match=(eth.dst == 
> 02:ac:10:01:00:01 && inport == "lrp1" && is_chassis_resident("cr-lrp1") && 
> (arp)), action=(xreg0[[0..47]] = 02:ac:10:01:00:01; next;)
> +])
> +
> +wait_row_count Port_Binding 1 logical_port=cr-lrp1 
> options:always-redirect="true"
> +
> +OVN_CLEANUP_NORTHD
> +AT_CLEANUP
> +])
> +
> +OVN_FOR_EACH_NORTHD_NO_HV([
> +AT_SETUP([lr_in_admission cr-port guard toggle on distributed LB option])
> +ovn_start
> +
> +check ovn-nbctl lr-add lr1
> +check ovn-nbctl lrp-add lr1 lrp1 02:ac:10:01:00:01 172.16.1.1/24
> +check ovn-nbctl set logical_router_port lrp1 options:gateway_mtu=1400
> +check ovn-nbctl ls-add ls1
> +check ovn-nbctl lsp-add-router-port ls1 ls1-lrp1 lrp1
> +check ovn-nbctl lrp-set-gateway-chassis lrp1 ch1
> +check ovn-nbctl lb-add lb1 1.1.1.1:80 192.168.0.1:8080
> +check ovn-nbctl --wait=sb lr-lb-add lr1 lb1
> +
> +# Toggle options:distributed on an already-attached LB.
> +
> +# --- distributed=true: admission guards must drop ---
> +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
> +check ovn-nbctl --wait=sb set load_balancer lb1 options:distributed=true
> +
> +check_engine_compute northd recompute
> +
> +ovn-sbctl lflow-list lr1 > lr1_lflows
> +AT_CHECK([grep lr_in_admission lr1_lflows | grep -E 'priority=5[[05]]' | 
> grep '02:ac:10:01:00:01' | grep -v eth.mcast | ovn_strip_lflows], [0], [dnl
> +  table=??(lr_in_admission    ), priority=50   , match=(eth.dst == 
> 02:ac:10:01:00:01 && inport == "lrp1"), action=(reg9[[1]] = 
> check_pkt_larger(1414); xreg0[[0..47]] = 02:ac:10:01:00:01; next;)
> +  table=??(lr_in_admission    ), priority=55   , match=(eth.dst == 
> 02:ac:10:01:00:01 && inport == "lrp1" && (arp)), action=(xreg0[[0..47]] = 
> 02:ac:10:01:00:01; next;)
> +])
> +
> +# cr-port options must drop always-redirect=true.
> +AT_CHECK([ovn-sbctl --bare --columns options find Port_Binding 
> logical_port=cr-lrp1 | tr ' ' '\n' | sort], [0], [dnl
> +distributed-port=lrp1
> +])
> +
> +# --- distributed=false: admission guards must return ---
> +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
> +check ovn-nbctl --wait=sb set load_balancer lb1 options:distributed=false
> +
> +check_engine_compute northd recompute
> +
> +ovn-sbctl lflow-list lr1 > lr1_lflows
> +AT_CHECK([grep lr_in_admission lr1_lflows | grep -E 'priority=5[[05]]' | 
> grep '02:ac:10:01:00:01' | grep -v eth.mcast | ovn_strip_lflows], [0], [dnl
> +  table=??(lr_in_admission    ), priority=50   , match=(eth.dst == 
> 02:ac:10:01:00:01 && inport == "lrp1" && is_chassis_resident("cr-lrp1")), 
> action=(reg9[[1]] = check_pkt_larger(1414); xreg0[[0..47]] = 
> 02:ac:10:01:00:01; next;)
> +  table=??(lr_in_admission    ), priority=55   , match=(eth.dst == 
> 02:ac:10:01:00:01 && inport == "lrp1" && is_chassis_resident("cr-lrp1") && 
> (arp)), action=(xreg0[[0..47]] = 02:ac:10:01:00:01; next;)
> +])
> +
> +# cr-port options must regain always-redirect=true.
> +wait_row_count Port_Binding 1 logical_port=cr-lrp1 
> options:always-redirect="true"
> +
> +# --- distributed=true again: admission guards must drop again ---
> +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
> +check ovn-nbctl --wait=sb set load_balancer lb1 options:distributed=true
> +
> +check_engine_compute northd recompute
> +
> +ovn-sbctl lflow-list lr1 > lr1_lflows
> +AT_CHECK([grep lr_in_admission lr1_lflows | grep -E 'priority=5[[05]]' | 
> grep '02:ac:10:01:00:01' | grep -v eth.mcast | ovn_strip_lflows], [0], [dnl
> +  table=??(lr_in_admission    ), priority=50   , match=(eth.dst == 
> 02:ac:10:01:00:01 && inport == "lrp1"), action=(reg9[[1]] = 
> check_pkt_larger(1414); xreg0[[0..47]] = 02:ac:10:01:00:01; next;)
> +  table=??(lr_in_admission    ), priority=55   , match=(eth.dst == 
> 02:ac:10:01:00:01 && inport == "lrp1" && (arp)), action=(xreg0[[0..47]] = 
> 02:ac:10:01:00:01; next;)
> +])
> +
> +AT_CHECK([ovn-sbctl --bare --columns options find Port_Binding 
> logical_port=cr-lrp1 | tr ' ' '\n' | sort], [0], [dnl
> +distributed-port=lrp1
> +])
> +
> +OVN_CLEANUP_NORTHD
> +AT_CLEANUP
> +])
> +
> +OVN_FOR_EACH_NORTHD_NO_HV([
> +AT_SETUP([lr_in_admission cr-port guard on LB group membership changes])
> +ovn_start
> +
> +check ovn-nbctl lr-add lr1
> +check ovn-nbctl lrp-add lr1 lrp1 02:ac:10:01:00:01 172.16.1.1/24
> +check ovn-nbctl set logical_router_port lrp1 options:gateway_mtu=1400
> +check ovn-nbctl ls-add ls1
> +check ovn-nbctl lsp-add-router-port ls1 ls1-lrp1 lrp1
> +check ovn-nbctl lrp-set-gateway-chassis lrp1 ch1
> +check ovn-nbctl --wait=sb sync
> +
> +check ovn-nbctl lb-add lb1 1.1.1.1:80 192.168.0.1:8080
> +check ovn-nbctl set load_balancer lb1 options:distributed=true
> +lbg=$(ovn-nbctl create load_balancer_group name=lbg)
> +
> +# Attach the empty group, then add the distributed LB to it.
> +check ovn-nbctl --wait=sb add logical_router lr1 load_balancer_group $lbg
> +
> +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
> +check ovn-nbctl --wait=sb add load_balancer_group $lbg load_balancer 
> $(fetch_column nb:load_balancer _uuid name=lb1)
> +
> +# Adding an LB to an attached group goes through
> +# lb_data_load_balancer_group_handler; the lb_data engine must handle
> +# it incrementally.
> +check_engine_compute lb_data incremental
> +check_engine_compute northd recompute
> +
> +ovn-sbctl lflow-list lr1 > lr1_lflows
> +AT_CHECK([grep lr_in_admission lr1_lflows | grep -E 'priority=5[[05]]' | 
> grep '02:ac:10:01:00:01' | grep -v eth.mcast | ovn_strip_lflows], [0], [dnl
> +  table=??(lr_in_admission    ), priority=50   , match=(eth.dst == 
> 02:ac:10:01:00:01 && inport == "lrp1"), action=(reg9[[1]] = 
> check_pkt_larger(1414); xreg0[[0..47]] = 02:ac:10:01:00:01; next;)
> +  table=??(lr_in_admission    ), priority=55   , match=(eth.dst == 
> 02:ac:10:01:00:01 && inport == "lrp1" && (arp)), action=(xreg0[[0..47]] = 
> 02:ac:10:01:00:01; next;)
> +])
> +
> +AT_CHECK([ovn-sbctl --bare --columns options find Port_Binding 
> logical_port=cr-lrp1 | tr ' ' '\n' | sort], [0], [dnl
> +distributed-port=lrp1
> +])
> +
> +# Remove the distributed LB from the group. The group is now empty,
> +# so is_distributed must flip back to false.
> +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
> +check ovn-nbctl --wait=sb remove load_balancer_group $lbg load_balancer 
> $(fetch_column nb:load_balancer _uuid name=lb1)
> +
> +check_engine_compute lb_data incremental
> +check_engine_compute northd recompute
> +
> +ovn-sbctl lflow-list lr1 > lr1_lflows
> +AT_CHECK([grep lr_in_admission lr1_lflows | grep -E 'priority=5[[05]]' | 
> grep '02:ac:10:01:00:01' | grep -v eth.mcast | ovn_strip_lflows], [0], [dnl
> +  table=??(lr_in_admission    ), priority=50   , match=(eth.dst == 
> 02:ac:10:01:00:01 && inport == "lrp1" && is_chassis_resident("cr-lrp1")), 
> action=(reg9[[1]] = check_pkt_larger(1414); xreg0[[0..47]] = 
> 02:ac:10:01:00:01; next;)
> +  table=??(lr_in_admission    ), priority=55   , match=(eth.dst == 
> 02:ac:10:01:00:01 && inport == "lrp1" && is_chassis_resident("cr-lrp1") && 
> (arp)), action=(xreg0[[0..47]] = 02:ac:10:01:00:01; next;)
> +])
> +
> +wait_row_count Port_Binding 1 logical_port=cr-lrp1 
> options:always-redirect="true"
> +
> +OVN_CLEANUP_NORTHD
> +AT_CLEANUP
> +])
> +
> +OVN_FOR_EACH_NORTHD_NO_HV([
> +AT_SETUP([lr_in_admission cr-port guard with multiple distributed LBs])
> +ovn_start
> +
> +check ovn-nbctl lr-add lr1
> +check ovn-nbctl lrp-add lr1 lrp1 02:ac:10:01:00:01 172.16.1.1/24
> +check ovn-nbctl set logical_router_port lrp1 options:gateway_mtu=1400
> +check ovn-nbctl ls-add ls1
> +check ovn-nbctl lsp-add-router-port ls1 ls1-lrp1 lrp1
> +check ovn-nbctl lrp-set-gateway-chassis lrp1 ch1
> +check ovn-nbctl --wait=sb sync
> +
> +check ovn-nbctl lb-add lb1 1.1.1.1:80 192.168.0.1:8080
> +check ovn-nbctl set load_balancer lb1 options:distributed=true
> +check ovn-nbctl lb-add lb2 2.2.2.2:80 192.168.0.2:8080
> +check ovn-nbctl set load_balancer lb2 options:distributed=true
> +check ovn-nbctl --wait=sb lr-lb-add lr1 lb1
> +check ovn-nbctl --wait=sb lr-lb-add lr1 lb2
> +
> +# Both distributed LBs are attached. Admission guards must be off.
> +ovn-sbctl lflow-list lr1 > lr1_lflows
> +AT_CHECK([grep lr_in_admission lr1_lflows | grep -E 'priority=5[[05]]' | 
> grep '02:ac:10:01:00:01' | grep -v eth.mcast | ovn_strip_lflows], [0], [dnl
> +  table=??(lr_in_admission    ), priority=50   , match=(eth.dst == 
> 02:ac:10:01:00:01 && inport == "lrp1"), action=(reg9[[1]] = 
> check_pkt_larger(1414); xreg0[[0..47]] = 02:ac:10:01:00:01; next;)
> +  table=??(lr_in_admission    ), priority=55   , match=(eth.dst == 
> 02:ac:10:01:00:01 && inport == "lrp1" && (arp)), action=(xreg0[[0..47]] = 
> 02:ac:10:01:00:01; next;)
> +])
> +
> +# Remove lb1. lb2 is still distributed, so is_distributed must remain
> +# true and admission guards must stay off.
> +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
> +check ovn-nbctl --wait=sb lr-lb-del lr1 lb1
> +
> +# Detach goes through the per-LB lb_data path; must stay incremental.
> +check_engine_compute lb_data incremental
> +check_engine_compute northd recompute
> +
> +ovn-sbctl lflow-list lr1 > lr1_lflows
> +AT_CHECK([grep lr_in_admission lr1_lflows | grep -E 'priority=5[[05]]' | 
> grep '02:ac:10:01:00:01' | grep -v eth.mcast | ovn_strip_lflows], [0], [dnl
> +  table=??(lr_in_admission    ), priority=50   , match=(eth.dst == 
> 02:ac:10:01:00:01 && inport == "lrp1"), action=(reg9[[1]] = 
> check_pkt_larger(1414); xreg0[[0..47]] = 02:ac:10:01:00:01; next;)
> +  table=??(lr_in_admission    ), priority=55   , match=(eth.dst == 
> 02:ac:10:01:00:01 && inport == "lrp1" && (arp)), action=(xreg0[[0..47]] = 
> 02:ac:10:01:00:01; next;)
> +])
> +
> +# lb2 still distributed -- cr-port options must keep always-redirect off.
> +AT_CHECK([ovn-sbctl --bare --columns options find Port_Binding 
> logical_port=cr-lrp1 | tr ' ' '\n' | sort], [0], [dnl
> +distributed-port=lrp1
> +])
> +
> +# Remove lb2. No distributed LBs remain, so is_distributed must flip
> +# back to false and admission guards must return.
> +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
> +check ovn-nbctl --wait=sb lr-lb-del lr1 lb2
> +
> +check_engine_compute lb_data incremental
> +check_engine_compute northd recompute
> +
> +ovn-sbctl lflow-list lr1 > lr1_lflows
> +AT_CHECK([grep lr_in_admission lr1_lflows | grep -E 'priority=5[[05]]' | 
> grep '02:ac:10:01:00:01' | grep -v eth.mcast | ovn_strip_lflows], [0], [dnl
> +  table=??(lr_in_admission    ), priority=50   , match=(eth.dst == 
> 02:ac:10:01:00:01 && inport == "lrp1" && is_chassis_resident("cr-lrp1")), 
> action=(reg9[[1]] = check_pkt_larger(1414); xreg0[[0..47]] = 
> 02:ac:10:01:00:01; next;)
> +  table=??(lr_in_admission    ), priority=55   , match=(eth.dst == 
> 02:ac:10:01:00:01 && inport == "lrp1" && is_chassis_resident("cr-lrp1") && 
> (arp)), action=(xreg0[[0..47]] = 02:ac:10:01:00:01; next;)
> +])
> +
> +wait_row_count Port_Binding 1 logical_port=cr-lrp1 
> options:always-redirect="true"
> +
> +OVN_CLEANUP_NORTHD
> +AT_CLEANUP
> +])
> +
> +OVN_FOR_EACH_NORTHD_NO_HV([
> +AT_SETUP([lr_in_admission cr-port guard incremental on subsequent 
> distributed LB])
> +ovn_start
> +
> +check ovn-nbctl lr-add lr1
> +check ovn-nbctl lrp-add lr1 lrp1 02:ac:10:01:00:01 172.16.1.1/24
> +check ovn-nbctl set logical_router_port lrp1 options:gateway_mtu=1400
> +check ovn-nbctl ls-add ls1
> +check ovn-nbctl lsp-add-router-port ls1 ls1-lrp1 lrp1
> +check ovn-nbctl lrp-set-gateway-chassis lrp1 ch1
> +check ovn-nbctl --wait=sb sync
> +
> +check ovn-nbctl lb-add lb1 1.1.1.1:80 192.168.0.1:8080
> +check ovn-nbctl set load_balancer lb1 options:distributed=true
> +check ovn-nbctl lb-add lb2 1.1.1.2:80 192.168.0.2:8080
> +check ovn-nbctl set load_balancer lb2 options:distributed=true
> +
> +# --- Attach lb1: is_distributed flips false->true; northd recompute. ---
> +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
> +check ovn-nbctl --wait=sb lr-lb-add lr1 lb1
> +
> +check_engine_compute lb_data incremental
> +check_engine_compute northd recompute
> +
> +# Admission guards must drop (no is_chassis_resident).
> +ovn-sbctl lflow-list lr1 > lr1_lflows
> +AT_CHECK([grep lr_in_admission lr1_lflows | grep -E 'priority=5[[05]]' | 
> grep '02:ac:10:01:00:01' | grep -v eth.mcast | ovn_strip_lflows], [0], [dnl
> +  table=??(lr_in_admission    ), priority=50   , match=(eth.dst == 
> 02:ac:10:01:00:01 && inport == "lrp1"), action=(reg9[[1]] = 
> check_pkt_larger(1414); xreg0[[0..47]] = 02:ac:10:01:00:01; next;)
> +  table=??(lr_in_admission    ), priority=55   , match=(eth.dst == 
> 02:ac:10:01:00:01 && inport == "lrp1" && (arp)), action=(xreg0[[0..47]] = 
> 02:ac:10:01:00:01; next;)
> +])
> +
> +# --- Attach lb2: is_distributed already true, no transition.
> +#     northd must stay incremental. ---
> +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
> +check ovn-nbctl --wait=sb lr-lb-add lr1 lb2
> +
> +check_engine_compute lb_data incremental
> +check_engine_compute northd incremental
> +
> +# Admission guards must still drop.
> +ovn-sbctl lflow-list lr1 > lr1_lflows
> +AT_CHECK([grep lr_in_admission lr1_lflows | grep -E 'priority=5[[05]]' | 
> grep '02:ac:10:01:00:01' | grep -v eth.mcast | ovn_strip_lflows], [0], [dnl
> +  table=??(lr_in_admission    ), priority=50   , match=(eth.dst == 
> 02:ac:10:01:00:01 && inport == "lrp1"), action=(reg9[[1]] = 
> check_pkt_larger(1414); xreg0[[0..47]] = 02:ac:10:01:00:01; next;)
> +  table=??(lr_in_admission    ), priority=55   , match=(eth.dst == 
> 02:ac:10:01:00:01 && inport == "lrp1" && (arp)), action=(xreg0[[0..47]] = 
> 02:ac:10:01:00:01; next;)
> +])
> +
> +OVN_CLEANUP_NORTHD
> +AT_CLEANUP
> +])
> +
> +OVN_FOR_EACH_NORTHD_NO_HV([
> +AT_SETUP([lr_in_admission cr-port guard on distributed LB delete])
> +ovn_start
> +
> +check ovn-nbctl lr-add lr1
> +check ovn-nbctl lrp-add lr1 lrp1 02:ac:10:01:00:01 172.16.1.1/24
> +check ovn-nbctl set logical_router_port lrp1 options:gateway_mtu=1400
> +check ovn-nbctl ls-add ls1
> +check ovn-nbctl lsp-add-router-port ls1 ls1-lrp1 lrp1
> +check ovn-nbctl lrp-set-gateway-chassis lrp1 ch1
> +check ovn-nbctl --wait=sb sync
> +
> +check ovn-nbctl lb-add lb1 1.1.1.1:80 192.168.0.1:8080
> +check ovn-nbctl set load_balancer lb1 options:distributed=true
> +check ovn-nbctl --wait=sb lr-lb-add lr1 lb1
> +
> +# Admission guards must drop (no is_chassis_resident).
> +ovn-sbctl lflow-list lr1 > lr1_lflows
> +AT_CHECK([grep lr_in_admission lr1_lflows | grep -E 'priority=5[[05]]' | 
> grep '02:ac:10:01:00:01' | grep -v eth.mcast | ovn_strip_lflows], [0], [dnl
> +  table=??(lr_in_admission    ), priority=50   , match=(eth.dst == 
> 02:ac:10:01:00:01 && inport == "lrp1"), action=(reg9[[1]] = 
> check_pkt_larger(1414); xreg0[[0..47]] = 02:ac:10:01:00:01; next;)
> +  table=??(lr_in_admission    ), priority=55   , match=(eth.dst == 
> 02:ac:10:01:00:01 && inport == "lrp1" && (arp)), action=(xreg0[[0..47]] = 
> 02:ac:10:01:00:01; next;)
> +])
> +
> +# Delete lb1 entirely. is_distributed must flip back to false,
> +# northd must recompute, and the cr-port guard must return.
> +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
> +check ovn-nbctl --wait=sb lb-del lb1
> +
> +check_engine_compute lb_data incremental
> +check_engine_compute northd recompute
> +
> +ovn-sbctl lflow-list lr1 > lr1_lflows
> +AT_CHECK([grep lr_in_admission lr1_lflows | grep -E 'priority=5[[05]]' | 
> grep '02:ac:10:01:00:01' | grep -v eth.mcast | ovn_strip_lflows], [0], [dnl
> +  table=??(lr_in_admission    ), priority=50   , match=(eth.dst == 
> 02:ac:10:01:00:01 && inport == "lrp1" && is_chassis_resident("cr-lrp1")), 
> action=(reg9[[1]] = check_pkt_larger(1414); xreg0[[0..47]] = 
> 02:ac:10:01:00:01; next;)
> +  table=??(lr_in_admission    ), priority=55   , match=(eth.dst == 
> 02:ac:10:01:00:01 && inport == "lrp1" && is_chassis_resident("cr-lrp1") && 
> (arp)), action=(xreg0[[0..47]] = 02:ac:10:01:00:01; next;)
> +])
> +
> +wait_row_count Port_Binding 1 logical_port=cr-lrp1 
> options:always-redirect="true"
> +
> +OVN_CLEANUP_NORTHD
> +AT_CLEANUP
> +])
> +
>  OVN_FOR_EACH_NORTHD_NO_HV([
>  AT_SETUP([ip_port_mappings validation: IPv6])
>  ovn_start
> --
> 2.53.0
>
>
> _______________________________________________
> dev mailing list
> [email protected]
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>

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

Reply via email to