Hi Paulo, just one note on this one :)

On Tue, Jul 21, 2026 at 1:45 PM Paulo Guilherme Silva
<[email protected]> wrote:
>
> Add the incremental change handlers for the en_ts node: an NB
> Logical_Switch change that is not a transit-switch mirror is a no-op, and
> transit-switch changes are reconciled per transit switch (scoped) instead
> of forcing a full recompute of every transit switch.
>
> Add a tests/ovn-ic.at test for the transit-switch handler scoping.

The commit mentions a test but it is not present in this patch.



>
> Assisted-by: Claude Opus 4.8, Claude Code
> Co-authored-by: Tiago Matos <[email protected]>
> Signed-off-by: Tiago Matos <[email protected]>
> Signed-off-by: Paulo Guilherme Silva <[email protected]>
> ---
>  ic/en-ts.c       | 158 +++++++++++++++++++++++++
>  ic/en-ts.h       |   9 ++
>  ic/inc-proc-ic.c |  31 ++++-
>  ic/ovn-ic.c      | 293 ++++++++++++++++++++++++++++++++---------------
>  ic/ovn-ic.h      |  12 +-
>  5 files changed, 401 insertions(+), 102 deletions(-)
>
> diff --git a/ic/en-ts.c b/ic/en-ts.c
> index 8745e6aa4..7bad858cb 100644
> --- a/ic/en-ts.c
> +++ b/ic/en-ts.c
> @@ -17,11 +17,26 @@
>  #include "en-dp-enum.h"
>  #include "en-ts.h"
>  #include "lib/inc-proc-eng.h"
> +#include "lib/ovn-ic-nb-idl.h"
> +#include "lib/ovn-ic-sb-idl.h"
> +#include "lib/ovn-nb-idl.h"
> +#include "openvswitch/shash.h"
>  #include "openvswitch/vlog.h"
> +#include "ovsdb-idl.h"
> +#include "smap.h"
> +#include "sset.h"
>  #include "ovn-ic.h"
>
>  VLOG_DEFINE_THIS_MODULE(en_ic_ts);
>
> +static void
> +ts_run(struct ic_context *ctx, struct hmap *dp_tnlids,
> +       struct shash *isb_ts_dps)
> +{
> +    ts_sync_scope(ctx, dp_tnlids, isb_ts_dps, NULL);
> +}
> +
> +
>  enum engine_node_state
>  en_ts_run(struct engine_node *node, void *data OVS_UNUSED)
>  {
> @@ -46,6 +61,149 @@ en_ts_run(struct engine_node *node, void *data OVS_UNUSED)
>      return EN_UPDATED;
>  }
>
> +/* Returns the ic_context, or NULL when there is no availability zone yet (in
> + * which case the caller should report EN_HANDLED_UNCHANGED). */
> +static struct ic_context *
> +ts_handler_ctx(void)
> +{
> +    struct ic_context *ctx = engine_get_context()->client_ctx;
> +    return ctx->runned_az ? ctx : NULL;
> +}
> +
> +/* Runs the scoped sync for the collected transit-switch scope and maps it to
> + * an engine result.  ts_sync_scope() gates each phase on the transactions it
> + * needs (ovnnb_txn for the NB mirror, the IC-SB lock for the datapath
> + * binding), exactly as the full recompute does, so the work for the affected
> + * switches can always be applied here. */
> +static enum engine_input_handler_result
> +ts_scope_finish(struct ic_context *ctx, struct ed_type_dp_enum *dp,
> +                struct sset *ts_scope)
> +{
> +    if (sset_is_empty(ts_scope)) {
> +        return EN_HANDLED_UNCHANGED;
> +    }
> +    ts_sync_scope(ctx, &dp->dp_tnlids, &dp->isb_ts_dps, ts_scope);
> +    return EN_HANDLED_UPDATED;
> +}
> +
> +/* IC-NB Transit_Switch: a new/deleted/renamed transit switch must have its 
> NB
> + * mirror and IC-SB datapath binding reconciled.  A deletion is honoured via
> + * the scoped GC in ts_sync_scope() (the deleted row's name stays in scope 
> but
> + * is absent from IC-NB, so its leftovers are removed). */
> +enum engine_input_handler_result
> +en_ts_icnb_transit_switch_handler(struct engine_node *node,
> +                                  void *data OVS_UNUSED)
> +{
> +    struct ic_context *ctx = ts_handler_ctx();
> +    if (!ctx) {
> +        return EN_HANDLED_UNCHANGED;
> +    }
> +
> +    struct ed_type_dp_enum *dp = engine_get_input_data("dp_enum", node);
> +    const struct icnbrec_transit_switch_table *tbl =
> +        EN_OVSDB_GET(engine_get_input("ICNB_transit_switch", node));
> +    struct sset ts_scope = SSET_INITIALIZER(&ts_scope);
> +    const struct icnbrec_transit_switch *ts;
> +    ICNBREC_TRANSIT_SWITCH_TABLE_FOR_EACH_TRACKED (ts, tbl) {
> +        sset_add(&ts_scope, ts->name);
> +    }
> +
> +    enum engine_input_handler_result ret = ts_scope_finish(ctx, dp, 
> &ts_scope);
> +    sset_destroy(&ts_scope);
> +    return ret;
> +}
> +
> +/* Only transit-switch mirror logical switches (other_config:interconn-ts)
> + * affect en_ts.  A change to such a logical switch reconciles that transit
> + * switch (re-creating the mirror if it was deleted externally); any other
> + * logical switch is irrelevant to en_ts, so its change is a no-op, avoiding 
> a
> + * recompute on unrelated NB Logical_Switch updates. */
> +enum engine_input_handler_result
> +en_ts_nb_logical_switch_handler(struct engine_node *node,
> +                                void *data OVS_UNUSED)
> +{
> +    struct ic_context *ctx = ts_handler_ctx();
> +    if (!ctx) {
> +        return EN_HANDLED_UNCHANGED;
> +    }
> +
> +    struct ed_type_dp_enum *dp = engine_get_input_data("dp_enum", node);
> +    const struct nbrec_logical_switch_table *tbl =
> +        EN_OVSDB_GET(engine_get_input("NB_logical_switch", node));
> +    struct sset ts_scope = SSET_INITIALIZER(&ts_scope);
> +    const struct nbrec_logical_switch *ls;
> +    NBREC_LOGICAL_SWITCH_TABLE_FOR_EACH_TRACKED (ls, tbl) {
> +        const char *ts_name = smap_get(&ls->other_config, "interconn-ts");
> +        if (ts_name) {
> +            sset_add(&ts_scope, ts_name);
> +        }
> +    }
> +
> +    enum engine_input_handler_result ret = ts_scope_finish(ctx, dp, 
> &ts_scope);
> +    sset_destroy(&ts_scope);
> +    return ret;
> +}
> +
> +/* IC-SB Datapath_Binding: when a transit switch's datapath tunnel key is
> + * (re)assigned, the NB Logical_Switch mirror's 
> other_config:requested-tnl-key
> + * must be updated to the committed value.  This is what synchronizes the key
> + * after a global tunnel-key refresh: an IC-NB vxlan_mode change makes en_ts
> + * reallocate the datapath key into the VXLAN range and write it to IC-SB, 
> but
> + * ts_sync_one() intentionally syncs the AZ NB mirror *before* that 
> (committed)
> + * key exists, so the NB value only catches up on a follow-up iteration.  The
> + * full recompute got that follow-up for free every poll; incrementally it is
> + * this handler, driven by the resulting IC-SB Datapath_Binding change.
> + *
> + * en_dp_enum (an upstream input) has already folded the new key into its
> + * datapath map by the time this runs, so re-syncing just the affected 
> transit
> + * switches propagates the committed key to NB.  Only transit-switch bindings
> + * have an NB mirror; transit routers (IC_ROUTER) are irrelevant.  Deletions
> + * are ignored: a transit switch removal is reconciled through
> + * en_ts_icnb_transit_switch_handler and the scoped GC in ts_sync_scope().
> + *
> + * Newly *inserted* bindings are also ignored, on purpose.  Creating a 
> transit
> + * switch's NB mirror is owned by en_ts_icnb_transit_switch_handler; the 
> leader
> + * inserts the IC-SB datapath binding in the very same iteration, so reacting
> + * to that insert here would run a second ts_sync_scope() for the same switch
> + * while the mirror the transit_switch handler just created is still
> + * uncommitted - find_ts_in_nb()'s index does not see the txn-local insert, 
> so
> + * a duplicate NB Logical_Switch would be created ("Multiple logical switches
> + * named ...").  The newly-created mirror's requested-tnl-key is instead 
> synced
> + * by the follow-up en_ts_nb_logical_switch_handler once the mirror is
> + * committed.  Here we only react to a tunnel-key *modify* on an
> + * already-existing binding (the vxlan refresh), whose mirror already exists
> + * and is found. */
> +enum engine_input_handler_result
> +en_ts_icsb_datapath_binding_handler(struct engine_node *node,
> +                                    void *data OVS_UNUSED)
> +{
> +    struct ic_context *ctx = ts_handler_ctx();
> +    if (!ctx) {
> +        return EN_HANDLED_UNCHANGED;
> +    }
> +
> +    struct ed_type_dp_enum *dp = engine_get_input_data("dp_enum", node);
> +    const struct icsbrec_datapath_binding_table *tbl =
> +        EN_OVSDB_GET(engine_get_input("ICSB_datapath_binding", node));
> +    struct sset ts_scope = SSET_INITIALIZER(&ts_scope);
> +    const struct icsbrec_datapath_binding *isb_dp;
> +    ICSBREC_DATAPATH_BINDING_TABLE_FOR_EACH_TRACKED (isb_dp, tbl) {
> +        if (icsbrec_datapath_binding_is_deleted(isb_dp) ||
> +            icsbrec_datapath_binding_is_new(isb_dp) ||
> +            ic_dp_get_type(isb_dp) != IC_SWITCH) {
> +            continue;
> +        }
> +        if (ovsdb_idl_track_is_updated(&isb_dp->header_,
> +                    &icsbrec_datapath_binding_col_tunnel_key)) {
> +            sset_add(&ts_scope, isb_dp->transit_switch);
> +        }
> +    }
> +
> +    enum engine_input_handler_result ret = ts_scope_finish(ctx, dp, 
> &ts_scope);
> +    sset_destroy(&ts_scope);
> +    return ret;
> +}
> +
>  void *
>  en_ts_init(struct engine_node *node OVS_UNUSED,
>             struct engine_arg *arg OVS_UNUSED)
> diff --git a/ic/en-ts.h b/ic/en-ts.h
> index 039b3196f..d1890205b 100644
> --- a/ic/en-ts.h
> +++ b/ic/en-ts.h
> @@ -9,4 +9,13 @@ enum engine_node_state en_ts_run(struct engine_node *node, 
> void *data);
>  void *en_ts_init(struct engine_node *node, struct engine_arg *arg);
>  void en_ts_cleanup(void *data);
>
> +enum engine_input_handler_result
> +en_ts_icnb_transit_switch_handler(struct engine_node *node, void *data);
> +
> +enum engine_input_handler_result
> +en_ts_nb_logical_switch_handler(struct engine_node *node, void *data);
> +
> +enum engine_input_handler_result
> +en_ts_icsb_datapath_binding_handler(struct engine_node *node, void *data);
> +
>  #endif /* EN_IC_TS_H */
> diff --git a/ic/inc-proc-ic.c b/ic/inc-proc-ic.c
> index 7b87ee0fc..3e0f87118 100644
> --- a/ic/inc-proc-ic.c
> +++ b/ic/inc-proc-ic.c
> @@ -249,12 +249,33 @@ void inc_proc_ic_init(struct ovsdb_idl_loop *nb,
>                       en_gateway_sb_chassis_handler);
>      engine_add_input(&en_gateway, &en_sb_encap, NULL);
>
> -    /* en_ts: sync transit switches to NB and IC-SB datapath bindings. */
> +    /* en_ts: sync transit switches to NB and IC-SB datapath bindings.
> +     *
> +     * en_dp_enum is an ordering dependency only: it owns the shared 
> tunnel-key
> +     * allocator (dp_tnlids) and transit-switch datapath map, both maintained
> +     * incrementally and read live by en_ts.  Because it recomputes en_ts's
> +     * datapath map in place, the en_dp_enum edge itself must not force a 
> full
> +     * en_ts recompute, so it uses a no-op handler; the resulting IC-SB
> +     * Datapath_Binding change is instead reacted to directly via
> +     * en_icsb_datapath_binding below, which re-syncs only the affected 
> transit
> +     * switches. This matters at scale (tens of thousands of transit 
> switches):
> +     * the alternative NULL edge would recompute every transit switch on any
> +     * datapath-binding change.
> +     *
> +     * en_icsb_datapath_binding drives the follow-up NB requested-tnl-key 
> sync
> +     * after a tunnel-key (re)assignment - notably the global refresh from an
> +     * IC-NB vxlan_mode change (see en_ts_icsb_datapath_binding_handler).  It
> +     * is ordered after en_dp_enum (which depends on the same table), so 
> en_ts
> +     * sees the freshly folded key. */
>      engine_add_input(&en_ts, &en_az, NULL);
> -    engine_add_input(&en_ts, &en_dp_enum, NULL);
> -    engine_add_input(&en_ts, &en_icnb_ic_nb_global, NULL);
> -    engine_add_input(&en_ts, &en_icnb_transit_switch, NULL);
> -    engine_add_input(&en_ts, &en_nb_logical_switch, NULL);
> +    engine_add_input(&en_ts, &en_dp_enum, engine_noop_handler);
> +    engine_add_input(&en_ts, &en_icsb_datapath_binding,
> +                     en_ts_icsb_datapath_binding_handler);
> +    engine_add_input(&en_ts, &en_icnb_ic_nb_global, en_ic_nb_global_handler);
> +    engine_add_input(&en_ts, &en_icnb_transit_switch,
> +                     en_ts_icnb_transit_switch_handler);
> +    engine_add_input(&en_ts, &en_nb_logical_switch,
> +                     en_ts_nb_logical_switch_handler);
>      engine_add_input(&en_ts, &en_icsb_encap, NULL);
>
>      /* en_tr: sync transit routers to NB and IC-SB datapath bindings. */
> diff --git a/ic/ovn-ic.c b/ic/ovn-ic.c
> index 887c4c30f..c1798b549 100644
> --- a/ic/ovn-ic.c
> +++ b/ic/ovn-ic.c
> @@ -70,6 +70,8 @@ static const char *ssl_ca_cert_file;
>
>  static const struct sbrec_port_binding * find_sb_pb_by_name(
>      struct ovsdb_idl_index *sbrec_port_binding_by_name, const char *name);
> +static const struct nbrec_logical_switch * find_ts_in_nb(
> +    struct ic_context *ctx, char *ts_name);
>
>
>  static void
> @@ -139,137 +141,238 @@ is_az_leader(struct ovsdb_idl_txn *txn)
>      return idl && ovsdb_idl_has_lock(idl);
>  }
>
> -void
> -ts_run(struct ic_context *ctx, struct hmap *dp_tnlids,
> -       struct shash *isb_ts_dps)
> +/* Returns true if transit-switch datapaths must use the VXLAN tunnel-key
> + * range: IC-NB requests vxlan_mode and the IC-SB actually has a VXLAN encap.
> + *
> + * Warning: ovnisb_unlocked should not be used to insert data on IC_SB which
> + * can cause a constraint violation, as an example, inserting data to IC-SB
> + * datapath_binding. */
> +static bool
> +ts_compute_vxlan_mode(struct ic_context *ctx)
>  {
> -    const struct icnbrec_transit_switch *ts;
> -    bool dp_key_refresh = false;
> -    bool vxlan_mode = false;
>      const struct icnbrec_ic_nb_global *ic_nb =
>          icnbrec_ic_nb_global_first(ctx->ovninb_idl);
>
> -    /*
> -     * Warning: ovnisb_unlocked should not be used to insert data on IC_SB
> -     * which can cause a constraint violation, as an example, inserting data 
> to
> -     * IC-SB datapath_binding.
> -     */
>      if (ic_nb && smap_get_bool(&ic_nb->options, "vxlan_mode", false)) {
>          const struct icsbrec_encap *encap;
>          ICSBREC_ENCAP_FOR_EACH (encap, ctx->ovnisb_unlocked_idl) {
>              if (!strcmp(encap->type, "vxlan")) {
> -                vxlan_mode = true;
> -                break;
> +                return true;
>              }
>          }
>      }
> +    return false;
> +}
>
> -    /* Sync INB TS to AZ NB */
> -    if (ctx->ovnnb_txn) {
> -        struct shash nb_tses = SHASH_INITIALIZER(&nb_tses);
> -        const struct nbrec_logical_switch *ls;
> -
> -        /* Get current NB Logical_Switch with other_config:interconn-ts */
> -        NBREC_LOGICAL_SWITCH_FOR_EACH (ls, ctx->ovnnb_idl) {
> -            const char *ts_name = smap_get(&ls->other_config, 
> "interconn-ts");
> -            if (ts_name) {
> -                shash_add(&nb_tses, ts_name, ls);
> -            }
> -        }
> +/* Reconciles a single transit switch 'ts'.  Phase 1 (gated by 'nb_gc' being
> + * non-NULL, i.e. an NB transaction is available) keeps its AZ NB
> + * Logical_Switch mirror in sync; Phase 2 (gated by 'leader') keeps its IC-SB
> + * Datapath_Binding in sync, allocating a tunnel key from 'dp_tnlids' when 
> one
> + * is missing.
> + *
> + * 'nb_gc' (keyed by transit-switch name) and 'isb_gc' (the datapath-binding
> + * map keyed by transit-switch name) double as garbage-collection sets: this
> + * function removes the entries it claims, so whatever remains after every
> + * in-scope switch has been reconciled is stale and deleted by the caller.
> + * Phase 1 runs before Phase 2 so that, for an already-committed binding,
> + * other_config:requested-tnl-key is derived from the committed IC-SB key
> + * rather than one that may still change (e.g. a vxlan-mode refresh).  The 
> one
> + * exception is a brand-new binding, whose freshly-allocated key Phase 2
> + * publishes to the mirror in this same iteration (see there): that key is
> + * stable and doing so avoids a datapath tunnel-key flap on every TS 
> creation.
> + * */
> +static void
> +ts_sync_one(struct ic_context *ctx, const struct icnbrec_transit_switch *ts,
> +            struct hmap *dp_tnlids, struct shash *isb_gc, struct shash 
> *nb_gc,
> +            bool vxlan_mode, bool leader)
> +{
> +    bool dp_key_refresh = false;
> +    const struct nbrec_logical_switch *ls = NULL;
>
> -        /* Create/update NB Logical_Switch for each TS */
> -        ICNBREC_TRANSIT_SWITCH_FOR_EACH (ts, ctx->ovninb_idl) {
> -            ls = shash_find_and_delete(&nb_tses, ts->name);
> -            if (!ls) {
> -                ls = nbrec_logical_switch_insert(ctx->ovnnb_txn);
> -                nbrec_logical_switch_set_name(ls, ts->name);
> -                nbrec_logical_switch_update_other_config_setkey(ls,
> -                                                                
> "interconn-ts",
> -                                                                ts->name);
> +    /* Phase 1: Sync INB TS to AZ NB. */
> +    if (nb_gc) {
> +        ls = shash_find_and_delete(nb_gc, ts->name);
> +        if (!ls) {
> +            ls = nbrec_logical_switch_insert(ctx->ovnnb_txn);
> +            nbrec_logical_switch_set_name(ls, ts->name);
> +            nbrec_logical_switch_update_other_config_setkey(ls, 
> "interconn-ts",
> +                                                            ts->name);
> +            nbrec_logical_switch_update_other_config_setkey(
> +                    ls, "ic-vxlan_mode", vxlan_mode ? "true" : "false");
> +        } else {
> +            bool _vxlan_mode = smap_get_bool(&ls->other_config,
> +                                             "ic-vxlan_mode", false);
> +            if (_vxlan_mode != vxlan_mode) {
> +                dp_key_refresh = true;
>                  nbrec_logical_switch_update_other_config_setkey(
>                          ls, "ic-vxlan_mode", vxlan_mode ? "true" : "false");
> -            } else {
> -                bool _vxlan_mode = smap_get_bool(&ls->other_config,
> -                                                 "ic-vxlan_mode", false);
> -                if (_vxlan_mode != vxlan_mode) {
> -                    dp_key_refresh = true;
> -                    nbrec_logical_switch_update_other_config_setkey(
> -                            ls, "ic-vxlan_mode",
> -                            vxlan_mode ? "true" : "false");
> -                }
> -            }
> -
> -            const struct icsbrec_datapath_binding *isb_dp;
> -            isb_dp = shash_find_data(isb_ts_dps, ts->name);
> -            if (isb_dp) {
> -                int64_t nb_tnl_key = smap_get_int(&ls->other_config,
> -                                                  "requested-tnl-key",
> -                                                  0);
> -                if (nb_tnl_key != isb_dp->tunnel_key) {
> -                    VLOG_DBG("Set other_config:requested-tnl-key %"PRId64
> -                             " for transit switch %s in NB.",
> -                             isb_dp->tunnel_key, ts->name);
> -                    char *tnl_key_str = xasprintf("%"PRId64,
> -                                                  isb_dp->tunnel_key);
> -                    nbrec_logical_switch_update_other_config_setkey(
> -                        ls, "requested-tnl-key", tnl_key_str);
> -                    free(tnl_key_str);
> -                }
>              }
>          }
>
> -        /* Delete extra NB Logical_Switch with other_config:interconn-ts */
> -        struct shash_node *node;
> -        SHASH_FOR_EACH (node, &nb_tses) {
> -            nbrec_logical_switch_delete(node->data);
> +        const struct icsbrec_datapath_binding *isb_dp =
> +            shash_find_data(isb_gc, ts->name);
> +        if (isb_dp) {
> +            int64_t nb_tnl_key = smap_get_int(&ls->other_config,
> +                                              "requested-tnl-key", 0);
> +            if (nb_tnl_key != isb_dp->tunnel_key) {
> +                VLOG_DBG("Set other_config:requested-tnl-key %"PRId64
> +                         " for transit switch %s in NB.",
> +                         isb_dp->tunnel_key, ts->name);
> +                char *tnl_key_str = xasprintf("%"PRId64, isb_dp->tunnel_key);
> +                nbrec_logical_switch_update_other_config_setkey(
> +                    ls, "requested-tnl-key", tnl_key_str);
> +                free(tnl_key_str);
> +            }
>          }
> -        shash_destroy(&nb_tses);
>      }
>
>      /* Sync TS between INB and ISB.  This is performed after syncing with AZ
>       * SB, to avoid uncommitted ISB datapath tunnel key to be synced back to
>       * AZ. */
> -    if (ctx->ovnisb_txn &&
> -        is_az_leader(ctx->ovnisb_txn)) {
> -        /* Create ISB Datapath_Binding */
> -        ICNBREC_TRANSIT_SWITCH_FOR_EACH (ts, ctx->ovninb_idl) {
> -            const struct icsbrec_datapath_binding *isb_dp =
> -                shash_find_and_delete(isb_ts_dps, ts->name);
> -            if (!isb_dp) {
> -                /* Allocate tunnel key */
> -                int64_t dp_key = allocate_dp_key(dp_tnlids, vxlan_mode,
> -                                                 "transit switch datapath");
> -                if (!dp_key) {
> -                    continue;
> -                }
> +    if (leader) {
> +        const struct icsbrec_datapath_binding *isb_dp =
> +            shash_find_and_delete(isb_gc, ts->name);
> +        if (!isb_dp) {
> +            /* Allocate tunnel key */
> +            int64_t dp_key = allocate_dp_key(dp_tnlids, vxlan_mode,
> +                                             "transit switch datapath");
> +            if (!dp_key) {
> +                return;
> +            }
>
> -                isb_dp = icsbrec_datapath_binding_insert(ctx->ovnisb_txn);
> -                icsbrec_datapath_binding_set_transit_switch(isb_dp, 
> ts->name);
> +            isb_dp = icsbrec_datapath_binding_insert(ctx->ovnisb_txn);
> +            icsbrec_datapath_binding_set_transit_switch(isb_dp, ts->name);
> +            icsbrec_datapath_binding_set_tunnel_key(isb_dp, dp_key);
> +
> +            /* Publish the freshly-allocated key to the AZ NB mirror in this
> +             * same iteration.  Otherwise the mirror carries no
> +             * requested-tnl-key until a follow-up iteration copies the
> +             * committed IC-SB key back (Phase 1 above), and in the meantime
> +             * northd auto-assigns a different datapath tunnel key and then 
> has
> +             * to change it - a per-transit-switch datapath tunnel-key flap 
> on
> +             * every TS creation.  Unlike a refresh (handled on a later
> +             * iteration once the new key is committed, to avoid publishing a
> +             * key that may still change), a brand-new binding's key is 
> stable:
> +             * it is the one being committed now, and if the IC-SB 
> transaction
> +             * fails the whole run is retried, so the NB hint cannot outlive
> +             * its binding. */
> +            if (ls) {
> +                char *tnl_key_str = xasprintf("%"PRId64, dp_key);
> +                nbrec_logical_switch_update_other_config_setkey(
> +                    ls, "requested-tnl-key", tnl_key_str);
> +                free(tnl_key_str);
> +            }
> +        } else if (dp_key_refresh) {
> +            /* Refresh tunnel key since encap mode has changed. */
> +            int64_t dp_key = allocate_dp_key(dp_tnlids, vxlan_mode,
> +                                             "transit switch datapath");
> +            if (dp_key) {
>                  icsbrec_datapath_binding_set_tunnel_key(isb_dp, dp_key);
> -            } else if (dp_key_refresh) {
> -                /* Refresh tunnel key since encap mode has changed. */
> -                int64_t dp_key = allocate_dp_key(dp_tnlids, vxlan_mode,
> -                                                 "transit switch datapath");
> -                if (dp_key) {
> -                    icsbrec_datapath_binding_set_tunnel_key(isb_dp, dp_key);
> -                }
>              }
> +        }
>
> -            if (!isb_dp->type) {
> -                icsbrec_datapath_binding_set_type(isb_dp, "transit-switch");
> +        if (!isb_dp->type) {
> +            icsbrec_datapath_binding_set_type(isb_dp, "transit-switch");
> +        }
> +
> +        if (!isb_dp->nb_ic_uuid) {
> +            icsbrec_datapath_binding_set_nb_ic_uuid(isb_dp,
> +                                                    &ts->header_.uuid, 1);
> +        }
> +    }
> +}
> +
> +/* Synchronizes transit switches to their NB Logical_Switch mirrors and IC-SB
> + * Datapath_Bindings.  When 'ts_scope' is NULL every transit switch is
> + * reconciled (full recompute); otherwise only the switches named in
> + * 'ts_scope' are.  A name still in scope but no longer present in IC-NB (a
> + * deleted switch) is honoured: its mirror/datapath end up as
> + * garbage-collection leftovers and are deleted, matching full-recompute
> + * behaviour.
> + *
> + * In the full case 'isb_ts_dps' is consumed destructively (the caller must
> + * own a private copy); in the scoped case it is only read - a private scoped
> + * subset is built for garbage collection so the caller's authoritative map 
> is
> + * left intact. */
> +void
> +ts_sync_scope(struct ic_context *ctx, struct hmap *dp_tnlids,
> +              struct shash *isb_ts_dps, const struct sset *ts_scope)
> +{
> +    bool full = !ts_scope;
> +    bool vxlan_mode = ts_compute_vxlan_mode(ctx);
> +    bool leader = ctx->ovnisb_txn && is_az_leader(ctx->ovnisb_txn);
> +
> +    /* Build the NB Logical_Switch mirror GC set, keyed by transit-switch
> +     * name.  Only needed when an NB transaction is available. */
> +    struct shash nb_ts_mirrors = SHASH_INITIALIZER(&nb_ts_mirrors);
> +    struct shash *nb_gc = NULL;
> +    if (ctx->ovnnb_txn) {
> +        nb_gc = &nb_ts_mirrors;
> +        if (full) {
> +            const struct nbrec_logical_switch *ls;
> +            NBREC_LOGICAL_SWITCH_FOR_EACH (ls, ctx->ovnnb_idl) {
> +                const char *ts_name = smap_get(&ls->other_config,
> +                                               "interconn-ts");
> +                if (ts_name) {
> +                    shash_add(nb_gc, ts_name, ls);
> +                }
> +            }
> +        } else {
> +            const char *name;
> +            SSET_FOR_EACH (name, ts_scope) {
> +                const struct nbrec_logical_switch *ls =
> +                    find_ts_in_nb(ctx, CONST_CAST(char *, name));
> +                if (ls && !shash_find(nb_gc, name)) {
> +                    shash_add(nb_gc, name, ls);
> +                }
>              }
> +        }
> +    }
>
> -            if (!isb_dp->nb_ic_uuid) {
> -                icsbrec_datapath_binding_set_nb_ic_uuid(isb_dp,
> -                                                        &ts->header_.uuid, 
> 1);
> +    /* Build the IC-SB Datapath_Binding GC set.  In the full case this is the
> +     * caller-owned map itself (consumed); in the scoped case it is a private
> +     * subset of the in-scope names so the caller's map is preserved. */
> +    struct shash isb_dps_scoped = SHASH_INITIALIZER(&isb_dps_scoped);
> +    struct shash *isb_gc;
> +    if (full) {
> +        isb_gc = isb_ts_dps;
> +    } else {
> +        const char *name;
> +        SSET_FOR_EACH (name, ts_scope) {
> +            struct icsbrec_datapath_binding *isb_dp =
> +                shash_find_data(isb_ts_dps, name);
> +            if (isb_dp && !shash_find(&isb_dps_scoped, name)) {
> +                shash_add(&isb_dps_scoped, name, isb_dp);
>              }
>          }
> +        isb_gc = &isb_dps_scoped;
> +    }
>
> -        struct shash_node *node;
> -        SHASH_FOR_EACH (node, isb_ts_dps) {
> +    const struct icnbrec_transit_switch *ts;
> +    ICNBREC_TRANSIT_SWITCH_FOR_EACH (ts, ctx->ovninb_idl) {
> +        if (full || sset_contains(ts_scope, ts->name)) {
> +            ts_sync_one(ctx, ts, dp_tnlids, isb_gc, nb_gc, vxlan_mode, 
> leader);
> +        }
> +    }
> +
> +    struct shash_node *node;
> +
> +    /* Delete extra NB Logical_Switch with other_config:interconn-ts. */
> +    if (nb_gc) {
> +        SHASH_FOR_EACH (node, nb_gc) {
> +            nbrec_logical_switch_delete(node->data);
> +        }
> +    }
> +
> +    /* Delete extra IC-SB Datapath_Binding. */
> +    if (leader) {
> +        SHASH_FOR_EACH (node, isb_gc) {
>              icsbrec_datapath_binding_delete(node->data);
>          }
>      }
> +
> +    shash_destroy(&nb_ts_mirrors);
> +    shash_destroy(&isb_dps_scoped);
>  }
>
>  void
> diff --git a/ic/ovn-ic.h b/ic/ovn-ic.h
> index b4b95b3ba..2e020db9d 100644
> --- a/ic/ovn-ic.h
> +++ b/ic/ovn-ic.h
> @@ -71,11 +71,19 @@ enum ic_datapath_type ic_dp_get_type(
>      const struct icsbrec_datapath_binding *isb_dp);
>
>  void address_set_run(struct ic_context *ctx);
> -void ts_run(struct ic_context *ctx, struct hmap *dp_tnlids,
> -            struct shash *isb_ts_dps);
>  void tr_run(struct ic_context *ctx, struct hmap *dp_tnlids,
>              struct shash *isb_tr_dps);
>  void port_binding_run(struct ic_context *ctx);
> +
> +struct sset;
> +
> +/* Reconciles the transit switches named in 'ts_scope' (NULL reconciles every
> + * transit switch, equivalent to ts_run()).  'isb_ts_dps' is the 
> transit-switch
> + * datapath map owned by the dp_enum engine node; it is consumed 
> destructively
> + * only in the full (NULL scope) case, so a private copy must be passed 
> there.
> + */
> +void ts_sync_scope(struct ic_context *ctx, struct hmap *dp_tnlids,
> +                   struct shash *isb_ts_dps, const struct sset *ts_scope);
>  void route_run(struct ic_context *ctx);
>  void sync_service_monitor(struct ic_context *ctx);
>
> --
> 2.34.1
>
>
> --
>
>
>
>
> _'Esta mensagem é direcionada apenas para os endereços constantes no
> cabeçalho inicial. Se você não está listado nos endereços constantes no
> cabeçalho, pedimos-lhe que desconsidere completamente o conteúdo dessa
> mensagem e cuja cópia, encaminhamento e/ou execução das ações citadas estão
> imediatamente anuladas e proibidas'._
>
>
> * **'Apesar do Magazine Luiza tomar
> todas as precauções razoáveis para assegurar que nenhum vírus esteja
> presente nesse e-mail, a empresa não poderá aceitar a responsabilidade por
> quaisquer perdas ou danos causados por esse e-mail ou por seus anexos'.*
>
>
>
> _______________________________________________
> 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