@@ -14,14 +14,335 @@
#include <config.h>
-#include "en-gateway.h"
#include "en-az.h"
+#include "en-gateway.h"
#include "lib/inc-proc-eng.h"
+#include "lib/ovn-ic-sb-idl.h"
+#include "lib/ovn-sb-idl.h"
#include "openvswitch/vlog.h"
#include "ovn-ic.h"
VLOG_DEFINE_THIS_MODULE(en_ic_gateway);
+/* Returns true if any information in gw and chassis is different. */
+static bool
+is_gateway_data_changed(const struct icsbrec_gateway *gw,
+ const struct sbrec_chassis *chassis)
+{
+ if (strcmp(gw->hostname, chassis->hostname)) {
+ return true;
+ }
+
+ if (gw->n_encaps != chassis->n_encaps) {
+ return true;
+ }
+
+ for (size_t g = 0; g < gw->n_encaps; g++) {
+ bool found = false;
+ const struct icsbrec_encap *gw_encap = gw->encaps[g];
+ for (size_t s = 0; s < chassis->n_encaps; s++) {
+ const struct sbrec_encap *chassis_encap = chassis->encaps[s];
+ if (!strcmp(gw_encap->type, chassis_encap->type) &&
+ !strcmp(gw_encap->ip, chassis_encap->ip)) {
+ found = true;
+ if (!smap_equal(&gw_encap->options, &chassis_encap->options)) {
+ return true;
+ }
+ break;
+ }
+ }
+ if (!found) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static void
+sync_isb_gw_to_sb(struct ic_context *ctx,
+ const struct icsbrec_gateway *gw,
+ const struct sbrec_chassis *chassis)
+{
+ struct smap temp_map;
+ sbrec_chassis_set_hostname(chassis, gw->hostname);
+ smap_clone(&temp_map, &chassis->other_config);
+ smap_replace(&temp_map, "is-remote", "true");
+ /* Use sbrec_chassis_set_other_config instead of
+ * sbrec_chassis_update_other_config_setkey so the in-memory datum is
+ * updated for reads in the same loop iteration. */
+ sbrec_chassis_set_other_config(chassis, &temp_map);
+ smap_destroy(&temp_map);
+
+ /* Sync encaps used by this gateway. */
+ ovs_assert(gw->n_encaps);
+ struct sbrec_encap *sb_encap;
+ struct sbrec_encap **sb_encaps =
+ xmalloc(gw->n_encaps * sizeof *sb_encaps);
+ for (size_t i = 0; i < gw->n_encaps; i++) {
+ sb_encap = sbrec_encap_insert(ctx->ovnsb_txn);
+ sbrec_encap_set_chassis_name(sb_encap, gw->name);
+ sbrec_encap_set_ip(sb_encap, gw->encaps[i]->ip);
+ sbrec_encap_set_type(sb_encap, gw->encaps[i]->type);
+ sbrec_encap_set_options(sb_encap, &gw->encaps[i]->options);
+ sb_encaps[i] = sb_encap;
+ }
+ sbrec_chassis_set_encaps(chassis, sb_encaps, gw->n_encaps);
+ free(sb_encaps);
+}
+
+static void
+sync_sb_gw_to_isb(struct ic_context *ctx,
+ const struct sbrec_chassis *chassis,
+ const struct icsbrec_gateway *gw)
+{
+ icsbrec_gateway_set_hostname(gw, chassis->hostname);
+
+ /* Sync encaps used by this chassis. */
+ ovs_assert(chassis->n_encaps);
+ struct icsbrec_encap *isb_encap;
+ struct icsbrec_encap **isb_encaps =
+ xmalloc(chassis->n_encaps * sizeof *isb_encaps);
+ for (size_t i = 0; i < chassis->n_encaps; i++) {
+ isb_encap = icsbrec_encap_insert(ctx->ovnisb_unlocked_txn);
+ icsbrec_encap_set_gateway_name(isb_encap,
+ chassis->name);
+ icsbrec_encap_set_ip(isb_encap, chassis->encaps[i]->ip);
+ icsbrec_encap_set_type(isb_encap,
+ chassis->encaps[i]->type);
+ icsbrec_encap_set_options(isb_encap,
+ &chassis->encaps[i]->options);
+ isb_encaps[i] = isb_encap;
+ }
+ icsbrec_gateway_set_encaps(gw, isb_encaps,
+ chassis->n_encaps);
+ free(isb_encaps);
+}
+
+/* Returns true if it ran to completion, false if the IC-SB/SB transactions
+ * needed to sync were not available (in which case the caller should report
+ * that the node is still stale). */
+static bool
+gateway_run(struct ic_context *ctx,
+ const struct icsbrec_availability_zone *runned_az)
+{
+ if (!ctx->ovnisb_unlocked_txn || !ctx->ovnsb_txn) {
+ return false;
+ }
+
+ struct shash local_gws = SHASH_INITIALIZER(&local_gws);
+ struct shash remote_gws = SHASH_INITIALIZER(&remote_gws);
+ const struct icsbrec_gateway *gw;
+ ICSBREC_GATEWAY_FOR_EACH (gw, ctx->ovnisb_unlocked_idl) {
+ if (gw->availability_zone == runned_az) {
+ shash_add(&local_gws, gw->name, gw);
+ } else {
+ shash_add(&remote_gws, gw->name, gw);
+ }
+ }
+
+ const struct sbrec_chassis *chassis;
+ SBREC_CHASSIS_FOR_EACH (chassis, ctx->ovnsb_idl) {
+ if (smap_get_bool(&chassis->other_config, "is-interconn", false)) {
+ gw = shash_find_and_delete(&local_gws, chassis->name);
+ if (!gw) {
+ gw = icsbrec_gateway_insert(ctx->ovnisb_unlocked_txn);
+ icsbrec_gateway_set_availability_zone(gw, runned_az);
+ icsbrec_gateway_set_name(gw, chassis->name);
+ sync_sb_gw_to_isb(ctx, chassis, gw);
+ } else if (is_gateway_data_changed(gw, chassis)) {
+ sync_sb_gw_to_isb(ctx, chassis, gw);
+ }
+ } else if (smap_get_bool(&chassis->other_config, "is-remote", false)) {
+ gw = shash_find_and_delete(&remote_gws, chassis->name);
+ if (!gw) {
+ sbrec_chassis_delete(chassis);
+ } else if (is_gateway_data_changed(gw, chassis)) {
+ sync_isb_gw_to_sb(ctx, gw, chassis);
+ }
+ }
+ }
+
+ /* Delete extra gateways from ISB for the local AZ */
+ struct shash_node *node;
+ SHASH_FOR_EACH (node, &local_gws) {
+ icsbrec_gateway_delete(node->data);
+ }
+ shash_destroy(&local_gws);
+
+ /* Create SB chassis for remote gateways in ISB */
+ SHASH_FOR_EACH (node, &remote_gws) {
+ gw = node->data;
+ chassis = sbrec_chassis_insert(ctx->ovnsb_txn);
+ sbrec_chassis_set_name(chassis, gw->name);
+ sync_isb_gw_to_sb(ctx, gw, chassis);
+ }
+ shash_destroy(&remote_gws);
+
+ return true;
+}
+
+/* Returns the IC-SB gateway named 'name' whose availability zone is (when
+ * 'local') or is not (otherwise) this instance's AZ, or NULL. There is no
+ * index on Gateway by name, but the number of gateways is small. */
+static const struct icsbrec_gateway *
+find_gw_by_name(struct ic_context *ctx,
+ const struct icsbrec_availability_zone *runned_az,
+ const char *name, bool local)
+{
+ const struct icsbrec_gateway *gw;
+ ICSBREC_GATEWAY_FOR_EACH (gw, ctx->ovnisb_unlocked_idl) {
+ if ((gw->availability_zone == runned_az) == local
+ && !strcmp(gw->name, name)) {
+ return gw;
+ }
+ }
+ return NULL;
+}
+
+/* Incremental handler for en_gateway's SB Chassis input. Mirrors the
+ * SB-Chassis-driven half of gateway_run() for the tracked chassis only:
+ * an is-interconn chassis maintains its local IC-SB gateway, while an
+ * is-remote chassis (one we created from a remote gateway) is re-synced or
+ * deleted. Returns EN_UNHANDLED to fall back to a full recompute for cases
+ * that cannot be handled incrementally, and EN_HANDLED_UNCHANGED when the
+ * tracked chassis required no write at all. */
+static enum engine_input_handler_result
+sync_gateway_handle_sb_chassis(
+ struct ic_context *ctx,
+ const struct icsbrec_availability_zone *runned_az,
+ const struct sbrec_chassis_table *tbl)
+{
+ if (!ctx->ovnisb_unlocked_txn || !ctx->ovnsb_txn) {
+ return EN_UNHANDLED;
+ }
+
+ bool changed = false;
+ const struct sbrec_chassis *chassis;
+ SBREC_CHASSIS_TABLE_FOR_EACH_TRACKED (chassis, tbl) {
+ const struct icsbrec_gateway *local_gw =
+ find_gw_by_name(ctx, runned_az, chassis->name, true);
+
+ if (sbrec_chassis_is_deleted(chassis)) {
+ /* The local gateway derived from this chassis (if any) is now
+ * orphaned. IC-SB Gateway is indexed uniquely by name, so a local
+ * gateway with this name rules out a remote one: nothing more to
+ * do for this chassis. */
+ if (local_gw) {
+ icsbrec_gateway_delete(local_gw);
+ changed = true;
+ continue;
+ }
+ /* If this was a remote chassis we created and the remote gateway
+ * still exists, it must be recreated: leave that to a full
+ * recompute. */
+ if (find_gw_by_name(ctx, runned_az, chassis->name, false)) {
+ return EN_UNHANDLED;
+ }
+ continue;
+ }
+
+ if (smap_get_bool(&chassis->other_config, "is-interconn", false)) {
+ if (!local_gw) {
+ local_gw = icsbrec_gateway_insert(ctx->ovnisb_unlocked_txn);
+ icsbrec_gateway_set_availability_zone(local_gw,
+ runned_az);
+ icsbrec_gateway_set_name(local_gw, chassis->name);
+ sync_sb_gw_to_isb(ctx, chassis, local_gw);
+ changed = true;
+ } else if (is_gateway_data_changed(local_gw, chassis)) {
+ sync_sb_gw_to_isb(ctx, chassis, local_gw);
+ changed = true;
+ }
+ } else {
+ /* No longer an interconn chassis: drop any local gateway derived
+ * from it. */
+ if (local_gw) {
+ icsbrec_gateway_delete(local_gw);
+ changed = true;
+ }
+ if (smap_get_bool(&chassis->other_config, "is-remote", false)) {
+ const struct icsbrec_gateway *remote_gw =
+ find_gw_by_name(ctx, runned_az, chassis->name, false);
+ if (!remote_gw) {
+ sbrec_chassis_delete(chassis);
+ changed = true;
+ } else if (is_gateway_data_changed(remote_gw, chassis)) {
+ sync_isb_gw_to_sb(ctx, remote_gw, chassis);
+ changed = true;
+ }
+ }
+ }
+ }
+
+ return changed ? EN_HANDLED_UPDATED : EN_HANDLED_UNCHANGED;
+}
+
+/* Incremental handler for en_gateway's IC-SB Gateway input. Mirrors the
+ * remote-gateway-driven half of gateway_run() for the tracked gateways only:
+ * a remote gateway maintains its SB chassis mirror. Returns EN_UNHANDLED to
+ * fall back to a full recompute for cases that cannot be handled
+ * incrementally, and EN_HANDLED_UNCHANGED when the tracked gateways required
+ * no write at all. */
+static enum engine_input_handler_result
+sync_gateway_handle_icsb_gateway(
+ struct ic_context *ctx,
+ const struct icsbrec_availability_zone *runned_az,
+ const struct icsbrec_gateway_table *tbl)
+{
+ if (!ctx->ovnisb_unlocked_txn || !ctx->ovnsb_txn) {
+ return EN_UNHANDLED;
+ }
+
+ bool changed = false;
+ const struct icsbrec_gateway *gw;
+ ICSBREC_GATEWAY_TABLE_FOR_EACH_TRACKED (gw, tbl) {
+ bool local = gw->availability_zone == runned_az;
+ const struct sbrec_chassis *chassis = find_sb_chassis(ctx, gw->name);
+
+ if (icsbrec_gateway_is_deleted(gw)) {
+ if (!local && chassis &&
+ smap_get_bool(&chassis->other_config, "is-remote", false)) {
+ sbrec_chassis_delete(chassis);
+ changed = true;
+ }
+ /* A local gateway disappearing while its interconn chassis still
+ * exists needs the chassis-driven recreate path:
+ * full recompute. */
+ if (local && chassis &&
+ smap_get_bool(&chassis->other_config, "is-interconn", false)) {
+ return EN_UNHANDLED;
+ }
+ continue;
+ }
+
+ if (local) {
+ /* Local gateways are produced from SB chassis (handled by the
+ * SB Chassis handler); a create/update here is our own write. */
+ continue;
+ }
+
+ /* Remote gateway: ensure a matching SB chassis exists and is synced.
+ * If the only chassis with this name is a local interconn chassis,
+ * this is an unexpected name collision; fall back. */
+ if (chassis &&
+ smap_get_bool(&chassis->other_config, "is-interconn", false)) {
+ return EN_UNHANDLED;
+ }
+ if (!chassis) {
+ chassis = sbrec_chassis_insert(ctx->ovnsb_txn);
+ sbrec_chassis_set_name(chassis, gw->name);
+ sync_isb_gw_to_sb(ctx, gw, chassis);
+ changed = true;
+ } else if (is_gateway_data_changed(gw, chassis)) {
+ sync_isb_gw_to_sb(ctx, gw, chassis);
+ changed = true;
+ }
+ }
+
+ return changed ? EN_HANDLED_UPDATED : EN_HANDLED_UNCHANGED;
+}
+
enum engine_node_state
en_gateway_run(struct engine_node *node, void *data OVS_UNUSED)
{
@@ -35,11 +356,50 @@ en_gateway_run(struct engine_node *node, void *data OVS_UNUSED)
return EN_UNCHANGED;
}
- gateway_run(ctx, az->runned_az);
+ /* If the transactions needed to sync were not available, the node could
+ * not be brought up to date; report it as stale so the engine retries. */
+ if (!gateway_run(ctx, az->runned_az)) {
+ return EN_STALE;
+ }
return EN_UPDATED;
}
+enum engine_input_handler_result
+en_gateway_sb_chassis_handler(struct engine_node *node, void *data OVS_UNUSED)
+{
+ const struct engine_context *eng_ctx = engine_get_context();
+ struct ic_context *ctx = eng_ctx->client_ctx;
+ const struct ed_type_az *az = engine_get_input_data("az", node);
+
+ if (!az->runned_az) {
+ return EN_HANDLED_UNCHANGED;
+ }
+
+ const struct sbrec_chassis_table *tbl =
+ EN_OVSDB_GET(engine_get_input("SB_chassis", node));
+
+ return sync_gateway_handle_sb_chassis(ctx, az->runned_az, tbl);
+}
+
+enum engine_input_handler_result
+en_gateway_icsb_gateway_handler(struct engine_node *node,
+ void *data OVS_UNUSED)
+{
+ const struct engine_context *eng_ctx = engine_get_context();
+ struct ic_context *ctx = eng_ctx->client_ctx;
+ const struct ed_type_az *az = engine_get_input_data("az", node);
+
+ if (!az->runned_az) {
+ return EN_HANDLED_UNCHANGED;
+ }
+
+ const struct icsbrec_gateway_table *tbl =
+ EN_OVSDB_GET(engine_get_input("ICSB_gateway", node));
+
+ return sync_gateway_handle_icsb_gateway(ctx, az->runned_az, tbl);
+}
+
void *
en_gateway_init(struct engine_node *node OVS_UNUSED,
struct engine_arg *arg OVS_UNUSED)
@@ -21,4 +21,9 @@ enum engine_node_state en_gateway_run(struct engine_node *node, void *data);
void *en_gateway_init(struct engine_node *node, struct engine_arg *arg);
void en_gateway_cleanup(void *data);
+enum engine_input_handler_result
+en_gateway_sb_chassis_handler(struct engine_node *node, void *data);
+enum engine_input_handler_result
+en_gateway_icsb_gateway_handler(struct engine_node *node, void *data);
+
#endif /* EN_IC_GATEWAY_H */
@@ -201,12 +201,20 @@ void inc_proc_ic_init(struct ovsdb_idl_loop *nb,
* splitting the monolithic ovn_db_run() into independently-gated nodes.
* Change handlers are added incrementally in a later step. */
- /* en_gateway: sync gateways/chassis between SB and IC-SB. */
+ /* en_gateway: sync gateways/chassis between SB and IC-SB.
+ *
+ * The availability zone is provided by en_az (which reports EN_UPDATED
+ * only when the AZ identity changes). en_gateway does not read the
+ * Availability_Zone table itself - only gateway rows' availability_zone
+ * reference and en_az's resolved AZ - so it deliberately does not depend
+ * on en_icsb_availability_zone, whose rows also carry the
+ * frequently-bumped nb_ic_cfg sequence number. */
engine_add_input(&en_gateway, &en_az, NULL);
- engine_add_input(&en_gateway, &en_icsb_availability_zone, NULL);
- engine_add_input(&en_gateway, &en_icsb_gateway, NULL);
+ engine_add_input(&en_gateway, &en_icsb_gateway,
+ en_gateway_icsb_gateway_handler);
engine_add_input(&en_gateway, &en_icsb_encap, NULL);
- engine_add_input(&en_gateway, &en_sb_chassis, NULL);
+ engine_add_input(&en_gateway, &en_sb_chassis,
+ en_gateway_sb_chassis_handler);
engine_add_input(&en_gateway, &en_sb_encap, NULL);
/* en_ts: sync transit switches to their AZ NB Logical_Switch mirrors.
@@ -302,102 +302,6 @@ ts_sync_scope(struct ic_context *ctx, struct shash *isb_ts_dps,
shash_destroy(&nb_ts_mirrors);
}
-/* Returns true if any information in gw and chassis is different. */
-static bool
-is_gateway_data_changed(const struct icsbrec_gateway *gw,
- const struct sbrec_chassis *chassis)
-{
- if (strcmp(gw->hostname, chassis->hostname)) {
- return true;
- }
-
- if (gw->n_encaps != chassis->n_encaps) {
- return true;
- }
-
- for (int g = 0; g < gw->n_encaps; g++) {
-
- bool found = false;
- const struct icsbrec_encap *gw_encap = gw->encaps[g];
- for (int s = 0; s < chassis->n_encaps; s++) {
- const struct sbrec_encap *chassis_encap = chassis->encaps[s];
- if (!strcmp(gw_encap->type, chassis_encap->type) &&
- !strcmp(gw_encap->ip, chassis_encap->ip)) {
- found = true;
- if (!smap_equal(&gw_encap->options, &chassis_encap->options)) {
- return true;
- }
- break;
- }
- }
- if (!found) {
- return true;
- }
- }
-
- return false;
-}
-
-static void
-sync_isb_gw_to_sb(struct ic_context *ctx,
- const struct icsbrec_gateway *gw,
- const struct sbrec_chassis *chassis)
-{
- struct smap temp_map;
- sbrec_chassis_set_hostname(chassis, gw->hostname);
- smap_clone(&temp_map, &chassis->other_config);
- smap_replace(&temp_map, "is-remote", "true");
- /* Use sbrec_chassis_set_other_config instead of
- * sbrec_chassis_update_other_config_setkey so the in-memory datum is
- * updated for reads in the same loop iteration. */
- sbrec_chassis_set_other_config(chassis, &temp_map);
- smap_destroy(&temp_map);
-
- /* Sync encaps used by this gateway. */
- ovs_assert(gw->n_encaps);
- struct sbrec_encap *sb_encap;
- struct sbrec_encap **sb_encaps =
- xmalloc(gw->n_encaps * sizeof *sb_encaps);
- for (int i = 0; i < gw->n_encaps; i++) {
- sb_encap = sbrec_encap_insert(ctx->ovnsb_txn);
- sbrec_encap_set_chassis_name(sb_encap, gw->name);
- sbrec_encap_set_ip(sb_encap, gw->encaps[i]->ip);
- sbrec_encap_set_type(sb_encap, gw->encaps[i]->type);
- sbrec_encap_set_options(sb_encap, &gw->encaps[i]->options);
- sb_encaps[i] = sb_encap;
- }
- sbrec_chassis_set_encaps(chassis, sb_encaps, gw->n_encaps);
- free(sb_encaps);
-}
-
-static void
-sync_sb_gw_to_isb(struct ic_context *ctx,
- const struct sbrec_chassis *chassis,
- const struct icsbrec_gateway *gw)
-{
- icsbrec_gateway_set_hostname(gw, chassis->hostname);
-
- /* Sync encaps used by this chassis. */
- ovs_assert(chassis->n_encaps);
- struct icsbrec_encap *isb_encap;
- struct icsbrec_encap **isb_encaps =
- xmalloc(chassis->n_encaps * sizeof *isb_encaps);
- for (int i = 0; i < chassis->n_encaps; i++) {
- isb_encap = icsbrec_encap_insert(ctx->ovnisb_unlocked_txn);
- icsbrec_encap_set_gateway_name(isb_encap,
- chassis->name);
- icsbrec_encap_set_ip(isb_encap, chassis->encaps[i]->ip);
- icsbrec_encap_set_type(isb_encap,
- chassis->encaps[i]->type);
- icsbrec_encap_set_options(isb_encap,
- &chassis->encaps[i]->options);
- isb_encaps[i] = isb_encap;
- }
- icsbrec_gateway_set_encaps(gw, isb_encaps,
- chassis->n_encaps);
- free(isb_encaps);
-}
-
static void
nb_addr_set_apply_diff(const void *arg, const char *item, bool add)
{
@@ -576,65 +480,6 @@ address_set_run(struct ic_context *ctx,
shash_destroy(&ic_remote_as);
}
-void
-gateway_run(struct ic_context *ctx,
- const struct icsbrec_availability_zone *runned_az)
-{
- if (!ctx->ovnisb_unlocked_txn || !ctx->ovnsb_txn) {
- return;
- }
-
- struct shash local_gws = SHASH_INITIALIZER(&local_gws);
- struct shash remote_gws = SHASH_INITIALIZER(&remote_gws);
- const struct icsbrec_gateway *gw;
- ICSBREC_GATEWAY_FOR_EACH (gw, ctx->ovnisb_unlocked_idl) {
- if (gw->availability_zone == runned_az) {
- shash_add(&local_gws, gw->name, gw);
- } else {
- shash_add(&remote_gws, gw->name, gw);
- }
- }
-
- const struct sbrec_chassis *chassis;
- SBREC_CHASSIS_FOR_EACH (chassis, ctx->ovnsb_idl) {
- if (smap_get_bool(&chassis->other_config, "is-interconn", false)) {
- gw = shash_find_and_delete(&local_gws, chassis->name);
- if (!gw) {
- gw = icsbrec_gateway_insert(ctx->ovnisb_unlocked_txn);
- icsbrec_gateway_set_availability_zone(gw,
- runned_az);
- icsbrec_gateway_set_name(gw, chassis->name);
- sync_sb_gw_to_isb(ctx, chassis, gw);
- } else if (is_gateway_data_changed(gw, chassis)) {
- sync_sb_gw_to_isb(ctx, chassis, gw);
- }
- } else if (smap_get_bool(&chassis->other_config, "is-remote", false)) {
- gw = shash_find_and_delete(&remote_gws, chassis->name);
- if (!gw) {
- sbrec_chassis_delete(chassis);
- } else if (is_gateway_data_changed(gw, chassis)) {
- sync_isb_gw_to_sb(ctx, gw, chassis);
- }
- }
- }
-
- /* Delete extra gateways from ISB for the local AZ */
- struct shash_node *node;
- SHASH_FOR_EACH (node, &local_gws) {
- icsbrec_gateway_delete(node->data);
- }
- shash_destroy(&local_gws);
-
- /* Create SB chassis for remote gateways in ISB */
- SHASH_FOR_EACH (node, &remote_gws) {
- gw = node->data;
- chassis = sbrec_chassis_insert(ctx->ovnsb_txn);
- sbrec_chassis_set_name(chassis, gw->name);
- sync_isb_gw_to_sb(ctx, gw, chassis);
- }
- shash_destroy(&remote_gws);
-}
-
const struct nbrec_logical_switch *
find_ts_in_nb(struct ic_context *ctx, char *ts_name)
{
@@ -785,7 +630,7 @@ get_lp_address_for_sb_pb(struct ic_context *ctx,
return peer->n_mac ? *peer->mac : NULL;
}
-static const struct sbrec_chassis *
+const struct sbrec_chassis *
find_sb_chassis(struct ic_context *ctx, const char *name)
{
const struct sbrec_chassis *key =
@@ -66,12 +66,12 @@ struct shash;
struct sset;
struct icsbrec_availability_zone;
struct icsbrec_datapath_binding;
+struct sbrec_chassis_table;
+struct icsbrec_gateway_table;
enum ic_datapath_type ic_dp_get_type(
const struct icsbrec_datapath_binding *isb_dp);
-void gateway_run(struct ic_context *ctx,
- const struct icsbrec_availability_zone *runned_az);
void address_set_run(struct ic_context *ctx,
const struct icsbrec_availability_zone *runned_az);
@@ -98,6 +98,8 @@ void sync_service_monitor(struct ic_context *ctx,
/* Shared IC helpers used by more than one engine node. */
uint32_t
allocate_dp_key(struct hmap *dp_tnlids, bool vxlan_mode, const char *name);
+const struct sbrec_chassis *
+find_sb_chassis(struct ic_context *ctx, const char *name);
const struct nbrec_logical_switch *
find_ts_in_nb(struct ic_context *ctx, char *ts_name);
bool
@@ -817,6 +817,70 @@ OVN_CLEANUP_IC([az1], [az2])
AT_CLEANUP
])
+OVN_FOR_EACH_NORTHD([
+AT_SETUP([ovn-ic -- incremental processing - tunnel_key and gateway])
+
+# Validates the incremental-processing change handlers of the en_tunnel_key and
+# en_gateway engine nodes in two ways:
+# - the change handler of the node that reads the affected table fires
+# (its compute stat is non-zero), and
+# - forcing a full recompute afterwards leaves the databases unchanged,
+# i.e. the incremental result matches a full recompute.
+
+ovn_init_ic_db
+net_add n1
+ovn_start az1
+sim_add gw1
+as gw1
+check ovs-vsctl add-br br-phys
+ovn_az_attach az1 n1 br-phys 192.168.0.1
+check ovs-vsctl set open . external-ids:ovn-is-interconn=true \
+ external-ids:hostname=gw1
+as az1
+
+# Wait until the local gateway is registered in IC-SB.
+OVS_WAIT_UNTIL([test "$(ovn-ic-sbctl --bare --columns=hostname \
+ find gateway name=gw1)" = gw1])
+check ovn-ic-nbctl --wait=sb sync
+
+# A targeted SB Chassis change is handled by the gateway node's change
+# handler: its compute stat becomes non-zero.
+check ovn-appctl -t ic/ovn-ic inc-engine/clear-stats
+as gw1
+check ovs-vsctl set open . external-ids:hostname=gw1-new
+as az1
+OVS_WAIT_UNTIL([test "$(ovn-ic-sbctl --bare --columns=hostname \
+ find gateway name=gw1)" = gw1-new])
+AT_CHECK([test "$(ovn-appctl -t ic/ovn-ic \
+ inc-engine/show-stats gateway compute)" -gt 0])
+
+# Exercise the tunnel_key handler too: a transit switch creates an IC-SB
+# datapath binding, and adding a port binding drives further IC-SB updates.
+check ovn-ic-nbctl --wait=sb ts-add ts1
+OVS_WAIT_UNTIL([test "$(ovn-nbctl --bare --columns=name \
+ find logical_switch name=ts1)" = ts1])
+check ovn-ic-nbctl --wait=sb sync
+
+# After all the incremental updates above, a forced full recompute must not
+# change any IC-managed table: incremental processing produced the same result.
+m4_define([_IC_DUMP], [
+ ovn-ic-sbctl --columns=name,hostname,availability_zone list gateway \
+ | sort > $1
+ ovn-ic-sbctl --columns=transit_switch,type,tunnel_key \
+ list datapath_binding | sort >> $1
+ ovn-nbctl --columns=name,other_config list logical_switch | sort >> $1
+ ovn-sbctl --columns=name,hostname,other_config list chassis | sort >> $1
+])
+_IC_DUMP([ic_before])
+check ovn-appctl -t ic/ovn-ic inc-engine/recompute
+check ovn-ic-nbctl --wait=sb sync
+_IC_DUMP([ic_after])
+AT_CHECK([diff ic_before ic_after])
+
+OVN_CLEANUP_IC([az1])
+AT_CLEANUP
+])
+
OVN_FOR_EACH_NORTHD([
AT_SETUP([ovn-ic -- port sync])
Add the incremental change handlers for the en_gateway node so SB/IC-SB gateway and chassis changes are reconciled in place instead of forcing a full recompute. The handlers report EN_HANDLED_UNCHANGED when the tracked rows required no write at all (an unchanged interconn chassis, a gateway update we made ourselves, a deleted chassis with no gateway to clean up), so a tracked change that turns out to be a no-op does not mark the node as updated. Add a tests/ovn-ic.at test exercising the en_dp_enum and en_gateway incremental handlers (the change handler of the node that reads a changed table fires, and a forced recompute produces no diff). Assisted-by: Claude Opus 4.8, Claude Code Signed-off-by: Paulo Guilherme Silva <guilherme.paulo@magalu.cloud> --- ic/en-gateway.c | 364 ++++++++++++++++++++++++++++++++++++++++++++++- ic/en-gateway.h | 5 + ic/inc-proc-ic.c | 16 ++- ic/ovn-ic.c | 157 +------------------- ic/ovn-ic.h | 6 +- tests/ovn-ic.at | 64 +++++++++ 6 files changed, 448 insertions(+), 164 deletions(-)