On 3/26/25 11:30 AM, Mansi Sharma wrote:
> This change will be useful for migration cases, where it can be
> used to sync ct-entries before port is up on new chassis,
> resulting in reduced network package drops.
> It also fulfills the need of any other service which might need
> advance ct-zone reservation in future.
> 
> ---
> v10->v11
> 1. Added in details in news and ovn-controller.8.xml
> 2. Resolved requested changes in definition
>    declartions, and memory issues.
> v11->v12
> 1. Fixed xml file format issues.
> ---

Hi Mansi,

Sorry for the delay in reviews.

> 
> Signed-off-by: Mansi Sharma <mansi.sha...@nutanix.com>

Please add the signed-off-by before the "---".  git-am ignores
everything in between "---" and the actuall diff so it will
fail to add your sign-off to the commit.

> ---
>  NEWS                            |   2 +
>  controller/ct-zone.c            |  54 +++++++++++++--
>  controller/ct-zone.h            |   9 ++-
>  controller/ovn-controller.8.xml |  13 ++++
>  controller/ovn-controller.c     |  18 +++--
>  tests/ovn-controller.at         | 119 +++++++++++++++++++++++++++++---
>  6 files changed, 193 insertions(+), 22 deletions(-)
> 
> diff --git a/NEWS b/NEWS
> index 656176d20..169cccada 100644
> --- a/NEWS
> +++ b/NEWS
> @@ -4,6 +4,8 @@ Post v25.03.0
>       external-ids, this option allows to specify if ovn-controller should
>       perform cleanup when exiting. The "--restart" exit always has priority
>       to keep the backward compatibility.
> +   - Added support to reserve ct-zones for upcoming ports via setting
> +     external_ids:reserve_ct_zones option in Open_vSwitch table.
>  
>  OVN v25.03.0 - 07 Mar 2025
>  --------------------------
> diff --git a/controller/ct-zone.c b/controller/ct-zone.c
> index 469a8fc54..763837a54 100644
> --- a/controller/ct-zone.c
> +++ b/controller/ct-zone.c
> @@ -118,6 +118,37 @@ ct_zones_restore(struct ct_zone_ctx *ctx,
>      }
>  }
>  
> +void
> +ct_zones_reserved_lports(const struct ovsrec_open_vswitch_table *ovs_table,
> +                         struct sset * reserved_lports_set)

Nit, should be: "struct sset *reserved_lports_set".


> +{
> +    const struct ovsrec_open_vswitch *cfg;
> +    cfg = ovsrec_open_vswitch_table_first(ovs_table);
> +
> +    if (cfg == NULL) {
> +        return;
> +    }
> +
> +    const char *reserve_ct_zone_request_list = smap_get(
> +      &cfg->external_ids, "reserve_ct_zones");
> +

> +    if (reserve_ct_zone_request_list == NULL) {
> +        return;
> +    }


Nit: I'd rewrite this as:

const char *req_list = smap_get(&cfg->external_ids, "reserve_ct_zones");
if (req_list == NULL) {
    return;
}

> +
> +    char *dup_reserve_list = xstrdup(reserve_ct_zone_request_list);
> +    char *reserve_port;
> +    char *save_ptr = NULL;
> +
> +    for (reserve_port = strtok_r(dup_reserve_list, ",", &save_ptr);
> +         reserve_port != NULL;
> +         reserve_port = strtok_r(NULL, ",", &save_ptr)) {
> +            sset_add(reserved_lports_set, reserve_port);
> +    }
> +    free(reserve_port);
> +    free(dup_reserve_list);


Let's use the existing library functions.  This whole chunk can be
written as:

sset_from_delimited_string(reserved_lports_set, req_list, ",");

> +}
> +
>  void
>  ct_zones_parse_range(const struct ovsrec_open_vswitch_table *ovs_table,
>                       int *min_ct_zone, int *max_ct_zone)
> @@ -169,7 +200,8 @@ out:
>  void
>  ct_zones_update(const struct sset *local_lports,
>                  const struct ovsrec_open_vswitch_table *ovs_table,
> -                const struct hmap *local_datapaths, struct ct_zone_ctx *ctx)
> +                const struct hmap *local_datapaths, struct ct_zone_ctx *ctx,
> +                struct sset *reserved_lports)
>  {
>      int min_ct_zone, max_ct_zone;
>      const char *user;
> @@ -183,6 +215,13 @@ ct_zones_update(const struct sset *local_lports,
>          sset_add(&all_users, local_lport);
>      }
>  
> +    if (!sset_is_empty(reserved_lports)) {
> +        const char *reserved_lport;
> +        SSET_FOR_EACH (reserved_lport, reserved_lports) {
> +            sset_add(&all_users, reserved_lport);
> +        }
> +    }
> +
>      /* Local patched datapath (gateway routers) need zones assigned. */
>      const struct local_datapath *ld;
>      HMAP_FOR_EACH (ld, hmap_node, local_datapaths) {
> @@ -397,12 +436,15 @@ ct_zone_handle_dp_update(struct ct_zone_ctx *ctx,
>  
>  /* Returns "true" if there was an update to the context. */
>  bool
> -ct_zone_handle_port_update(struct ct_zone_ctx *ctx,
> -                           const struct sbrec_port_binding *pb,
> -                           bool updated, int *scan_start,
> -                           int min_ct_zone, int max_ct_zone)
> +ct_zone_handle_port_update(
> +    struct ct_zone_ctx *ctx,
> +    const struct sbrec_port_binding *pb,
> +    bool updated, int *scan_start,
> +    int min_ct_zone, int max_ct_zone,
> +    struct sset *reserved_lports)
>  {
>      struct shash_node *node = shash_find(&ctx->current, pb->logical_port);
> +    bool is_reserved = node && sset_contains(reserved_lports, node->name);
>  
>      if (node) {
>          struct ct_zone *ct_zone = node->data;
> @@ -419,6 +461,8 @@ ct_zone_handle_port_update(struct ct_zone_ctx *ctx,
>          }
>          ct_zone_limit_update(ctx, pb->logical_port, 
> ct_zone_get_pb_limit(pb));
>          return true;
> +    } else if (node && is_reserved) {
> +        return true;
>      } else if (node && ct_zone_remove(ctx, node->name)) {
>          return true;
>      }
> diff --git a/controller/ct-zone.h b/controller/ct-zone.h
> index 6df03975c..bf26a467d 100644
> --- a/controller/ct-zone.h
> +++ b/controller/ct-zone.h
> @@ -64,6 +64,9 @@ struct ct_zone_pending_entry {
>  
>  void ct_zone_ctx_init(struct ct_zone_ctx *ctx);
>  void ct_zone_ctx_destroy(struct ct_zone_ctx *ctx);
> +void ct_zones_reserved_lports(
> +    const struct ovsrec_open_vswitch_table *,
> +    struct sset *reserved_lports_set);
>  void ct_zones_parse_range(const struct ovsrec_open_vswitch_table *ovs_table,
>                            int *min_ct_zone, int *max_ct_zone);
>  void ct_zones_restore(struct ct_zone_ctx *ctx,
> @@ -73,7 +76,8 @@ void ct_zones_restore(struct ct_zone_ctx *ctx,
>  void ct_zones_update(const struct sset *local_lports,
>                       const struct ovsrec_open_vswitch_table *ovs_table,
>                       const struct hmap *local_datapaths,
> -                     struct ct_zone_ctx *ctx);
> +                     struct ct_zone_ctx *ctx,
> +                     struct sset *reserved_lports);
>  void ct_zones_commit(const struct ovsrec_bridge *br_int,
>                       const struct ovsrec_datapath *ovs_dp,
>                       struct ovsdb_idl_txn *ovs_idl_txn,
> @@ -85,7 +89,8 @@ bool ct_zone_handle_dp_update(struct ct_zone_ctx *ctx,
>  bool ct_zone_handle_port_update(struct ct_zone_ctx *ctx,
>                                  const struct sbrec_port_binding *pb,
>                                  bool updated, int *scan_start,
> -                                int min_ct_zone, int max_ct_zone);
> +                                int min_ct_zone, int max_ct_zone,
> +                                struct sset *reserved_lports);
>  uint16_t ct_zone_find_zone(const struct shash *ct_zones, const char *name);
>  void ct_zones_limits_sync(struct ct_zone_ctx *ctx,
>                            const struct hmap *local_datapaths,
> diff --git a/controller/ovn-controller.8.xml b/controller/ovn-controller.8.xml
> index 31f790875..6c9fb6314 100644
> --- a/controller/ovn-controller.8.xml
> +++ b/controller/ovn-controller.8.xml
> @@ -664,6 +664,19 @@
>            <code>external_ids:ovn-installed-ts</code>.
>          </p>
>        </dd>
> +
> +      <dt>
> +        <code>external-ids:reserve_ct_zones</code> in the <code>Bridge</code>
> +        table
> +      </dt>
> +
> +      <dd>
> +        <p>
> +          This key represents list of ports which are supposed to come up on
> +          the chassis, and hence need advance reservation of ct-zones.
> +          It is comma seprated list of port names.
> +        </p>
> +      </dd>
>      </dl>
>  
>      <h1>OVN Southbound Database Usage</h1>
> diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c
> index 0b09c98bd..70be0734e 100644
> --- a/controller/ovn-controller.c
> +++ b/controller/ovn-controller.c
> @@ -2368,10 +2368,13 @@ en_ct_zones_run(struct engine_node *node, void *data)
>              EN_OVSDB_GET(engine_get_input("SB_datapath_binding", node));
>  
>      const struct ovsrec_bridge *br_int = get_br_int(bridge_table, ovs_table);
> -
> +    struct sset reserved_lports = SSET_INITIALIZER(&reserved_lports);
> +    ct_zones_reserved_lports(ovs_table, &reserved_lports);
>      ct_zones_restore(&ct_zones_data->ctx, ovs_table, dp_table, br_int);
>      ct_zones_update(&rt_data->local_lports, ovs_table,
> -                    &rt_data->local_datapaths, &ct_zones_data->ctx);
> +                    &rt_data->local_datapaths, &ct_zones_data->ctx,
> +                    &reserved_lports);
> +    sset_destroy(&reserved_lports);
>      ct_zones_limits_sync(&ct_zones_data->ctx, &rt_data->local_datapaths,
>                           &rt_data->lbinding_data.lports);
>  
> @@ -2438,6 +2441,9 @@ ct_zones_runtime_data_handler(struct engine_node *node, 
> void *data)
>      ct_zones_parse_range(ovs_table, &min_ct_zone, &max_ct_zone);
>      scan_start = min_ct_zone;
>  
> +    struct sset reserved_lports = SSET_INITIALIZER(&reserved_lports);
> +    ct_zones_reserved_lports(ovs_table, &reserved_lports);
> +
>      HMAP_FOR_EACH (tdp, node, tracked_dp_bindings) {
>          if (tdp->tracked_type == TRACKED_RESOURCE_NEW) {
>              /* A new datapath has been added. Fall back to full recompute. */

The actual code returns here:

        if (tdp->tracked_type == TRACKED_RESOURCE_NEW) {
            /* A new datapath has been added. Fall back to full recompute. */
            return false;
        }

So we'll leak "reserved_lports".  This was caught by CI:
https://github.com/ovsrobot/ovn/actions/runs/14081256140/job/39434720061#step:10:5838

Please make sure you check CI results.  If you wish, you can get these
early by pushing to your own fork of the OVN repo on GitHub.

> @@ -2458,7 +2464,8 @@ ct_zones_runtime_data_handler(struct engine_node *node, 
> void *data)
>                      updated |= 
> ct_zone_handle_port_update(&ct_zones_data->ctx,
>                                                 t_lport->pb,
>                                                 false, &scan_start,
> -                                               min_ct_zone, max_ct_zone);
> +                                               min_ct_zone, max_ct_zone,
> +                                               &reserved_lports);
>                  }
>  
>                  continue;
> @@ -2470,10 +2477,13 @@ ct_zones_runtime_data_handler(struct engine_node 
> *node, void *data)
>              updated |= ct_zone_handle_port_update(&ct_zones_data->ctx,
>                                                    t_lport->pb,
>                                                    port_updated, &scan_start,
> -                                                  min_ct_zone, max_ct_zone);
> +                                                  min_ct_zone, max_ct_zone,
> +                                                  &reserved_lports);
>          }
>      }
>  
> +    sset_destroy(&reserved_lports);
> +
>      if (updated) {
>          engine_set_node_state(node, EN_UPDATED);
>      }
> diff --git a/tests/ovn-controller.at b/tests/ovn-controller.at
> index 2ebf879f1..94c75c6b6 100644
> --- a/tests/ovn-controller.at
> +++ b/tests/ovn-controller.at
> @@ -2633,7 +2633,7 @@ ovn_attach n1 br-phys 192.168.0.1
>  get_zone_num () {
>      output=$1
>      name=$2
> -    printf "$output" | grep $name | cut -d ' ' -f 2
> +    echo "$output" | grep $name | cut -d ' ' -f 2
>  }
>  
>  check_ovsdb_zone() {
> @@ -2643,8 +2643,14 @@ check_ovsdb_zone() {
>      test $ct_zone -eq $db_zone
>  }
>  
> +check_duplicates() {
> +    output=$1
> +    AT_CHECK([echo "$output" | tr ' ' '\n' | sort | uniq -d | grep .], [1])
> +}
> +
>  check ovs-vsctl add-port br-int ls0-hv1 -- set Interface ls0-hv1 
> external-ids:iface-id=ls0-hv1
>  check ovs-vsctl add-port br-int ls0-hv2 -- set Interface ls0-hv2 
> external-ids:iface-id=ls0-hv2
> +check ovs-vsctl set Open_vSwitch . 
> external_ids:reserve_ct_zones=ls0-req-hv3,ls0-req-hv4
>  
>  check ovn-nbctl lr-add lr0
>  
> @@ -2666,21 +2672,23 @@ check ovn-nbctl lrp-set-gateway-chassis lrp-gw hv1
>  check ovn-nbctl --wait=hv sync
>  
>  ct_zones=$(ovn-appctl -t ovn-controller ct-zone-list)
> -echo "$ct_zones"
> +
>  
>  port1_zone=$(get_zone_num "$ct_zones" ls0-hv1)
>  port2_zone=$(get_zone_num "$ct_zones" ls0-hv2)
> +req_port3_zone=$(get_zone_num "$ct_zones" ls0-req-hv3)
> +req_port4_zone=$(get_zone_num "$ct_zones" ls0-req-hv4)
>  
>  snat_zone=$(get_zone_num "$ct_zones" lr0_snat)
>  echo "snat_zone is $snat_zone"
>  
> -check test "$port1_zone" -ne "$port2_zone"
> -check test "$port2_zone" -ne "$snat_zone"
> -check test "$port1_zone" -ne "$snat_zone"
> -
>  OVS_WAIT_UNTIL([check_ovsdb_zone ls0-hv1 $port1_zone])
>  OVS_WAIT_UNTIL([check_ovsdb_zone ls0-hv2 $port2_zone])
>  OVS_WAIT_UNTIL([check_ovsdb_zone lr0_snat $snat_zone])
> +OVS_WAIT_UNTIL([check_ovsdb_zone ls0-req-hv3 $req_port3_zone])
> +OVS_WAIT_UNTIL([check_ovsdb_zone ls0-req-hv4 $req_port4_zone])
> +
> +check_duplicates "$ct_zones"
>  
>  # Now purposely request an SNAT zone for lr0 that conflicts with a zone
>  # currently assigned to a logical port
> @@ -2690,20 +2698,110 @@ check ovn-nbctl set Logical_Router lr0 
> options:snat-ct-zone=$snat_req_zone
>  check ovn-nbctl --wait=hv sync
>  
>  ct_zones=$(ovn-appctl -t ovn-controller ct-zone-list)
> -echo "$ct_zones"
>  
>  port1_zone=$(get_zone_num "$ct_zones" ls0-hv1)
>  port2_zone=$(get_zone_num "$ct_zones" ls0-hv2)
>  snat_zone=$(get_zone_num "$ct_zones" lr0_snat)
> +req_port3_zone=$(get_zone_num "$ct_zones" ls0-req-hv3)
> +req_port4_zone=$(get_zone_num "$ct_zones" ls0-req-hv4)
> +
>  
>  check test "$snat_zone" -eq "$snat_req_zone"
> -check test "$port1_zone" -ne "$port2_zone"
> -check test "$port2_zone" -ne "$snat_zone"
> -check test "$port1_zone" -ne "$snat_zone"
> +check_duplicates "$ct_zones"
> +
> +OVS_WAIT_UNTIL([check_ovsdb_zone ls0-hv1 $port1_zone])
> +OVS_WAIT_UNTIL([check_ovsdb_zone ls0-hv2 $port2_zone])
> +OVS_WAIT_UNTIL([check_ovsdb_zone lr0_snat $snat_zone])
> +OVS_WAIT_UNTIL([check_ovsdb_zone ls0-req-hv3 $req_port3_zone])
> +OVS_WAIT_UNTIL([check_ovsdb_zone ls0-req-hv4 $req_port4_zone])
> +
> +# Add port named ls0-req-hv3 and check if same zone assigned
> +# previously get assigned to it this time as well.
> +
> +check ovn-nbctl lsp-add ls0 ls0-req-hv3
> +check ovs-vsctl -- add-port br-int hv3-vif3 -- \
> +    set interface hv3-vif3 external-ids:iface-id=ls0-req-hv3
> +ct_zones=$(ovn-appctl -t ovn-controller ct-zone-list)
> +
> +check ovn-nbctl --wait=hv sync
> +
> +req_port3_zone_new=$(get_zone_num "$ct_zones" ls0-req-hv3)
> +check test "$req_port3_zone" -eq "$req_port3_zone_new"
> +check_duplicates "$ct_zones"
>  
>  OVS_WAIT_UNTIL([check_ovsdb_zone ls0-hv1 $port1_zone])
>  OVS_WAIT_UNTIL([check_ovsdb_zone ls0-hv2 $port2_zone])
>  OVS_WAIT_UNTIL([check_ovsdb_zone lr0_snat $snat_zone])
> +OVS_WAIT_UNTIL([check_ovsdb_zone ls0-req-hv3 $req_port3_zone])
> +OVS_WAIT_UNTIL([check_ovsdb_zone ls0-req-hv4 $req_port4_zone])
> +
> +AT_CHECK([ovs-vsctl get bridge br-int external_ids:ct-zone-ls0-req-hv3],
> +[0], [ignore])
> +
> +# Checks for two cases after removing entry from ovs_vswitch table -
> +# 1. If port is already up, ct-zone should be reserved.
> +# 2. If port is not up yet, ct-zone should not be reserved.
> +
> +check ovs-vsctl remove Open_vSwitch . external_ids reserve_ct_zones
> +
> +ct_zones=$(ovn-appctl -t ovn-controller ct-zone-list)
> +
> +req_port3_zone_after_delete=$(get_zone_num "$ct_zones" ls0-req-hv3)
> +req_port4_zone_after_delete=$(get_zone_num "$ct_zones" ls0-req-hv4)
> +
> +check test "$req_port3_zone_new" -eq "$req_port3_zone_after_delete"
> +
> +AT_CHECK([ovs-vsctl get bridge br-int external_ids:ct-zone-ls0-req-hv3],
> +[0], [ignore])
> +
> +AT_CHECK([ovs-vsctl get bridge br-int external_ids:ct-zone-ls0-req-hv4],
> +[1], [ignore], [ignore])
> +
> +# check test "$req_port4_zone_after_delete" == ""
> +
> +# Checks for case when a ct-zone is reserved it comes up on that chassis, and
> +# gets deleted, but its persisted in ovs_vswitch table, it should persist the
> +# same zone throughout.
> +
> +check ovs-vsctl set Open_vSwitch . external_ids:reserve_ct_zones=ls0-req-hv5
> +check ovn-nbctl lsp-add ls0 ls0-req-hv5
> +check ovs-vsctl -- add-port br-int hv5-vif5 -- \
> +    set interface hv5-vif5 external-ids:iface-id=ls0-req-hv5
> +ct_zones=$(ovn-appctl -t ovn-controller ct-zone-list)
> +req_port5_zone=$(get_zone_num "$ct_zones" ls0-req-hv5)
> +AT_CHECK([ovs-vsctl get bridge br-int external_ids:ct-zone-ls0-req-hv5],
> +[0], [ignore])
> +
> +check ovs-vsctl remove interface hv5-vif5 external_ids iface-id
> +ct_zones=$(ovn-appctl -t ovn-controller ct-zone-list)
> +req_port5_zone_new=$(get_zone_num "$ct_zones" ls0-req-hv5)
> +AT_CHECK([ovs-vsctl get bridge br-int external_ids:ct-zone-ls0-req-hv5],
> +[0], [ignore])
> +check test "$req_port5_zone" -eq "$req_port5_zone_new"
> +check_duplicates "$ct_zones"
> +
> +OVS_WAIT_UNTIL([check_ovsdb_zone ls0-hv1 $port1_zone])
> +OVS_WAIT_UNTIL([check_ovsdb_zone ls0-hv2 $port2_zone])
> +OVS_WAIT_UNTIL([check_ovsdb_zone lr0_snat $snat_zone])
> +OVS_WAIT_UNTIL([check_ovsdb_zone ls0-req-hv3 $req_port3_zone])
> +
> +check ovs-vsctl set Open_vSwitch . external_ids:reserve_ct_zones=ls0-req-hv6
> +check ovn-nbctl lsp-add ls0 ls0-req-hv6
> +check ovs-vsctl -- add-port br-int hv6-vif6 -- \
> +    set interface hv6-vif6 external-ids:iface-id=ls0-req-hv6
> +ct_zones=$(ovn-appctl -t ovn-controller ct-zone-list)
> +req_port6_zone=$(get_zone_num "$ct_zones" ls0-req-hv6)
> +
> +AT_CHECK([ovs-vsctl get bridge br-int external_ids:ct-zone-ls0-req-hv6],
> +[0], [ignore])
> +check ovn-nbctl --wait=hv lsp-del ls0-req-hv6
> +ct_zones=$(ovn-appctl -t ovn-controller ct-zone-list)
> +AT_CHECK([ovs-vsctl get bridge br-int external_ids:ct-zone-ls0-req-hv6],
> +[0], [ignore])
> +req_port6_zone_new=$(get_zone_num "$ct_zones" ls0-req-hv6)
> +check_duplicates "$ct_zones"
> +check test "$req_port6_zone" -eq "$req_port6_zone_new"
> +check_duplicates "$ct_zones"
>  
>  # Now create a conflict in the OVSDB and restart ovn-controller.
>  
> @@ -2713,7 +2811,6 @@ ovs-vsctl set bridge br-int 
> external_ids:ct-zone-ls0-hv2="$snat_req_zone"
>  ovn-appctl -t ovn-controller inc-engine/recompute
>  
>  ct_zones=$(ovn-appctl -t ovn-controller ct-zone-list)
> -echo "$ct_zones"
>  
>  port1_zone=$(get_zone_num "$ct_zones" ls0-hv1)
>  port2_zone=$(get_zone_num "$ct_zones" ls0-hv2)

Regards,
Dumitru

_______________________________________________
dev mailing list
d...@openvswitch.org
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to