Assisted-by: Claude Opus 4.8, Claude Code
Signed-off-by: Paulo Guilherme Silva <[email protected]>
---
NEWS | 7 +
ic/en-service-monitor.c | 500 +++++++++++++++++++++++++++++++++++++++-
ic/en-service-monitor.h | 4 +
ic/inc-proc-ic.c | 34 ++-
ic/ovn-ic.c | 441 -----------------------------------
ic/ovn-ic.h | 2 -
tests/ovn-ic.at | 76 ++++++
7 files changed, 610 insertions(+), 454 deletions(-)
diff --git a/NEWS b/NEWS
index 8b25bc27c..7088586f3 100644
--- a/NEWS
+++ b/NEWS
@@ -100,6 +100,13 @@ Post v26.03.0
coalescing bursts of change-driven runs into fewer, larger runs. It
defaults to 0, which keeps running as soon as there is work to do. A
forced full recompute is never delayed.
+ - ovn-ic now processes database changes incrementally. Its processing
+ engine is split into one node per subsystem - availability zone,
+ gateways, transit switches and routers, datapath tunnel keys, port
+ bindings, routes, service monitors and address sets - and each node has
+ change handlers, so a change is reconciled in place instead of re-running
+ the whole ovn-ic computation. The "inc-engine/show-stats" command
+ reports the per-node recompute counters.
OVN v26.03.0 - xxx xx xxxx
--------------------------
diff --git a/ic/en-service-monitor.c b/ic/en-service-monitor.c
index d0aab60bb..bd903e709 100644
--- a/ic/en-service-monitor.c
+++ b/ic/en-service-monitor.c
@@ -14,14 +14,452 @@
#include <config.h>
-#include "en-service-monitor.h"
#include "en-az.h"
+#include "en-service-monitor.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"
+#include "ovsdb-idl.h"
VLOG_DEFINE_THIS_MODULE(en_ic_service_monitor);
+struct service_monitor_info {
+ struct hmap_node hmap_node;
+ union {
+ const struct sbrec_service_monitor *sb_rec;
+ const struct icsbrec_service_monitor *ic_rec;
+ } db_rec;
+ /* Destination availability zone name. */
+ char *dst_az_name;
+ /* Source availability zone name. */
+ char *src_az_name;
+ /* Chassis name associated with monitor logical port. */
+ char *chassis_name;
+};
+
+struct sync_service_monitor_data {
+ /* Map of service monitors to be pushed to other AZs. */
+ struct hmap pushed_svcs_map;
+ /* Map of service monitors synced from other AZs to our. */
+ struct hmap synced_svcs_map;
+ /* Map of local service monitors in the ICSBDB. */
+ struct hmap local_ic_svcs_map;
+ /* Map of local service monitors in SBDB. */
+ struct hmap local_sb_svcs_map;
+ /* MAC address used for service monitor. */
+ char *prpg_svc_monitor_mac;
+};
+
+static void
+create_service_monitor_info(struct hmap *svc_map,
+ const void *db_rec,
+ const struct uuid *uuid,
+ const char *src_az_name,
+ const char *target_az_name,
+ const char *chassis_name,
+ bool ic_rec)
+{
+ struct service_monitor_info *svc_mon = xzalloc(sizeof(*svc_mon));
+ size_t hash = uuid_hash(uuid);
+
+ if (ic_rec) {
+ svc_mon->db_rec.ic_rec =
+ (const struct icsbrec_service_monitor *) db_rec;
+ } else {
+ svc_mon->db_rec.sb_rec =
+ (const struct sbrec_service_monitor *) db_rec;
+ }
+
+ svc_mon->dst_az_name = target_az_name ? xstrdup(target_az_name) : NULL;
+ svc_mon->chassis_name = chassis_name ? xstrdup(chassis_name) : NULL;
+ svc_mon->src_az_name = xstrdup(src_az_name);
+
+ hmap_insert(svc_map, &svc_mon->hmap_node, hash);
+}
+
+static void
+destroy_service_monitor_info(struct service_monitor_info *svc_mon)
+{
+ free(svc_mon->src_az_name);
+ free(svc_mon->dst_az_name);
+ free(svc_mon->chassis_name);
+ free(svc_mon);
+}
+
+static void
+refresh_sb_record_cache(struct hmap *svc_mon_map,
+ const struct sbrec_service_monitor *lookup_rec)
+{
+ size_t hash = uuid_hash(&lookup_rec->header_.uuid);
+ struct service_monitor_info *svc_mon;
+
+ HMAP_FOR_EACH_WITH_HASH (svc_mon, hmap_node, hash, svc_mon_map) {
+ ovs_assert(svc_mon->db_rec.sb_rec);
+ if (svc_mon->db_rec.sb_rec == lookup_rec) {
+ hmap_remove(svc_mon_map, &svc_mon->hmap_node);
+ destroy_service_monitor_info(svc_mon);
+ return;
+ }
+ }
+}
+
+static void
+refresh_ic_record_cache(struct hmap *svc_mon_map,
+ const struct icsbrec_service_monitor *lookup_rec)
+{
+ size_t hash = uuid_hash(&lookup_rec->header_.uuid);
+ struct service_monitor_info *svc_mon;
+
+ HMAP_FOR_EACH_WITH_HASH (svc_mon, hmap_node, hash, svc_mon_map) {
+ ovs_assert(svc_mon->db_rec.ic_rec);
+ if (svc_mon->db_rec.ic_rec == lookup_rec) {
+ hmap_remove(svc_mon_map, &svc_mon->hmap_node);
+ destroy_service_monitor_info(svc_mon);
+ return;
+ }
+ }
+}
+
+static void
+remove_unused_ic_records(struct hmap *local_ic_svcs_map)
+{
+ struct service_monitor_info *svc_mon;
+ HMAP_FOR_EACH_SAFE (svc_mon, hmap_node, local_ic_svcs_map) {
+ icsbrec_service_monitor_delete(svc_mon->db_rec.ic_rec);
+ destroy_service_monitor_info(svc_mon);
+ }
+
+ hmap_destroy(local_ic_svcs_map);
+}
+
+static void
+remove_unused_sb_records(struct hmap *local_sb_svcs_map)
+{
+ struct service_monitor_info *svc_mon;
+ HMAP_FOR_EACH_SAFE (svc_mon, hmap_node, local_sb_svcs_map) {
+ sbrec_service_monitor_delete(svc_mon->db_rec.sb_rec);
+ destroy_service_monitor_info(svc_mon);
+ }
+
+ hmap_destroy(local_sb_svcs_map);
+}
+
+static void
+create_pushed_svcs_mon(struct ic_context *ctx,
+ const struct icsbrec_availability_zone *runned_az,
+ struct hmap *pushed_svcs_map)
+{
+ struct sbrec_service_monitor *key =
+ sbrec_service_monitor_index_init_row(
+ ctx->sbrec_service_monitor_by_remote_type);
+
+ sbrec_service_monitor_index_set_remote(key, true);
+
+ const struct sbrec_service_monitor *sb_rec;
+ SBREC_SERVICE_MONITOR_FOR_EACH_EQUAL (sb_rec, key,
+ ctx->sbrec_service_monitor_by_remote_type) {
+ const char *target_az_name = smap_get(&sb_rec->options,
+ "az-name");
+ if (!target_az_name) {
+ continue;
+ }
+ create_service_monitor_info(pushed_svcs_map, sb_rec,
+ &sb_rec->header_.uuid,
+ runned_az->name, target_az_name,
+ NULL, false);
+ }
+
+ sbrec_service_monitor_index_destroy_row(key);
+}
+
+static void
+create_synced_svcs_mon(struct ic_context *ctx,
+ const struct icsbrec_availability_zone *runned_az,
+ struct hmap *synced_svcs_map)
+{
+ struct icsbrec_service_monitor *key =
+ icsbrec_service_monitor_index_init_row(
+ ctx->icsbrec_service_monitor_by_target_az);
+
+ icsbrec_service_monitor_index_set_target_availability_zone(
+ key, runned_az->name);
+
+ const struct icsbrec_service_monitor *ic_rec;
+ ICSBREC_SERVICE_MONITOR_FOR_EACH_EQUAL (ic_rec, key,
+ ctx->icsbrec_service_monitor_by_target_az) {
+
+ const struct sbrec_port_binding *pb =
+ find_sb_pb_by_name(ctx->sbrec_port_binding_by_name,
+ ic_rec->logical_port);
+
+ if (!pb || !pb->up) {
+ continue;
+ }
+
+ const char *chassis_name = pb->chassis ? pb->chassis->name : NULL;
+ create_service_monitor_info(synced_svcs_map, ic_rec,
+ &ic_rec->header_.uuid,
+ runned_az->name,
+ NULL, chassis_name, true);
+ }
+
+ icsbrec_service_monitor_index_destroy_row(key);
+}
+
+static void
+create_local_ic_svcs_map(struct ic_context *ctx,
+ const struct icsbrec_availability_zone *runned_az,
+ struct hmap *owned_svc_map)
+{
+ struct icsbrec_service_monitor *key =
+ icsbrec_service_monitor_index_init_row(
+ ctx->icsbrec_service_monitor_by_source_az);
+
+ icsbrec_service_monitor_index_set_source_availability_zone(
+ key, runned_az->name);
+
+ const struct icsbrec_service_monitor *ic_rec;
+ ICSBREC_SERVICE_MONITOR_FOR_EACH_EQUAL (ic_rec, key,
+ ctx->icsbrec_service_monitor_by_source_az) {
+ create_service_monitor_info(owned_svc_map, ic_rec,
+ &ic_rec->header_.uuid,
+ runned_az->name, NULL,
+ NULL, true);
+ }
+
+ icsbrec_service_monitor_index_destroy_row(key);
+}
+
+static void
+create_local_sb_svcs_map(struct ic_context *ctx,
+ const struct icsbrec_availability_zone *runned_az,
+ struct hmap *owned_svc_map)
+{
+ struct sbrec_service_monitor *key =
+ sbrec_service_monitor_index_init_row(
+ ctx->sbrec_service_monitor_by_ic_learned);
+
+ sbrec_service_monitor_index_set_ic_learned(
+ key, true);
+
+ const struct sbrec_service_monitor *sb_rec;
+ SBREC_SERVICE_MONITOR_FOR_EACH_EQUAL (sb_rec, key,
+ ctx->sbrec_service_monitor_by_ic_learned) {
+ create_service_monitor_info(owned_svc_map, sb_rec,
+ &sb_rec->header_.uuid,
+ runned_az->name, NULL,
+ NULL, false);
+ }
+
+ sbrec_service_monitor_index_destroy_row(key);
+}
+
+static const struct sbrec_service_monitor *
+lookup_sb_svc_rec(struct ic_context *ctx,
+ const struct service_monitor_info *svc_mon)
+{
+ const struct icsbrec_service_monitor *db_rec =
+ svc_mon->db_rec.ic_rec;
+ struct sbrec_service_monitor *key =
+ sbrec_service_monitor_index_init_row(
+ ctx->sbrec_service_monitor_by_remote_type_logical_port);
+
+ sbrec_service_monitor_index_set_remote(key, false);
+ sbrec_service_monitor_index_set_logical_port(key, db_rec->logical_port);
+
+ const struct sbrec_service_monitor *sb_rec;
+ SBREC_SERVICE_MONITOR_FOR_EACH_EQUAL (sb_rec, key,
+ ctx->sbrec_service_monitor_by_remote_type_logical_port) {
+ if (db_rec->port == sb_rec->port &&
+ ((db_rec->type && sb_rec->type &&
+ !strcmp(db_rec->type, sb_rec->type)) ||
+ (!db_rec->type && !sb_rec->type)) &&
+ !strcmp(db_rec->ip, sb_rec->ip) &&
+ !strcmp(db_rec->src_ip, sb_rec->src_ip) &&
+ !strcmp(db_rec->protocol, sb_rec->protocol)) {
+ sbrec_service_monitor_index_destroy_row(key);
+ return sb_rec;
+ }
+ }
+
+ sbrec_service_monitor_index_destroy_row(key);
+
+ return NULL;
+}
+
+static const struct icsbrec_service_monitor *
+lookup_icsb_svc_rec(struct ic_context *ctx,
+ const struct service_monitor_info *svc_mon)
+{
+ const struct sbrec_service_monitor *db_rec =
+ svc_mon->db_rec.sb_rec;
+ struct icsbrec_service_monitor *key =
+ icsbrec_service_monitor_index_init_row(
+ ctx->icsbrec_service_monitor_by_target_az_logical_port);
+
+ ovs_assert(svc_mon->dst_az_name);
+ icsbrec_service_monitor_index_set_target_availability_zone(
+ key, svc_mon->dst_az_name);
+
+ icsbrec_service_monitor_index_set_logical_port(
+ key, db_rec->logical_port);
+
+ const struct icsbrec_service_monitor *ic_rec;
+ ICSBREC_SERVICE_MONITOR_FOR_EACH_EQUAL (ic_rec, key,
+ ctx->icsbrec_service_monitor_by_target_az_logical_port) {
+ if (db_rec->port == ic_rec->port &&
+ ((db_rec->type && ic_rec->type &&
+ !strcmp(db_rec->type, ic_rec->type)) ||
+ (!db_rec->type && !ic_rec->type)) &&
+ !strcmp(db_rec->ip, ic_rec->ip) &&
+ !strcmp(db_rec->src_ip, ic_rec->src_ip) &&
+ !strcmp(db_rec->protocol, ic_rec->protocol) &&
+ !strcmp(db_rec->logical_port, ic_rec->logical_port)) {
+ icsbrec_service_monitor_index_destroy_row(key);
+ return ic_rec;
+ }
+ }
+
+ icsbrec_service_monitor_index_destroy_row(key);
+
+ return NULL;
+}
+
+static void
+create_service_monitor_data(struct ic_context *ctx,
+ const struct icsbrec_availability_zone *runned_az,
+ struct sync_service_monitor_data *sync_data)
+{
+ const struct sbrec_sb_global *ic_sb = sbrec_sb_global_first(
+ ctx->ovnsb_idl);
+ const char *svc_monitor_mac = smap_get(&ic_sb->options,
+ "svc_monitor_mac");
+
+ if (!svc_monitor_mac) {
+ return;
+ }
+
+ sync_data->prpg_svc_monitor_mac = xstrdup(svc_monitor_mac);
+ create_pushed_svcs_mon(ctx, runned_az, &sync_data->pushed_svcs_map);
+ create_synced_svcs_mon(ctx, runned_az, &sync_data->synced_svcs_map);
+ create_local_ic_svcs_map(ctx, runned_az, &sync_data->local_ic_svcs_map);
+ create_local_sb_svcs_map(ctx, runned_az, &sync_data->local_sb_svcs_map);
+}
+
+static void
+destroy_service_monitor_data(struct sync_service_monitor_data *sync_data)
+{
+ struct service_monitor_info *svc_mon;
+ HMAP_FOR_EACH_SAFE (svc_mon, hmap_node, &sync_data->pushed_svcs_map) {
+ destroy_service_monitor_info(svc_mon);
+ }
+
+ HMAP_FOR_EACH_SAFE (svc_mon, hmap_node, &sync_data->synced_svcs_map) {
+ destroy_service_monitor_info(svc_mon);
+ }
+
+ hmap_destroy(&sync_data->pushed_svcs_map);
+ hmap_destroy(&sync_data->synced_svcs_map);
+ free(sync_data->prpg_svc_monitor_mac);
+}
+
+static void
+sync_service_monitor(struct ic_context *ctx,
+ const struct icsbrec_availability_zone *runned_az)
+{
+ if (!ctx->ovnisb_unlocked_txn || !ctx->ovnsb_txn) {
+ return;
+ }
+
+ struct sync_service_monitor_data sync_data;
+ memset(&sync_data, 0, sizeof(sync_data));
+ hmap_init(&sync_data.pushed_svcs_map);
+ hmap_init(&sync_data.synced_svcs_map);
+ hmap_init(&sync_data.local_ic_svcs_map);
+ hmap_init(&sync_data.local_sb_svcs_map);
+
+ create_service_monitor_data(ctx, runned_az, &sync_data);
+
+ struct service_monitor_info *svc_mon;
+ HMAP_FOR_EACH_SAFE (svc_mon, hmap_node, &sync_data.pushed_svcs_map) {
+ const struct sbrec_service_monitor *db_rec = svc_mon->db_rec.sb_rec;
+ const struct icsbrec_service_monitor *ic_rec =
+ lookup_icsb_svc_rec(ctx, svc_mon);
+
+ if (ic_rec) {
+ sbrec_service_monitor_set_status(db_rec, ic_rec->status);
+ } else {
+ ic_rec = icsbrec_service_monitor_insert(ctx->ovnisb_unlocked_txn);
+ icsbrec_service_monitor_set_type(ic_rec, db_rec->type);
+ icsbrec_service_monitor_set_ip(ic_rec, db_rec->ip);
+ icsbrec_service_monitor_set_port(ic_rec, db_rec->port);
+ icsbrec_service_monitor_set_src_ip(ic_rec, db_rec->src_ip);
+ icsbrec_service_monitor_set_src_mac(ic_rec,
+ sync_data.prpg_svc_monitor_mac);
+ icsbrec_service_monitor_set_protocol(ic_rec, db_rec->protocol);
+ icsbrec_service_monitor_set_logical_port(ic_rec,
+ db_rec->logical_port);
+ icsbrec_service_monitor_set_target_availability_zone(ic_rec,
+ svc_mon->dst_az_name);
+ icsbrec_service_monitor_set_source_availability_zone(ic_rec,
+ svc_mon->src_az_name);
+ }
+
+ /* Always update options because they change from NB. */
+ icsbrec_service_monitor_set_options(ic_rec, &db_rec->options);
+ refresh_ic_record_cache(&sync_data.local_ic_svcs_map, ic_rec);
+ }
+
+ HMAP_FOR_EACH_SAFE (svc_mon, hmap_node, &sync_data.synced_svcs_map) {
+ const struct icsbrec_service_monitor *db_rec =
+ svc_mon->db_rec.ic_rec;
+ const struct sbrec_service_monitor *sb_rec =
+ lookup_sb_svc_rec(ctx, svc_mon);
+
+ if (sb_rec) {
+ icsbrec_service_monitor_set_status(svc_mon->db_rec.ic_rec,
+ sb_rec->status);
+ } else {
+ sb_rec = sbrec_service_monitor_insert(ctx->ovnsb_txn);
+ sbrec_service_monitor_set_type(sb_rec, db_rec->type);
+ sbrec_service_monitor_set_ip(sb_rec, db_rec->ip);
+ sbrec_service_monitor_set_port(sb_rec, db_rec->port);
+ sbrec_service_monitor_set_src_ip(sb_rec, db_rec->src_ip);
+ /* Set svc_monitor_mac from local SBDB. */
+ sbrec_service_monitor_set_src_mac(sb_rec,
+ sync_data.prpg_svc_monitor_mac);
+ sbrec_service_monitor_set_protocol(sb_rec,
+ db_rec->protocol);
+ sbrec_service_monitor_set_logical_port(sb_rec,
+ db_rec->logical_port);
+ sbrec_service_monitor_set_remote(sb_rec, false);
+ sbrec_service_monitor_set_ic_learned(sb_rec, true);
+ }
+
+ /* Only update if ic owns it */
+ if (sb_rec->ic_learned) {
+ /* Always update options since they may change via
+ * NB configuration. Also update chassis_name if
+ * the port has been reassigned to a different chassis.
+ */
+ if (svc_mon->chassis_name) {
+ sbrec_service_monitor_set_chassis_name(sb_rec,
+ svc_mon->chassis_name);
+ }
+ sbrec_service_monitor_set_options(sb_rec, &db_rec->options);
+ refresh_sb_record_cache(&sync_data.local_sb_svcs_map, sb_rec);
+ }
+ }
+
+ /* Delete local created records that are no longer used. */
+ remove_unused_ic_records(&sync_data.local_ic_svcs_map);
+ remove_unused_sb_records(&sync_data.local_sb_svcs_map);
+
+ destroy_service_monitor_data(&sync_data);
+}
+
enum engine_node_state
en_service_monitor_run(struct engine_node *node,
void *data OVS_UNUSED)
@@ -41,6 +479,66 @@ en_service_monitor_run(struct engine_node *node,
return EN_UPDATED;
}
+/* True if 'logical_port' backs a service monitor targeting this AZ - i.e. a
+ * port whose 'up'/'chassis' state create_synced_svcs_mon() reads. */
+static bool
+svc_monitor_target_lport(struct ic_context *ctx,
+ const struct icsbrec_availability_zone *runned_az,
+ const char *logical_port)
+{
+ struct icsbrec_service_monitor *key =
+ icsbrec_service_monitor_index_init_row(
+ ctx->icsbrec_service_monitor_by_target_az_logical_port);
+ icsbrec_service_monitor_index_set_target_availability_zone(
+ key, runned_az->name);
+ icsbrec_service_monitor_index_set_logical_port(key, logical_port);
+ const struct icsbrec_service_monitor *ic_rec =
+ icsbrec_service_monitor_index_find(
+ ctx->icsbrec_service_monitor_by_target_az_logical_port, key);
+ icsbrec_service_monitor_index_destroy_row(key);
+ return ic_rec != NULL;
+}
+
+/* SB Port_Binding: sync_service_monitor() reads a port binding only through
+ * create_synced_svcs_mon(), which - for each IC-SB service monitor targeting
+ * this AZ - looks up the backing SB port binding by logical_port and reads its
+ * 'up' and 'chassis'. So a port-binding change matters only when it is
+ * new/deleted or its 'up'/'chassis' changed AND the port backs such a service
+ * monitor. Every other SB port-binding change (other columns, or ports that
+ * are not service-monitor backends - the vast majority of SB churn) is a
+ * no-op, instead of forcing a full recompute of this node. */
+enum engine_input_handler_result
+en_service_monitor_sb_port_binding_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_port_binding_table *tbl =
+ EN_OVSDB_GET(engine_get_input("SB_port_binding", node));
+ const struct sbrec_port_binding *pb;
+ SBREC_PORT_BINDING_TABLE_FOR_EACH_TRACKED (pb, tbl) {
+ if (!sbrec_port_binding_is_new(pb) &&
+ !sbrec_port_binding_is_deleted(pb) &&
+ !ovsdb_idl_track_is_updated(&pb->header_,
+ &sbrec_port_binding_col_up) &&
+ !ovsdb_idl_track_is_updated(&pb->header_,
+ &sbrec_port_binding_col_chassis)) {
+ continue;
+ }
+ if (svc_monitor_target_lport(ctx, az->runned_az, pb->logical_port)) {
+ return EN_UNHANDLED;
+ }
+ }
+
+ return EN_HANDLED_UNCHANGED;
+}
+
void *
en_service_monitor_init(struct engine_node *node OVS_UNUSED,
struct engine_arg *arg OVS_UNUSED)
diff --git a/ic/en-service-monitor.h b/ic/en-service-monitor.h
index 55103635e..ff6cc9c6f 100644
--- a/ic/en-service-monitor.h
+++ b/ic/en-service-monitor.h
@@ -9,4 +9,8 @@ void *en_service_monitor_init(struct engine_node *node,
struct engine_arg *arg);
void en_service_monitor_cleanup(void *data);
+enum engine_input_handler_result
+en_service_monitor_sb_port_binding_handler(struct engine_node *node,
+ void *data);
+
#endif /* EN_IC_SERVICE_MONITOR_H */
diff --git a/ic/inc-proc-ic.c b/ic/inc-proc-ic.c
index 92b368153..af0060ae5 100644
--- a/ic/inc-proc-ic.c
+++ b/ic/inc-proc-ic.c
@@ -118,12 +118,14 @@ VLOG_DEFINE_THIS_MODULE(inc_proc_ic);
ICNB_NODES
#undef ICNB_NODE
-/* Note: the ic_sb_global table is intentionally not modeled as an engine input
- * node. It only carries IC-SB sequence numbers, which are written by
- * update_sequence_numbers() in the main loop (outside the engine) and are not
- * read by any subsystem node. */
+/* Note: the ic_sb_global and availability_zone tables are intentionally not
+ * modeled as engine input nodes. ic_sb_global only carries IC-SB sequence
+ * numbers, written by update_sequence_numbers() in the main loop (outside the
+ * engine). availability_zone is consumed by the en_az node, which reads it
+ * directly every iteration; the subsystem nodes depend on en_az for the AZ
+ * identity rather than on the (sequence-number-bumped) Availability_Zone
+ * table. */
#define ICSB_NODES \
- ICSB_NODE(availability_zone, "availability_zone") \
ICSB_NODE(service_monitor, "service_monitor") \
ICSB_NODE(route, "route") \
ICSB_NODE(datapath_binding, "datapath_binding") \
@@ -351,13 +353,23 @@ void inc_proc_ic_init(struct ovsdb_idl_loop *nb,
engine_add_input(&en_route, &en_sb_learned_route,
route_sb_learned_route_handler);
- /* en_service_monitor: sync load-balancer health checks across AZs. */
+ /* en_service_monitor: sync load-balancer health checks across AZs.
+ *
+ * Like the other AZ-scoped nodes it uses only the AZ identity
+ * (en_az's resolved AZ name and the by-source/target-AZ indexes)
+ * and does not read the Availability_Zone table, so it does not depend on
+ * en_icsb_availability_zone and is not churned by its nb_ic_cfg sequence
+ * number. */
engine_add_input(&en_service_monitor, &en_az, NULL);
- engine_add_input(&en_service_monitor, &en_icsb_availability_zone, NULL);
engine_add_input(&en_service_monitor, &en_icsb_service_monitor, NULL);
engine_add_input(&en_service_monitor, &en_sb_sb_global, NULL);
engine_add_input(&en_service_monitor, &en_sb_service_monitor, NULL);
- engine_add_input(&en_service_monitor, &en_sb_port_binding, NULL);
+ /* SB port bindings are the busiest table in the AZ, but the sync reads
+ * only the 'up'/'chassis' of ports backing a service monitor targeting
+ * this AZ, so a dedicated handler scopes that churn out instead of forcing
+ * a full recompute on every port-binding change. */
+ engine_add_input(&en_service_monitor, &en_sb_port_binding,
+ en_service_monitor_sb_port_binding_handler);
/* en_address_set: advertise/learn address sets across AZs.
*
@@ -365,9 +377,11 @@ void inc_proc_ic_init(struct ovsdb_idl_loop *nb,
* address sets into local/remote by comparing their availability_zone
* against this instance's AZ, so it depends on en_az (which reports
* EN_UPDATED only when the AZ identity changes) to be re-run when the
- * resolved AZ changes. */
+ * resolved AZ changes. It uses only that AZ identity and does not read
+ * the Availability_Zone table, so it does not depend on
+ * en_icsb_availability_zone and is not churned by its nb_ic_cfg sequence
+ * number. */
engine_add_input(&en_address_set, &en_az, NULL);
- engine_add_input(&en_address_set, &en_icsb_availability_zone, NULL);
engine_add_input(&en_address_set, &en_nb_nb_global, NULL);
engine_add_input(&en_address_set, &en_nb_address_set, NULL);
engine_add_input(&en_address_set, &en_sb_address_set, NULL);
diff --git a/ic/ovn-ic.c b/ic/ovn-ic.c
index a73a30723..6730c4c79 100644
--- a/ic/ovn-ic.c
+++ b/ic/ovn-ic.c
@@ -595,447 +595,6 @@ port_binding_collect_lsp_ts(const struct shash
*lsp_ts_map,
}
}
-/*
- * Data structures and functions related to
- * synchronize health checks for load balancers
- * between availability zones.
- */
-struct sync_service_monitor_data {
- /* Map of service monitors to be pushed to other AZs. */
- struct hmap pushed_svcs_map;
- /* Map of service monitors synced from other AZs to our. */
- struct hmap synced_svcs_map;
- /* Map of local service monitors in the ICSBDB. */
- struct hmap local_ic_svcs_map;
- /* Map of local service monitors in SBDB. */
- struct hmap local_sb_svcs_map;
- /* MAC address used for service monitor. */
- char *prpg_svc_monitor_mac;
-};
-
-struct service_monitor_info {
- struct hmap_node hmap_node;
- union {
- const struct sbrec_service_monitor *sb_rec;
- const struct icsbrec_service_monitor *ic_rec;
- } db_rec;
- /* Destination availability zone name. */
- char *dst_az_name;
- /* Source availability zone name. */
- char *src_az_name;
- /* Chassis name associated with monitor logical port. */
- char *chassis_name;
-};
-
-static void
-create_service_monitor_info(struct hmap *svc_map,
- const void *db_rec,
- const struct uuid *uuid,
- const char *src_az_name,
- const char *target_az_name,
- const char *chassis_name,
- bool ic_rec)
-{
- struct service_monitor_info *svc_mon = xzalloc(sizeof(*svc_mon));
- size_t hash = uuid_hash(uuid);
-
- if (ic_rec) {
- svc_mon->db_rec.ic_rec =
- (const struct icsbrec_service_monitor *) db_rec;
- } else {
- svc_mon->db_rec.sb_rec =
- (const struct sbrec_service_monitor *) db_rec;
- }
-
- svc_mon->dst_az_name = target_az_name ? xstrdup(target_az_name) : NULL;
- svc_mon->chassis_name = chassis_name ? xstrdup(chassis_name) : NULL;
- svc_mon->src_az_name = xstrdup(src_az_name);
-
- hmap_insert(svc_map, &svc_mon->hmap_node, hash);
-}
-
-static void
-destroy_service_monitor_info(struct service_monitor_info *svc_mon)
-{
- free(svc_mon->src_az_name);
- free(svc_mon->dst_az_name);
- free(svc_mon->chassis_name);
- free(svc_mon);
-}
-
-static void
-refresh_sb_record_cache(struct hmap *svc_mon_map,
- const struct sbrec_service_monitor *lookup_rec)
-{
- size_t hash = uuid_hash(&lookup_rec->header_.uuid);
- struct service_monitor_info *svc_mon;
-
- HMAP_FOR_EACH_WITH_HASH (svc_mon, hmap_node, hash, svc_mon_map) {
- ovs_assert(svc_mon->db_rec.sb_rec);
- if (svc_mon->db_rec.sb_rec == lookup_rec) {
- hmap_remove(svc_mon_map, &svc_mon->hmap_node);
- destroy_service_monitor_info(svc_mon);
- return;
- }
- }
-}
-
-static void
-refresh_ic_record_cache(struct hmap *svc_mon_map,
- const struct icsbrec_service_monitor *lookup_rec)
-{
- size_t hash = uuid_hash(&lookup_rec->header_.uuid);
- struct service_monitor_info *svc_mon;
-
- HMAP_FOR_EACH_WITH_HASH (svc_mon, hmap_node, hash, svc_mon_map) {
- ovs_assert(svc_mon->db_rec.ic_rec);
- if (svc_mon->db_rec.ic_rec == lookup_rec) {
- hmap_remove(svc_mon_map, &svc_mon->hmap_node);
- destroy_service_monitor_info(svc_mon);
- return;
- }
- }
-}
-
-static void
-remove_unused_ic_records(struct hmap *local_ic_svcs_map)
-{
- struct service_monitor_info *svc_mon;
- HMAP_FOR_EACH_SAFE (svc_mon, hmap_node, local_ic_svcs_map) {
- icsbrec_service_monitor_delete(svc_mon->db_rec.ic_rec);
- destroy_service_monitor_info(svc_mon);
- }
-
- hmap_destroy(local_ic_svcs_map);
-}
-
-static void
-remove_unused_sb_records(struct hmap *local_sb_svcs_map)
-{
- struct service_monitor_info *svc_mon;
- HMAP_FOR_EACH_SAFE (svc_mon, hmap_node, local_sb_svcs_map) {
- sbrec_service_monitor_delete(svc_mon->db_rec.sb_rec);
- destroy_service_monitor_info(svc_mon);
- }
-
- hmap_destroy(local_sb_svcs_map);
-}
-
-static void
-create_pushed_svcs_mon(struct ic_context *ctx,
- const struct icsbrec_availability_zone *runned_az,
- struct hmap *pushed_svcs_map)
-{
- struct sbrec_service_monitor *key =
- sbrec_service_monitor_index_init_row(
- ctx->sbrec_service_monitor_by_remote_type);
-
- sbrec_service_monitor_index_set_remote(key, true);
-
- const struct sbrec_service_monitor *sb_rec;
- SBREC_SERVICE_MONITOR_FOR_EACH_EQUAL (sb_rec, key,
- ctx->sbrec_service_monitor_by_remote_type) {
- const char *target_az_name = smap_get(&sb_rec->options,
- "az-name");
- if (!target_az_name) {
- continue;
- }
- create_service_monitor_info(pushed_svcs_map, sb_rec,
- &sb_rec->header_.uuid,
- runned_az->name,
- target_az_name, NULL, false);
- }
-
- sbrec_service_monitor_index_destroy_row(key);
-}
-
-static void
-create_synced_svcs_mon(struct ic_context *ctx,
- const struct icsbrec_availability_zone *runned_az,
- struct hmap *synced_svcs_map)
-{
- struct icsbrec_service_monitor *key =
- icsbrec_service_monitor_index_init_row(
- ctx->icsbrec_service_monitor_by_target_az);
-
- icsbrec_service_monitor_index_set_target_availability_zone(
- key, runned_az->name);
-
- const struct icsbrec_service_monitor *ic_rec;
- ICSBREC_SERVICE_MONITOR_FOR_EACH_EQUAL (ic_rec, key,
- ctx->icsbrec_service_monitor_by_target_az) {
-
- const struct sbrec_port_binding *pb =
- find_sb_pb_by_name(ctx->sbrec_port_binding_by_name,
- ic_rec->logical_port);
-
- if (!pb || !pb->up) {
- continue;
- }
-
- const char *chassis_name = pb->chassis ? pb->chassis->name : NULL;
- create_service_monitor_info(synced_svcs_map, ic_rec,
- &ic_rec->header_.uuid,
- runned_az->name,
- NULL, chassis_name, true);
- }
-
- icsbrec_service_monitor_index_destroy_row(key);
-}
-
-static void
-create_local_ic_svcs_map(struct ic_context *ctx,
- const struct icsbrec_availability_zone *runned_az,
- struct hmap *owned_svc_map)
-{
- struct icsbrec_service_monitor *key =
- icsbrec_service_monitor_index_init_row(
- ctx->icsbrec_service_monitor_by_source_az);
-
- icsbrec_service_monitor_index_set_source_availability_zone(
- key, runned_az->name);
-
- const struct icsbrec_service_monitor *ic_rec;
- ICSBREC_SERVICE_MONITOR_FOR_EACH_EQUAL (ic_rec, key,
- ctx->icsbrec_service_monitor_by_source_az) {
- create_service_monitor_info(owned_svc_map, ic_rec,
- &ic_rec->header_.uuid,
- runned_az->name, NULL,
- NULL, true);
- }
-
- icsbrec_service_monitor_index_destroy_row(key);
-}
-
-static void
-create_local_sb_svcs_map(struct ic_context *ctx,
- const struct icsbrec_availability_zone *runned_az,
- struct hmap *owned_svc_map)
-{
- struct sbrec_service_monitor *key =
- sbrec_service_monitor_index_init_row(
- ctx->sbrec_service_monitor_by_ic_learned);
-
- sbrec_service_monitor_index_set_ic_learned(
- key, true);
-
- const struct sbrec_service_monitor *sb_rec;
- SBREC_SERVICE_MONITOR_FOR_EACH_EQUAL (sb_rec, key,
- ctx->sbrec_service_monitor_by_ic_learned) {
- create_service_monitor_info(owned_svc_map, sb_rec,
- &sb_rec->header_.uuid,
- runned_az->name, NULL,
- NULL, false);
- }
-
- sbrec_service_monitor_index_destroy_row(key);
-}
-
-static const struct sbrec_service_monitor *
-lookup_sb_svc_rec(struct ic_context *ctx,
- const struct service_monitor_info *svc_mon)
-{
- const struct icsbrec_service_monitor *db_rec =
- svc_mon->db_rec.ic_rec;
- struct sbrec_service_monitor *key =
- sbrec_service_monitor_index_init_row(
- ctx->sbrec_service_monitor_by_remote_type_logical_port);
-
- sbrec_service_monitor_index_set_remote(key, false);
- sbrec_service_monitor_index_set_logical_port(key, db_rec->logical_port);
-
- const struct sbrec_service_monitor *sb_rec;
- SBREC_SERVICE_MONITOR_FOR_EACH_EQUAL (sb_rec, key,
- ctx->sbrec_service_monitor_by_remote_type_logical_port) {
- if (db_rec->port == sb_rec->port &&
- ((db_rec->type && sb_rec->type &&
- !strcmp(db_rec->type, sb_rec->type)) ||
- (!db_rec->type && !sb_rec->type)) &&
- !strcmp(db_rec->ip, sb_rec->ip) &&
- !strcmp(db_rec->src_ip, sb_rec->src_ip) &&
- !strcmp(db_rec->protocol, sb_rec->protocol)) {
- sbrec_service_monitor_index_destroy_row(key);
- return sb_rec;
- }
- }
-
- sbrec_service_monitor_index_destroy_row(key);
-
- return NULL;
-}
-
-static const struct icsbrec_service_monitor *
-lookup_icsb_svc_rec(struct ic_context *ctx,
- const struct service_monitor_info *svc_mon)
-{
- const struct sbrec_service_monitor *db_rec =
- svc_mon->db_rec.sb_rec;
- struct icsbrec_service_monitor *key =
- icsbrec_service_monitor_index_init_row(
- ctx->icsbrec_service_monitor_by_target_az_logical_port);
-
- ovs_assert(svc_mon->dst_az_name);
- icsbrec_service_monitor_index_set_target_availability_zone(
- key, svc_mon->dst_az_name);
-
- icsbrec_service_monitor_index_set_logical_port(
- key, db_rec->logical_port);
-
- const struct icsbrec_service_monitor *ic_rec;
- ICSBREC_SERVICE_MONITOR_FOR_EACH_EQUAL (ic_rec, key,
- ctx->icsbrec_service_monitor_by_target_az_logical_port) {
- if (db_rec->port == ic_rec->port &&
- ((db_rec->type && ic_rec->type &&
- !strcmp(db_rec->type, ic_rec->type)) ||
- (!db_rec->type && !ic_rec->type)) &&
- !strcmp(db_rec->ip, ic_rec->ip) &&
- !strcmp(db_rec->src_ip, ic_rec->src_ip) &&
- !strcmp(db_rec->protocol, ic_rec->protocol) &&
- !strcmp(db_rec->logical_port, ic_rec->logical_port)) {
- icsbrec_service_monitor_index_destroy_row(key);
- return ic_rec;
- }
- }
-
- icsbrec_service_monitor_index_destroy_row(key);
-
- return NULL;
-}
-
-static void
-create_service_monitor_data(struct ic_context *ctx,
- const struct icsbrec_availability_zone *runned_az,
- struct sync_service_monitor_data *sync_data)
-{
- const struct sbrec_sb_global *ic_sb = sbrec_sb_global_first(
- ctx->ovnsb_idl);
- const char *svc_monitor_mac = smap_get(&ic_sb->options,
- "svc_monitor_mac");
-
- if (!svc_monitor_mac) {
- return;
- }
-
- sync_data->prpg_svc_monitor_mac = xstrdup(svc_monitor_mac);
- create_pushed_svcs_mon(ctx, runned_az, &sync_data->pushed_svcs_map);
- create_synced_svcs_mon(ctx, runned_az, &sync_data->synced_svcs_map);
- create_local_ic_svcs_map(ctx, runned_az, &sync_data->local_ic_svcs_map);
- create_local_sb_svcs_map(ctx, runned_az,
- &sync_data->local_sb_svcs_map);
-}
-
-static void
-destroy_service_monitor_data(struct sync_service_monitor_data *sync_data)
-{
- struct service_monitor_info *svc_mon;
- HMAP_FOR_EACH_SAFE (svc_mon, hmap_node, &sync_data->pushed_svcs_map) {
- destroy_service_monitor_info(svc_mon);
- }
-
- HMAP_FOR_EACH_SAFE (svc_mon, hmap_node, &sync_data->synced_svcs_map) {
- destroy_service_monitor_info(svc_mon);
- }
-
- hmap_destroy(&sync_data->pushed_svcs_map);
- hmap_destroy(&sync_data->synced_svcs_map);
- free(sync_data->prpg_svc_monitor_mac);
-}
-
-void
-sync_service_monitor(struct ic_context *ctx,
- const struct icsbrec_availability_zone *runned_az)
-{
- if (!ctx->ovnisb_unlocked_txn || !ctx->ovnsb_txn) {
- return;
- }
-
- struct sync_service_monitor_data sync_data;
- memset(&sync_data, 0, sizeof(sync_data));
- hmap_init(&sync_data.pushed_svcs_map);
- hmap_init(&sync_data.synced_svcs_map);
- hmap_init(&sync_data.local_ic_svcs_map);
- hmap_init(&sync_data.local_sb_svcs_map);
-
- create_service_monitor_data(ctx, runned_az, &sync_data);
-
- struct service_monitor_info *svc_mon;
- HMAP_FOR_EACH_SAFE (svc_mon, hmap_node, &sync_data.pushed_svcs_map) {
- const struct sbrec_service_monitor *db_rec = svc_mon->db_rec.sb_rec;
- const struct icsbrec_service_monitor *ic_rec =
- lookup_icsb_svc_rec(ctx, svc_mon);
-
- if (ic_rec) {
- sbrec_service_monitor_set_status(db_rec, ic_rec->status);
- } else {
- ic_rec = icsbrec_service_monitor_insert(ctx->ovnisb_unlocked_txn);
- icsbrec_service_monitor_set_type(ic_rec, db_rec->type);
- icsbrec_service_monitor_set_ip(ic_rec, db_rec->ip);
- icsbrec_service_monitor_set_port(ic_rec, db_rec->port);
- icsbrec_service_monitor_set_src_ip(ic_rec, db_rec->src_ip);
- icsbrec_service_monitor_set_src_mac(ic_rec,
- sync_data.prpg_svc_monitor_mac);
- icsbrec_service_monitor_set_protocol(ic_rec, db_rec->protocol);
- icsbrec_service_monitor_set_logical_port(ic_rec,
- db_rec->logical_port);
- icsbrec_service_monitor_set_target_availability_zone(ic_rec,
- svc_mon->dst_az_name);
- icsbrec_service_monitor_set_source_availability_zone(ic_rec,
- svc_mon->src_az_name);
- }
-
- /* Always update options because they change from NB. */
- icsbrec_service_monitor_set_options(ic_rec, &db_rec->options);
- refresh_ic_record_cache(&sync_data.local_ic_svcs_map, ic_rec);
- }
-
- HMAP_FOR_EACH_SAFE (svc_mon, hmap_node, &sync_data.synced_svcs_map) {
- const struct icsbrec_service_monitor *db_rec =
- svc_mon->db_rec.ic_rec;
- const struct sbrec_service_monitor *sb_rec =
- lookup_sb_svc_rec(ctx, svc_mon);
-
- if (sb_rec) {
- icsbrec_service_monitor_set_status(svc_mon->db_rec.ic_rec,
- sb_rec->status);
- } else {
- sb_rec = sbrec_service_monitor_insert(ctx->ovnsb_txn);
- sbrec_service_monitor_set_type(sb_rec, db_rec->type);
- sbrec_service_monitor_set_ip(sb_rec, db_rec->ip);
- sbrec_service_monitor_set_port(sb_rec, db_rec->port);
- sbrec_service_monitor_set_src_ip(sb_rec, db_rec->src_ip);
- /* Set svc_monitor_mac from local SBDB. */
- sbrec_service_monitor_set_src_mac(sb_rec,
- sync_data.prpg_svc_monitor_mac);
- sbrec_service_monitor_set_protocol(sb_rec,
- db_rec->protocol);
- sbrec_service_monitor_set_logical_port(sb_rec,
- db_rec->logical_port);
- sbrec_service_monitor_set_remote(sb_rec, false);
- sbrec_service_monitor_set_ic_learned(sb_rec, true);
- }
-
- /* Only update if ic owns it */
- if (sb_rec->ic_learned) {
- /* Always update options since they may change via
- * NB configuration. Also update chassis_name if
- * the port has been reassigned to a different chassis.
- */
- if (svc_mon->chassis_name) {
- sbrec_service_monitor_set_chassis_name(sb_rec,
- svc_mon->chassis_name);
- }
- sbrec_service_monitor_set_options(sb_rec, &db_rec->options);
- refresh_sb_record_cache(&sync_data.local_sb_svcs_map, sb_rec);
- }
- }
-
- /* Delete local created records that are no longer used. */
- remove_unused_ic_records(&sync_data.local_ic_svcs_map);
- remove_unused_sb_records(&sync_data.local_sb_svcs_map);
-
- destroy_service_monitor_data(&sync_data);
-}
-
/*
* This function implements a sequence number protocol that can be used by
* the INB end user to verify that ISB is synced with all the changes that
diff --git a/ic/ovn-ic.h b/ic/ovn-ic.h
index 44774ba81..f98d338db 100644
--- a/ic/ovn-ic.h
+++ b/ic/ovn-ic.h
@@ -100,8 +100,6 @@ void port_binding_lsp_ts_map_init(struct ic_context *ctx,
struct shash *map);
void port_binding_collect_lsp_ts(const struct shash *lsp_ts_map,
const struct nbrec_logical_switch_port *lsp,
struct sset *ts_scope);
-void sync_service_monitor(struct ic_context *ctx,
- const struct icsbrec_availability_zone *runned_az);
/* Shared IC helpers used by more than one engine node. */
uint32_t
diff --git a/tests/ovn-ic.at b/tests/ovn-ic.at
index 60028cc8b..df0c1d0dd 100644
--- a/tests/ovn-ic.at
+++ b/tests/ovn-ic.at
@@ -996,6 +996,82 @@ OVN_CLEANUP_IC([az1])
AT_CLEANUP
])
+OVN_FOR_EACH_NORTHD([
+AT_SETUP([ovn-ic -- incremental processing - port binding, route, svc monitor])
+
+# The en_port_binding, en_route and en_service_monitor nodes use only the AZ
+# identity, not the Availability_Zone table, so a change to IC-NB Global
+# options and the resulting nb_ic_cfg sequence-number bump must not recompute
+# them.
+
+ovn_init_ic_db
+net_add n1
+ovn_start az1
+as az1
+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
+check ovn-appctl -t ic/ovn-ic inc-engine/recompute
+check ovn-ic-nbctl --wait=sb sync
+check ovn-appctl -t ic/ovn-ic inc-engine/clear-stats
+
+check ovn-ic-nbctl --wait=sb set IC_NB_Global . options:foo=bar
+check ovn-ic-nbctl --wait=sb sync
+
+AT_CHECK([test "$(ovn-appctl -t ic/ovn-ic \
+ inc-engine/show-stats port_binding recompute)" = 0])
+AT_CHECK([test "$(ovn-appctl -t ic/ovn-ic \
+ inc-engine/show-stats route recompute)" = 0])
+AT_CHECK([test "$(ovn-appctl -t ic/ovn-ic \
+ inc-engine/show-stats service_monitor recompute)" = 0])
+
+OVN_CLEANUP_IC([az1])
+AT_CLEANUP
+])
+
+OVN_FOR_EACH_NORTHD([
+AT_SETUP([ovn-ic -- incremental processing - service monitor SB port binding
scoping])
+
+# SB Port_Binding is the busiest table in the AZ, but the en_service_monitor
+# sync reads only the 'up'/'chassis' of the port backing a service monitor
+# targeting this AZ. A port binding that backs no such service monitor must be
+# handled by the en_service_monitor SB Port_Binding handler without a full
+# recompute of the node.
+
+ovn_init_ic_db
+net_add n1
+ovn_start az1
+as az1
+check ovn-ic-nbctl --wait=sb ts-add ts1
+check ovn-nbctl ls-add sw0
+check ovn-nbctl --wait=sb lsp-add sw0 sp0
+OVS_WAIT_UNTIL([test -n "$(ovn-sbctl --bare --columns=_uuid \
+ find port_binding logical_port=sp0)"])
+
+# Quiesce the engine and reset stats so only the change under test counts.
+check ovn-ic-nbctl --wait=sb sync
+check ovn-appctl -t ic/ovn-ic inc-engine/recompute
+check ovn-ic-nbctl --wait=sb sync
+check ovn-appctl -t ic/ovn-ic inc-engine/clear-stats
+
+# Flip the SB port binding 'up' of a port that backs no service monitor,
+# directly in the SB (so SB_Global is not bumped). This is a watched column,
+# but the port is not a service-monitor target, so en_service_monitor must not
+# recompute.
+check ovn-sbctl set port_binding sp0 up=true
+OVS_WAIT_UNTIL([test "$(ovn-sbctl --bare --columns=up \
+ find port_binding logical_port=sp0)" = true])
+check ovn-ic-nbctl --wait=sb sync
+
+AT_CHECK([test "$(ovn-appctl -t ic/ovn-ic \
+ inc-engine/show-stats service_monitor recompute)" = 0])
+
+OVN_CLEANUP_IC([az1])
+AT_CLEANUP
+])
+
OVN_FOR_EACH_NORTHD([
AT_SETUP([ovn-ic -- incremental processing - route handlers])
--
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