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.

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       | 135 +++++++++++++++++++++++++++++++++++++++++++++++
 ic/en-ts.h       |   9 ++++
 ic/inc-proc-ic.c |  21 +++++---
 3 files changed, 159 insertions(+), 6 deletions(-)

diff --git a/ic/en-ts.c b/ic/en-ts.c
index 1f1bb5253..f830fa18e 100644
--- a/ic/en-ts.c
+++ b/ic/en-ts.c
@@ -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)
diff --git a/ic/en-ts.h b/ic/en-ts.h
index 3c22f3b40..8b833b0ca 100644
--- a/ic/en-ts.h
+++ b/ic/en-ts.h
@@ -7,4 +7,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 a30ddf367..89135fc5d 100644
--- a/ic/inc-proc-ic.c
+++ b/ic/inc-proc-ic.c
@@ -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.
-- 
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

Reply via email to