Recheck-request: github-robot-_Build_and_Test

On Friday, June 12th, 2026 at 6:35 AM, Dmitrii Shcherbakov 
<[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 to use.
> 
> 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 0dbf17426..66b26e657 100644
> --- a/northd/northd.c
> +++ b/northd/northd.c
> @@ -3692,6 +3692,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 *od;
> +            VECTOR_FOR_EACH (&lb_group_dps->lr, od) {
> +                handle_od_lb_datapath_modes(od, lb_dps);
> +            }
> +
> +            VECTOR_FOR_EACH (&lb_group_dps->ls, od) {
> +                handle_od_lb_datapath_modes(od, lb_dps);
> +            }
>          }
>      }
>  }
> @@ -5647,13 +5656,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;
> +};
> +
> +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);
> +            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)
>  {
>      if (trk_lb_data->has_health_checks) {
>          /* Fall back to recompute since a tracked load balancer
> @@ -5695,6 +5839,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;
> @@ -5784,6 +5951,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);
> @@ -5823,6 +5991,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);
> @@ -5864,11 +6033,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);
> @@ -5888,6 +6059,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 74fb58848..b1168c207 100644
> --- a/northd/northd.h
> +++ b/northd/northd.h
> @@ -987,6 +987,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 f87b14c9a..51c5b486a 100644
> --- a/tests/ovn-northd.at
> +++ b/tests/ovn-northd.at
> @@ -19174,6 +19174,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

Reply via email to