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. 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
