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
