Hi Alexandra, please see in-line for comments.

On 7/31/25 8:01 AM, Alexandra Rukomoinikova wrote:
This commit introduces the ability to configure health checks for load balancer
backends located in remote availability zones through OVN Interconnect.

Key changes include:
1. Added new 'local' field to service monitor schema.
2. Extended ip_port_mappings syntax to support remote backends:
    - Added :remote:<az-name> suffix for health check targets

Why is ":remote" necessary? Could we not just append ":<az-name>" onto the existing configuration to get the same results?

3. Modified controller logic to skip non-local monitors.
4. Enhanced northd to handle remote health check configuration.

Signed-off-by: Alexandra Rukomoinikova <arukomoinikova@k2.cloud>
---
  controller/pinctrl.c |  2 +-
  northd/lb.c          | 30 +++++++++++++++++++++++-------
  northd/lb.h          |  9 +++++++--
  northd/northd.c      | 35 ++++++++++++++++++++++++++++-------
  ovn-nb.xml           |  7 +++++++
  ovn-sb.ovsschema     |  5 +++--
  ovn-sb.xml           | 10 +++++++++-
  7 files changed, 78 insertions(+), 20 deletions(-)

diff --git a/controller/pinctrl.c b/controller/pinctrl.c
index d4f4da731..fcb04eadd 100644
--- a/controller/pinctrl.c
+++ b/controller/pinctrl.c
@@ -7048,7 +7048,7 @@ sync_svc_monitors(struct ovsdb_idl_txn *ovnsb_idl_txn,
          const struct sbrec_port_binding *pb
              = lport_lookup_by_name(sbrec_port_binding_by_name,
                                     sb_svc_mon->logical_port);
-        if (!pb) {
+        if (!pb || !sb_svc_mon->local) {
              continue;
          }
diff --git a/northd/lb.c b/northd/lb.c
index b11896cf1..15a0b8651 100644
--- a/northd/lb.c
+++ b/northd/lb.c
@@ -120,20 +120,29 @@ ovn_lb_vip_backends_health_check_init(const struct 
ovn_northd_lb *lb,
          }
char *svc_mon_src_ip = NULL;
+        char *az_name = NULL;
          char *port_name = xstrdup(s);
-        char *p = strstr(port_name, ":");
-        if (p) {
-            *p = 0;
-            p++;
+
+        char *ip_part = strstr(port_name, ":");
+        if (ip_part) {
+            *ip_part = '\0';
+            ip_part++;
+
+            char *remote_marker = strstr(ip_part, ":remote:");
+            if (remote_marker) {
+                *remote_marker = '\0';
+                az_name = remote_marker + 8;

This should probably check that the az_name is non-zero in length.

+            }
+
              struct sockaddr_storage svc_mon_src_addr;
-            if (!inet_parse_address(p, &svc_mon_src_addr)) {
+            if (!inet_parse_address(ip_part, &svc_mon_src_addr)) {
                  static struct vlog_rate_limit rl =
                      VLOG_RATE_LIMIT_INIT(5, 1);
-                VLOG_WARN_RL(&rl, "Invalid svc mon src IP %s", p);
+                VLOG_WARN_RL(&rl, "Invalid svc mon src IP %s", ip_part);
              } else {
                  struct ds src_ip_s = DS_EMPTY_INITIALIZER;
                  ss_format_address_nobracks(&svc_mon_src_addr,
-                                            &src_ip_s);
+                                           &src_ip_s);
                  svc_mon_src_ip = ds_steal_cstr(&src_ip_s);
              }
          }
@@ -144,6 +153,12 @@ ovn_lb_vip_backends_health_check_init(const struct 
ovn_northd_lb *lb,
              backend_nb->health_check = true;
              backend_nb->logical_port = xstrdup(port_name);
              backend_nb->svc_mon_src_ip = svc_mon_src_ip;
+            if (az_name) {
+                backend_nb->local_backend = false;
+                backend_nb->az_name = xstrdup(az_name);
+            } else {
+                backend_nb->local_backend = true;
+            }
          }
          free(port_name);
      }
@@ -158,6 +173,7 @@ void ovn_northd_lb_vip_destroy(struct ovn_northd_lb_vip 
*vip)
      for (size_t i = 0; i < vip->n_backends; i++) {
          free(vip->backends_nb[i].logical_port);
          free(vip->backends_nb[i].svc_mon_src_ip);
+        free(vip->backends_nb[i].az_name);
      }
      free(vip->backends_nb);
  }
diff --git a/northd/lb.h b/northd/lb.h
index eb1942bd4..6de953cfc 100644
--- a/northd/lb.h
+++ b/northd/lb.h
@@ -88,8 +88,13 @@ struct ovn_northd_lb_vip {
struct ovn_northd_lb_backend {
      bool health_check;
-    char *logical_port; /* Logical port to which the ip belong to. */
-    char *svc_mon_src_ip; /* Source IP to use for monitoring. */
+     /* Set to true if port locates in current ovn cluster. */

nit: Instead of saying "current ovn cluster", we should say "local AZ".

+    bool local_backend;
+    /* Logical port to which the ip belong to. */
+    char *logical_port;
+    char *svc_mon_src_ip;
+    /* Target az_name for service monitor. */
+    char *az_name;
  };
struct ovn_northd_lb *ovn_northd_lb_create(const struct nbrec_load_balancer *);
diff --git a/northd/northd.c b/northd/northd.c
index a7be29619..c83348fe5 100644
--- a/northd/northd.c
+++ b/northd/northd.c
@@ -3730,13 +3730,28 @@ get_service_mon(const struct hmap *local_monitor_map,
      return NULL;
  }
+static void
+set_service_mon_options(const struct sbrec_service_monitor *sbrec_mon,
+                        const struct smap *nb_hc_options,
+                        const char *target_az_name)
+{
+    struct smap sb_svc_options = SMAP_INITIALIZER(&sb_svc_options);
+
+    smap_clone(&sb_svc_options, nb_hc_options);
+    if (target_az_name) {
+        smap_add(&sb_svc_options, "az-name", target_az_name);
+    }
+    sbrec_service_monitor_set_options(sbrec_mon, &sb_svc_options);
+    smap_destroy(&sb_svc_options);
+}
+
  static struct service_monitor_info *
  create_or_get_service_mon(struct ovsdb_idl_txn *ovnsb_txn,
                            struct hmap *local_monitor_map,
                            struct hmap *ic_learned_monitor_map,
                            const char *ip, const char *logical_port,
                            uint16_t service_port, const char *protocol,
-                          const char *chassis_name)
+                          const char *chassis_name, bool local_backend)
  {
      struct service_monitor_info *mon_info =
          get_service_mon(local_monitor_map, ic_learned_monitor_map,
@@ -3762,6 +3777,8 @@ create_or_get_service_mon(struct ovsdb_idl_txn *ovnsb_txn,
      sbrec_service_monitor_set_port(sbrec_mon, service_port);
      sbrec_service_monitor_set_logical_port(sbrec_mon, logical_port);
      sbrec_service_monitor_set_protocol(sbrec_mon, protocol);
+    sbrec_service_monitor_set_local(sbrec_mon, local_backend);
+    sbrec_service_monitor_set_ic_learned(sbrec_mon, false);
      if (chassis_name) {
          sbrec_service_monitor_set_chassis_name(sbrec_mon, chassis_name);
      }
@@ -3803,7 +3820,8 @@ ovn_lb_svc_create(struct ovsdb_idl_txn *ovnsb_txn,
              struct ovn_port *op = ovn_port_find(ls_ports,
                                                  backend_nb->logical_port);
- if (!op || !lsp_is_enabled(op->nbsp)) {
+            if (backend_nb->local_backend &&
+                (!op || !lsp_is_enabled(op->nbsp))) {
                  continue;
              }
@@ -3813,7 +3831,7 @@ ovn_lb_svc_create(struct ovsdb_idl_txn *ovnsb_txn,
              }
const char *chassis_name = NULL;
-            if (op->sb->chassis) {
+            if (backend_nb->local_backend && op->sb->chassis) {
                  chassis_name = op->sb->chassis->name;
              }
@@ -3825,10 +3843,12 @@ ovn_lb_svc_create(struct ovsdb_idl_txn *ovnsb_txn,
                                            backend_nb->logical_port,
                                            backend->port,
                                            protocol,
-                                          chassis_name);
+                                          chassis_name,
+                                          backend_nb->local_backend);
              ovs_assert(mon_info);
-            sbrec_service_monitor_set_options(
-                mon_info->sbrec_mon, &lb_vip_nb->lb_health_check->options);
+            set_service_mon_options(mon_info->sbrec_mon,
+                                    &lb_vip_nb->lb_health_check->options,
+                                    backend_nb->az_name);
              struct eth_addr ea;
              if (!mon_info->sbrec_mon->src_mac ||
                  !eth_addr_from_string(mon_info->sbrec_mon->src_mac, &ea) ||
@@ -3845,7 +3865,8 @@ ovn_lb_svc_create(struct ovsdb_idl_txn *ovnsb_txn,
                      backend_nb->svc_mon_src_ip);
              }
- if ((!op->sb->n_up || !op->sb->up[0])
+            if (backend_nb->local_backend &&
+                (!op->sb->n_up || !op->sb->up[0])
                  && mon_info->sbrec_mon->status
                  && !strcmp(mon_info->sbrec_mon->status, "online")) {
                  sbrec_service_monitor_set_status(mon_info->sbrec_mon,
diff --git a/ovn-nb.xml b/ovn-nb.xml
index 4a7581807..674dfdc0c 100644
--- a/ovn-nb.xml
+++ b/ovn-nb.xml
@@ -2231,6 +2231,9 @@
            Health checks are sent to this port with the specified source IP.
            For IPv6 square brackets must be used around IP address, e.g:
            <code><var>port_name</var>:<var>[sourc_ip]</var></code>
+          Remote endpoint:
+          Use the :remote:<code>az-name</code> syntax to create remote health
+          checks in a specific zone.
          </p>
<p>
@@ -2238,11 +2241,15 @@
            defined as <code>10.0.0.4</code>=<code>sw0-p1:10.0.0.2</code> and
            <code>20.0.0.4</code>=<code>sw1-p1:20.0.0.2</code>, if the values
            given were suitable ports and IP addresses.
+          And remote endpoint:
+          <code>10.0.0.4</code>=<code>sw0-p1:10.0.0.2</code>:remote:az1, where
+          <code>sw0-p1</code> - logical port in <code>az1</code>.
          </p>
<p>
            For IPv6 IP to port mappings might be defined as
            <code>[2001::1]</code>=<code>sw0-p1:[2002::1]</code>.
+          Remote endpoint: same as for IP.
          </p>
        </column>
      </group>
diff --git a/ovn-sb.ovsschema b/ovn-sb.ovsschema
index da1fd38e8..91763b7f8 100644
--- a/ovn-sb.ovsschema
+++ b/ovn-sb.ovsschema
@@ -1,7 +1,7 @@
  {
      "name": "OVN_Southbound",
-    "version": "21.3.0",
-    "cksum": "3512532069 34910",
+    "version": "21.4.0",
+    "cksum": "1850884918 34956",
      "tables": {
          "SB_Global": {
              "columns": {
@@ -517,6 +517,7 @@
                  "src_mac": {"type": "string"},
                  "src_ip": {"type": "string"},
                  "chassis_name": {"type": "string"},
+                "local": {"type": "boolean"},
                  "ic_learned": {"type": "boolean"},
                  "status": {
                      "type": {"key": {"type": "string",
diff --git a/ovn-sb.xml b/ovn-sb.xml
index f7eafd379..08b4892f5 100644
--- a/ovn-sb.xml
+++ b/ovn-sb.xml
@@ -4967,6 +4967,10 @@ tcp.flags = RST;
        The service can be an IPv4 TCP or UDP
        service. <code>ovn-controller</code> periodically sends out service
        monitor packets and updates the status of the service.
+      If the service monitor is not local, then ovn-ic creates a record about
+      it in the SBDB database, after which another OVN deployment creates a
+      record about the Service Monitor in its SBDB and the status is
+      propagated back to the initial record in the original availability zone.
      </p>
<p>
@@ -4981,7 +4985,7 @@ tcp.flags = RST;
        </p>
<column name="ip">
-        IP of the service to be monitored. Only IPv4 is supported.
+        IP of the service to be monitored.
        </column>
<column name="protocol">
@@ -5010,6 +5014,10 @@ tcp.flags = RST;
          The name of the chassis where the logical port is bound.
        </column>
+ <column name="local">
+        Set to true if backend locates on local ovn deployment.
+      </column>
+
        <column name="ic_learned">
          Set to true if the service monitor was propagated from another
          OVN deployment via ovn-ic management.

_______________________________________________
dev mailing list
d...@openvswitch.org
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to