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