@@ -68,6 +68,141 @@ 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(struct engine_node *node)
+{
+ struct ic_context *ctx = engine_get_context()->client_ctx;
+ const struct ed_type_az *az = engine_get_input_data("az", node);
+ return az->runned_az ? ctx : NULL;
+}
+
+/* Runs the scoped NB-mirror sync for the collected transit-switch scope and
+ * maps it to an engine result. ts_sync_scope() gates the NB mirror on the
+ * ovnnb_txn it needs, exactly as the full recompute does. */
+static enum engine_input_handler_result
+ts_scope_finish(struct ic_context *ctx, struct sset *ts_scope)
+{
+ if (sset_is_empty(ts_scope)) {
+ return EN_HANDLED_UNCHANGED;
+ }
+
+ struct shash isb_ts_dps;
+ collect_ts_datapaths(ctx, &isb_ts_dps);
+ ts_sync_scope(ctx, &isb_ts_dps, ts_scope);
+ shash_destroy(&isb_ts_dps);
+
+ return EN_HANDLED_UPDATED;
+}
+
+/* IC-NB Transit_Switch: a new/deleted/renamed transit switch must have its NB
+ * mirror 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 mirror leftover is 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(node);
+ if (!ctx) {
+ return EN_HANDLED_UNCHANGED;
+ }
+
+ 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, &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(node);
+ if (!ctx) {
+ return EN_HANDLED_UNCHANGED;
+ }
+
+ 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, &ts_scope);
+ sset_destroy(&ts_scope);
+ return ret;
+}
+
+/* IC-SB Datapath_Binding: when a transit switch's datapath tunnel key is
+ * (re)assigned by en_tunnel_key, 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 reallocates the datapath key into the VXLAN range in
+ * IC-SB, but the NB value only catches up on a follow-up iteration).
+ *
+ * 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. en_tunnel_key
+ * inserts the binding and already publishes its freshly-allocated key to the
+ * mirror in the same iteration; reacting to that insert here would re-sync the
+ * transit switch while its mirror (if just created by
+ * en_ts_icnb_transit_switch_handler) 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. Here we only react to a tunnel-key *modify* on an
+ * already-existing binding (the vxlan refresh), whose mirror already
+ * exists. */
+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(node);
+ if (!ctx) {
+ return EN_HANDLED_UNCHANGED;
+ }
+
+ 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, &ts_scope);
+ sset_destroy(&ts_scope);
+ return ret;
+}
+
void *
en_ts_init(struct engine_node *node OVS_UNUSED,
struct engine_arg *arg OVS_UNUSED)
@@ -21,4 +21,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 */
@@ -218,14 +218,23 @@ void inc_proc_ic_init(struct ovsdb_idl_loop *nb,
engine_add_input(&en_gateway, &en_sb_encap, NULL);
/* en_ts: sync transit switches to their AZ NB Logical_Switch mirrors.
+ *
* en_ts builds its own transit-switch IC-SB Datapath_Binding map each run
- * and only maintains the NB mirror; IC-SB Datapath_Binding creation/keying
- * is owned by en_tunnel_key (downstream). */
+ * (local data, never shared) and only maintains the NB mirror. IC-SB
+ * Datapath_Binding creation/keying is owned by en_tunnel_key (downstream),
+ * so en_ts no longer allocates tunnel keys. en_icsb_datapath_binding
+ * drives the follow-up NB requested-tnl-key sync after en_tunnel_key
+ * (re)assigns a key - notably the global refresh from an IC-NB vxlan_mode
+ * change (see en_ts_icsb_datapath_binding_handler). */
engine_add_input(&en_ts, &en_az, NULL);
- engine_add_input(&en_ts, &en_icsb_datapath_binding, 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_icsb_datapath_binding,
+ en_ts_icsb_datapath_binding_handler);
+ engine_add_input(&en_ts, &en_icnb_ic_nb_global,
+ ic_nb_global_options_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 their AZ NB Logical_Router mirrors.