On 2/17/26 6:58 PM, Alexandra Rukomoinikova wrote:
> Add logic to process logical switch port health checks in northd.
> 
> Signed-off-by: Alexandra Rukomoinikova <[email protected]>
> ---
>  v2 --> v3: fixed Dumitru's comments
>             split port_addresses_contains_ip func into two separate check for 
> IPv4/6
> ---

Hi Alexandra,

Thanks for this new version!

>  lib/ovn-util.c                   |  56 +++++++++
>  lib/ovn-util.h                   |   4 +
>  northd/en-northd.c               |   2 +
>  northd/inc-proc-northd.c         |   5 +-
>  northd/northd.c                  | 187 +++++++++++++++++++++++++++----
>  northd/northd.h                  |   3 +
>  tests/ovn-inc-proc-graph-dump.at |   2 +
>  tests/ovn-northd.at              |  90 +++++++++++++++
>  8 files changed, 324 insertions(+), 25 deletions(-)
> 
> diff --git a/lib/ovn-util.c b/lib/ovn-util.c
> index f34c37a79..e743e41e7 100644
> --- a/lib/ovn-util.c
> +++ b/lib/ovn-util.c
> @@ -521,6 +521,62 @@ find_lport_address(const struct lport_addresses *laddrs, 
> const char *ip_s)
>      return NULL;
>  }
>  
> +static bool
> +contains_ipv4_address(const struct lport_addresses *lsp_addrs,
> +                      unsigned int n_lsp_addrs, ovs_be32 ip4)
> +{
> +    for (unsigned int i = 0; i < n_lsp_addrs; i++) {
> +        const struct lport_addresses *laddrs = &lsp_addrs[i];
> +        const size_t n = laddrs->n_ipv4_addrs;

Nit: no need for const.

> +
> +        for (size_t j = 0; j < n; j++) {
> +            if (laddrs->ipv4_addrs[j].addr == ip4) {
> +                return true;
> +            }
> +        }
> +    }
> +
> +    return false;
> +}
> +
> +static bool
> +contains_ipv6_address(const struct lport_addresses *lsp_addrs,
> +                      unsigned int n_lsp_addrs,
> +                      const struct in6_addr *ip6)
> +{
> +    for (unsigned int i = 0; i < n_lsp_addrs; i++) {
> +        const struct lport_addresses *laddrs = &lsp_addrs[i];
> +        const size_t n = laddrs->n_ipv6_addrs;

Nit: no need for const.

> +
> +        for (size_t j = 0; j < n; j++) {
> +            if (IN6_ARE_ADDR_EQUAL(&laddrs->ipv6_addrs[j].addr, ip6)) {
> +                return true;
> +            }
> +        }
> +    }
> +
> +    return false;
> +}
> +
> +bool
> +lport_addresses_contains_ip(const struct lport_addresses *lsp_addrs,
> +                            unsigned int n_lsp_addrs,

It's better to use 'size_t' instead of 'unsigned int'.

> +                            const char *ip_s)
> +{
> +    struct in6_addr ip6;
> +    ovs_be32 ip4;
> +
> +    if (ip_parse(ip_s, &ip4)) {
> +        return contains_ipv4_address(lsp_addrs, n_lsp_addrs, ip4);
> +    }
> +
> +    if (ipv6_parse(ip_s, &ip6)) {
> +        return contains_ipv6_address(lsp_addrs, n_lsp_addrs, &ip6);
> +    }
> +
> +    return false;
> +}
> +
>  /* Go through 'addresses' and add found IPv4 addresses to 'ipv4_addrs' and
>   * IPv6 addresses to 'ipv6_addrs'. */
>  void
> diff --git a/lib/ovn-util.h b/lib/ovn-util.h
> index 0efeb217c..0239ec2ec 100644
> --- a/lib/ovn-util.h
> +++ b/lib/ovn-util.h
> @@ -130,6 +130,10 @@ const char *find_lport_address(const struct 
> lport_addresses *laddrs,
>  void split_addresses(const char *addresses, struct svec *ipv4_addrs,
>                       struct svec *ipv6_addrs);
>  
> +bool lport_addresses_contains_ip(const struct lport_addresses *lport_address,
> +                                 unsigned int n_lsp_addrs,
> +                                 const char *ip_s);
> +
>  char *alloc_nat_zone_key(const char *name, const char *type);
>  
>  const char *default_nb_db(void);
> diff --git a/northd/en-northd.c b/northd/en-northd.c
> index 92be3eb2f..ef74175a2 100644
> --- a/northd/en-northd.c
> +++ b/northd/en-northd.c
> @@ -80,6 +80,8 @@ northd_get_input_data(struct engine_node *node,
>          EN_OVSDB_GET(engine_get_input("NB_network_function", node));
>      input_data->nbrec_network_function_group_table =
>          EN_OVSDB_GET(engine_get_input("NB_network_function_group", node));
> +    input_data->nbrec_lsp_hc_table = EN_OVSDB_GET(engine_get_input(
> +        "NB_logical_switch_port_health_check", node));
>  
>      input_data->sbrec_port_binding_table =
>          EN_OVSDB_GET(engine_get_input("SB_port_binding", node));
> diff --git a/northd/inc-proc-northd.c b/northd/inc-proc-northd.c
> index 21e6aedcc..732066638 100644
> --- a/northd/inc-proc-northd.c
> +++ b/northd/inc-proc-northd.c
> @@ -75,7 +75,8 @@ static unixctl_cb_func chassis_features_list;
>      NB_NODE(chassis_template_var) \
>      NB_NODE(sampling_app) \
>      NB_NODE(network_function) \
> -    NB_NODE(network_function_group)
> +    NB_NODE(network_function_group) \
> +    NB_NODE(logical_switch_port_health_check)
>  
>      enum nb_engine_node {
>  #define NB_NODE(NAME) NB_##NAME,
> @@ -256,6 +257,8 @@ void inc_proc_northd_init(struct ovsdb_idl_loop *nb,
>      engine_add_input(&en_northd, &en_nb_chassis_template_var, NULL);
>      engine_add_input(&en_northd, &en_nb_network_function, NULL);
>      engine_add_input(&en_northd, &en_nb_network_function_group, NULL);
> +    engine_add_input(&en_northd, &en_nb_logical_switch_port_health_check,
> +                     NULL);
>  
>      engine_add_input(&en_northd, &en_sb_chassis, NULL);
>      engine_add_input(&en_northd, &en_sb_mirror, NULL);
> diff --git a/northd/northd.c b/northd/northd.c
> index 7fe36b850..90d8edfe9 100644
> --- a/northd/northd.c
> +++ b/northd/northd.c
> @@ -3592,7 +3592,76 @@ build_lb_datapaths(const struct hmap *lbs, const 
> struct hmap *lb_groups,
>  }
>  
>  static void
> -build_svcs(
> +ovn_lsp_svc_monitors_process_port(struct ovsdb_idl_txn *ovnsb_txn,
> +                                  const struct ovn_port *op,
> +                                  const char *svc_monitor_mac,
> +                                  const struct eth_addr *svc_monitor_mac_ea,
> +                                  struct hmap *local_svc_monitors_map,
> +                                  struct sset *svc_monitor_lsps)
> +{
> +    sset_add(svc_monitor_lsps, op->key);
> +
> +    for (size_t i = 0; i < op->nbsp->n_health_checks; i++) {
> +        struct nbrec_logical_switch_port_health_check *lsp_hc =
> +            op->nbsp->health_checks[i];
> +        struct service_monitor_info *mon_info = NULL;
> +
> +        /* Check if this address is still on the port */
> +        bool lsp_contain_ip =
> +            lport_addresses_contains_ip(op->lsp_addrs, op->n_lsp_addrs,
> +                                        lsp_hc->address);
> +
> +        /* Remove outdated records */
> +        if (!lsp_contain_ip) {
> +            mon_info = get_service_mon(local_svc_monitors_map,
> +                                       NULL, lsp_hc->address,
> +                                       op->key, lsp_hc->port,
> +                                       lsp_hc->protocol);
> +            if (mon_info) {
> +                sbrec_service_monitor_delete(mon_info->sbrec_mon);
> +                hmap_remove(local_svc_monitors_map, &mon_info->hmap_node);
> +                free(mon_info);
> +            }
> +            continue;
> +        }
> +
> +        mon_info = create_or_get_service_mon(ovnsb_txn,
> +                                             local_svc_monitors_map,
> +                                             NULL, "logical-switch-port",
> +                                             lsp_hc->address, op->key, NULL,
> +                                             lsp_hc->port, lsp_hc->protocol,
> +                                             (op->sb && op->sb->chassis) ?
> +                                              op->sb->chassis->name : NULL,

Nit: according to our coding guidelines the "?" should be on the next line.

> +                                             false);
> +
> +        mon_info->required = true;
> +        set_service_mon_options(mon_info->sbrec_mon,
> +                                &lsp_hc->options, NULL);

This fits on one line.

> +
> +        struct eth_addr ea;
> +        if (!mon_info->sbrec_mon->src_mac ||
> +            !eth_addr_from_string(mon_info->sbrec_mon->src_mac, &ea) ||
> +            !eth_addr_equals(ea, *svc_monitor_mac_ea)) {
> +            sbrec_service_monitor_set_src_mac(mon_info->sbrec_mon,
> +                                              svc_monitor_mac);
> +        }
> +
> +        if (!mon_info->sbrec_mon->src_ip ||
> +            strcmp(mon_info->sbrec_mon->src_ip, lsp_hc->src_ip)) {
> +            sbrec_service_monitor_set_src_ip(mon_info->sbrec_mon,
> +                                             lsp_hc->src_ip);
> +        }
> +
> +        if ((!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, "offline");
> +        }
> +    }
> +}
> +
> +static void
> +build_svc_monitors_data(
>      struct ovsdb_idl_txn *ovnsb_txn,
>      struct ovsdb_idl_index *sbrec_service_monitor_by_learned_type,
>      const char *svc_monitor_mac,
> @@ -3604,7 +3673,8 @@ build_svcs(
>      const struct nbrec_network_function_table *nbrec_network_function_table,
>      struct sset *svc_monitor_lsps,
>      struct hmap *local_svc_monitors_map,
> -    struct hmap *ic_learned_svc_monitors_map)
> +    struct hmap *ic_learned_svc_monitors_map,
> +    struct hmapx *monitored_ports_map)
>  {
>      const struct sbrec_service_monitor *sbrec_mon;
>      struct sbrec_service_monitor *key =
> @@ -3654,6 +3724,16 @@ build_svcs(
>          }
>      }
>  
> +    struct hmapx_node *hmapx_node;
> +    const struct ovn_port *op;

The definition of 'op' fits better inside the loop, then we don't get
scope creep.

> +    HMAPX_FOR_EACH (hmapx_node, monitored_ports_map) {
> +        op = hmapx_node->data;
> +        ovn_lsp_svc_monitors_process_port(ovnsb_txn, op, svc_monitor_mac,
> +                                          svc_monitor_mac_ea,
> +                                          local_svc_monitors_map,
> +                                          svc_monitor_lsps);
> +    }
> +
>      struct service_monitor_info *mon_info;
>      HMAP_FOR_EACH_SAFE (mon_info, hmap_node, local_svc_monitors_map) {
>          if (!mon_info->required) {
> @@ -4134,6 +4214,16 @@ ovn_port_allocate_key(struct ovn_port *op)
>      return true;
>  }
>  
> +static inline void

No need for "inline".

> +add_hc_monitored_port(struct ovn_port *op, struct hmapx *monitored_ports_map)
> +{
> +    if (op->nbsp &&
> +        op->nbsp->n_health_checks &&
> +        lsp_is_enabled(op->nbsp)) {

This fits on one line.

> +        hmapx_add(monitored_ports_map, op);
> +    }
> +}
> +
>  /* Updates the southbound Port_Binding table so that it contains the logical
>   * switch ports specified by the northbound database.
>   *
> @@ -4150,7 +4240,8 @@ build_ports(struct ovsdb_idl_txn *ovnsb_txn,
>      struct ovsdb_idl_index *sbrec_chassis_by_hostname,
>      struct ovsdb_idl_index *sbrec_ha_chassis_grp_by_name,
>      struct hmap *ls_datapaths, struct hmap *lr_datapaths,
> -    struct hmap *ls_ports, struct hmap *lr_ports)
> +    struct hmap *ls_ports, struct hmap *lr_ports,
> +    struct hmapx *monitored_ports_map)
>  {
>      struct ovs_list sb_only, nb_only, both;
>      /* XXX: Add tag_alloc_table and queue_id_bitmap as part of northd_data
> @@ -4225,6 +4316,7 @@ build_ports(struct ovsdb_idl_txn *ovnsb_txn,
>                                op, queue_id_bitmap,
>                                &active_ha_chassis_grps);
>          op->od->is_transit_router |= is_transit_router_port(op);
> +        add_hc_monitored_port(op, monitored_ports_map);
>          ovs_list_remove(&op->list);
>      }
>  
> @@ -4239,6 +4331,7 @@ build_ports(struct ovsdb_idl_txn *ovnsb_txn,
>                                &active_ha_chassis_grps);
>          sbrec_port_binding_set_logical_port(op->sb, op->key);
>          op->od->is_transit_router |= is_transit_router_port(op);
> +        add_hc_monitored_port(op, monitored_ports_map);
>          ovs_list_remove(&op->list);
>      }
>  
> @@ -4420,6 +4513,11 @@ lsp_can_be_inc_processed(const struct 
> nbrec_logical_switch_port *nbsp)
>          }
>      }
>  
> +     /* Attaching health check to port is not supported for now. */
> +    if (nbsp->n_health_checks) {
> +        return false;
> +    }
> +

This is not enough, we also need to handle the "update" case, similar to
what we do for mirrors (lsp_handle_mirror_rules_changes()), i.e.:

static bool
lsp_handle_health_check_changes(const struct ovn_port *op)
{
    /* Changes to health check configuration cannot be incrementally
     * processed. */
    return !nbrec_logical_switch_port_is_updated(
        op->nbsp,
        NBREC_LOGICAL_SWITCH_PORT_COL_HEALTH_CHECKS);
}

and call this on the "update" path of ls_handle_lsp_changes().

>      return true;
>  }
>  
> @@ -15613,6 +15711,39 @@ build_arp_resolve_flows_for_lsp(
>      }
>  }
>  
> +static void
> +build_arp_nd_lflow_for_lsp_svc_hc(struct ovn_port *op,
> +                                  const char *svc_monitor_mac,
> +                                  struct lflow_table *lflows,
> +                                  struct ds *match,
> +                                  struct ds *actions)
> +{
> +    const struct nbrec_logical_switch_port *nbsp = op->nbsp;
> +    for (size_t i = 0; i < nbsp->n_health_checks; i++) {
> +        struct nbrec_logical_switch_port_health_check *lsp_hc =
> +            nbsp->health_checks[i];
> +
> +        /* Check if this address is still on the port */
> +        if (!lport_addresses_contains_ip(op->lsp_addrs, op->n_lsp_addrs,
> +                                         lsp_hc->address)) {
> +            continue;
> +        }
> +
> +        ds_clear(match);
> +        ds_clear(actions);
> +
> +        bool is_ipv4 = strchr(lsp_hc->src_ip, '.') ? true : false;
> +
> +        build_arp_nd_service_monitor_lflow(svc_monitor_mac, lsp_hc->src_ip,
> +                                           actions, match, is_ipv4);
> +
> +        ovn_lflow_add(lflows, op->od, S_SWITCH_IN_ARP_ND_RSP, 110,
> +                      ds_cstr(match), ds_cstr(actions),
> +                      op->lflow_ref,
> +                      WITH_HINT(&op->nbsp->header_));
> +    }
> +}
> +
>  #define ICMP4_NEED_FRAG_FORMAT                           \
>      "icmp4_error {"                                      \
>      "%s"                                                 \
> @@ -19165,6 +19296,7 @@ build_lswitch_and_lrouter_iterate_by_lsp(struct 
> ovn_port *op,
>                                           const struct shash *meter_groups,
>                                           struct ds *match,
>                                           struct ds *actions,
> +                                         const char *svc_monitor_mac,
>                                           struct lflow_table *lflows)
>  {
>      ovs_assert(op->nbsp);
> @@ -19182,12 +19314,13 @@ build_lswitch_and_lrouter_iterate_by_lsp(struct 
> ovn_port *op,
>      build_lswitch_dhcp_options_and_response(op, lflows, meter_groups);
>      build_lswitch_external_port(op, lflows);
>      build_lswitch_icmp_packet_toobig_admin_flows(op, lflows, match, actions);
> -    build_lswitch_ip_unicast_lookup(op, lflows, actions,
> -                                    match);
> +    build_lswitch_ip_unicast_lookup(op, lflows, actions, match);

Unrelated change.

>      build_lswitch_dhcp_relay_flows(op, ls_ports, lflows, match, actions);
>  
>      /* Build Logical Router Flows. */
>      build_arp_resolve_flows_for_lsp(op, lflows, lr_ports, match, actions);
> +    build_arp_nd_lflow_for_lsp_svc_hc(op, svc_monitor_mac, lflows,
> +                                      match, actions);
>  }
>  
>  /* Helper function to combine all lflow generation which is iterated by 
> logical
> @@ -19295,12 +19428,10 @@ build_lflows_thread(void *arg)
>                      if (stop_parallel_processing()) {
>                          return NULL;
>                      }
> -                    build_lswitch_and_lrouter_iterate_by_lsp(op, 
> lsi->ls_ports,
> -                                                             lsi->lr_ports,
> -                                                             
> lsi->meter_groups,
> -                                                             &lsi->match,
> -                                                             &lsi->actions,
> -                                                             lsi->lflows);
> +                    build_lswitch_and_lrouter_iterate_by_lsp(
> +                        op, lsi->ls_ports, lsi->lr_ports, lsi->meter_groups,
> +                        &lsi->match, &lsi->actions, lsi->svc_monitor_mac,
> +                        lsi->lflows);
>                      build_lbnat_lflows_iterate_by_lsp(
>                          op, lsi->lr_stateful_table, &lsi->match,
>                          &lsi->actions, lsi->lflows);
> @@ -19584,6 +19715,7 @@ build_lswitch_and_lrouter_flows(
>                                                       lsi.meter_groups,
>                                                       &lsi.match,
>                                                       &lsi.actions,
> +                                                     lsi.svc_monitor_mac,
>                                                       lsi.lflows);
>              build_lbnat_lflows_iterate_by_lsp(op, lsi.lr_stateful_table,
>                                                &lsi.match,
> @@ -19841,6 +19973,7 @@ lflow_handle_northd_port_changes(struct ovsdb_idl_txn 
> *ovnsb_txn,
>                                                   lflow_input->lr_ports,
>                                                   lflow_input->meter_groups,
>                                                   &match, &actions,
> +                                                 
> lflow_input->svc_monitor_mac,
>                                                   lflows);
>          /* Sync the new flows to SB. */
>          bool handled = lflow_ref_sync_lflows(
> @@ -19898,7 +20031,9 @@ lflow_handle_northd_port_changes(struct ovsdb_idl_txn 
> *ovnsb_txn,
>          build_lswitch_and_lrouter_iterate_by_lsp(op, lflow_input->ls_ports,
>                                                   lflow_input->lr_ports,
>                                                   lflow_input->meter_groups,
> -                                                 &match, &actions, lflows);
> +                                                 &match, &actions,
> +                                                 
> lflow_input->svc_monitor_mac,
> +                                                 lflows);
>  
>          /* Sync the newly added flows to SB. */
>          bool handled = lflow_ref_sync_lflows(
> @@ -20601,6 +20736,7 @@ northd_init(struct northd_data *data)
>      hmap_init(&data->lb_group_datapaths_map);
>      sset_init(&data->svc_monitor_lsps);
>      hmap_init(&data->local_svc_monitors_map);
> +    hmapx_init(&data->monitored_ports_map);
>      init_northd_tracked_data(data);
>  }
>  
> @@ -20686,6 +20822,7 @@ northd_destroy(struct northd_data *data)
>                                  &data->ls_ports, &data->lr_ports);
>  
>      sset_destroy(&data->svc_monitor_lsps);
> +    hmapx_destroy(&data->monitored_ports_map);
>      destroy_northd_tracked_data(data);
>  }
>  
> @@ -20791,21 +20928,23 @@ ovnnb_db_run(struct northd_input *input_data,
>                  input_data->sbrec_chassis_by_hostname,
>                  input_data->sbrec_ha_chassis_grp_by_name,
>                  &data->ls_datapaths.datapaths, &data->lr_datapaths.datapaths,
> -                &data->ls_ports, &data->lr_ports);
> +                &data->ls_ports, &data->lr_ports,
> +                &data->monitored_ports_map);
>      build_lb_port_related_data(&data->lr_datapaths, &data->ls_datapaths,
>                                 &data->lb_datapaths_map,
>                                 &data->lb_group_datapaths_map);
> -    build_svcs(ovnsb_txn,
> -               input_data->sbrec_service_monitor_by_learned_type,
> -               input_data->svc_monitor_mac,
> -               &input_data->svc_monitor_mac_ea,
> -               input_data->svc_monitor_mac_dst,
> -               input_data->svc_monitor_ip,
> -               input_data->svc_monitor_ip_dst,
> -               &data->ls_ports, &data->lb_datapaths_map,
> -               input_data->nbrec_network_function_table,
> -               &data->svc_monitor_lsps, &data->local_svc_monitors_map,
> -               input_data->ic_learned_svc_monitors_map);
> +    build_svc_monitors_data(ovnsb_txn,
> +        input_data->sbrec_service_monitor_by_learned_type,
> +        input_data->svc_monitor_mac,
> +        &input_data->svc_monitor_mac_ea,
> +        input_data->svc_monitor_mac_dst,
> +        input_data->svc_monitor_ip,
> +        input_data->svc_monitor_ip_dst,
> +        &data->ls_ports, &data->lb_datapaths_map,
> +        input_data->nbrec_network_function_table,
> +        &data->svc_monitor_lsps, &data->local_svc_monitors_map,
> +        input_data->ic_learned_svc_monitors_map,
> +        &data->monitored_ports_map);
>      build_lb_count_dps(&data->lb_datapaths_map);
>      build_network_function_active(
>          input_data->nbrec_network_function_group_table,
> diff --git a/northd/northd.h b/northd/northd.h
> index 25c8a909a..c2571fb30 100644
> --- a/northd/northd.h
> +++ b/northd/northd.h
> @@ -44,6 +44,8 @@ struct northd_input {
>      const struct nbrec_network_function_table *nbrec_network_function_table;
>      const struct nbrec_network_function_group_table
>          *nbrec_network_function_group_table;
> +    const struct nbrec_logical_switch_port_health_check_table
> +        *nbrec_lsp_hc_table;

This is unused and can be removed.

>  
>      /* Southbound table references */
>      const struct sbrec_port_binding_table *sbrec_port_binding_table;
> @@ -203,6 +205,7 @@ struct northd_data {
>      struct hmap lb_group_datapaths_map;
>      struct sset svc_monitor_lsps;
>      struct hmap local_svc_monitors_map;
> +    struct hmapx monitored_ports_map;
>  
>      /* Change tracking data. */
>      struct northd_tracked_data trk_data;
> diff --git a/tests/ovn-inc-proc-graph-dump.at 
> b/tests/ovn-inc-proc-graph-dump.at
> index 3fe7b8fbd..ff2c8c0c7 100644
> --- a/tests/ovn-inc-proc-graph-dump.at
> +++ b/tests/ovn-inc-proc-graph-dump.at
> @@ -18,6 +18,7 @@ digraph "Incremental-Processing-Engine" {
>       NB_chassis_template_var [[style=filled, shape=box, fillcolor=white, 
> label="NB_chassis_template_var"]];
>       NB_network_function [[style=filled, shape=box, fillcolor=white, 
> label="NB_network_function"]];
>       NB_network_function_group [[style=filled, shape=box, fillcolor=white, 
> label="NB_network_function_group"]];
> +     NB_logical_switch_port_health_check [[style=filled, shape=box, 
> fillcolor=white, label="NB_logical_switch_port_health_check"]];
>       SB_chassis [[style=filled, shape=box, fillcolor=white, 
> label="SB_chassis"]];
>       SB_mirror [[style=filled, shape=box, fillcolor=white, 
> label="SB_mirror"]];
>       SB_meter [[style=filled, shape=box, fillcolor=white, label="SB_meter"]];
> @@ -75,6 +76,7 @@ digraph "Incremental-Processing-Engine" {
>       NB_chassis_template_var -> northd [[label=""]];
>       NB_network_function -> northd [[label=""]];
>       NB_network_function_group -> northd [[label=""]];
> +     NB_logical_switch_port_health_check -> northd [[label=""]];
>       SB_chassis -> northd [[label=""]];
>       SB_mirror -> northd [[label=""]];
>       SB_meter -> northd [[label=""]];
> diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
> index 605d2ec4f..b4bf75fee 100644
> --- a/tests/ovn-northd.at
> +++ b/tests/ovn-northd.at
> @@ -19810,3 +19810,93 @@ AT_CHECK([grep "ls1-to-spine" ls1flows | 
> ovn_strip_lflows | sort], [0], [dnl
>  
>  OVN_CLEANUP_NORTHD
>  AT_CLEANUP
> +
> +AT_SETUP([Logical Switch Port Health Check - lflow/service monitor 
> synchronization])
> +ovn_start
> +
> +check ovn-nbctl ls-add ls1
> +check ovn-nbctl lsp-add ls1 lport1 -- \
> +    lsp-set-addresses lport1 "f0:00:0f:01:02:04 192.168.0.10" 
> "f0:00:0f:01:02:06 192.168.0.11"
> +
> +check ovn-nbctl set NB_Global . options:svc_monitor_mac="11:11:11:11:11:11"
> +
> +# Create service monitor for all lsp addresses.
> +check ovn-nbctl lsp-hc-add lport1 icmp 192.168.0.255 192.168.0.10
> +check ovn-nbctl lsp-hc-add lport1 icmp 192.168.0.255 192.168.0.11

The check below is racy if we don't sync here:
check ovn-nbctl --wait=sb sync

> +
> +# Check lflow and service monitor synchronization
> +AT_CHECK([ovn-sbctl lflow-list ls1 | grep 'ls_in_arp_rsp'| grep 
> "priority=110" | ovn_strip_lflows], [0], [dnl
> +  table=??(ls_in_arp_rsp      ), priority=110  , match=(arp.tpa == 
> 192.168.0.255 && arp.op == 1), action=(eth.dst = eth.src; eth.src = 
> 11:11:11:11:11:11; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; arp.sha = 
> 11:11:11:11:11:11; arp.tpa = arp.spa; arp.spa = 192.168.0.255; outport = 
> inport; flags.loopback = 1; output;)
> +])
> +
> +check_row_count sb:Service_Monitor 2
> +check_column "false false" sb:Service_Monitor ic_learned logical_port=lport1
> +check_column "false false" sb:Service_Monitor remote logical_port=lport1
> +check_column "192.168.0.10 192.168.0.11" sb:Service_Monitor ip 
> logical_port=lport1
> +check_column "icmp icmp" sb:Service_Monitor protocol logical_port=lport1
> +check_column "192.168.0.255 192.168.0.255" sb:Service_Monitor src_ip 
> logical_port=lport1
> +check_column "0 0" sb:Service_Monitor port logical_port=lport1
> +check_column "logical-switch-port logical-switch-port" sb:Service_Monitor 
> type logical_port=lport1
> +check_column "11:11:11:11:11:11 11:11:11:11:11:11" sb:Service_Monitor 
> src_mac logical_port=lport1
> +
> +# Create one more service monitor for all lsp addresses.
> +check ovn-nbctl lsp-hc-add lport1 tcp 192.168.0.254 80 192.168.0.10
> +check ovn-nbctl lsp-hc-add lport1 tcp 192.168.0.254 80 192.168.0.11

Same here.

> +
> +AT_CHECK([ovn-sbctl lflow-list ls1 | grep 'ls_in_arp_rsp'| grep 
> "priority=110" | ovn_strip_lflows], [0], [dnl
> +  table=??(ls_in_arp_rsp      ), priority=110  , match=(arp.tpa == 
> 192.168.0.254 && arp.op == 1), action=(eth.dst = eth.src; eth.src = 
> 11:11:11:11:11:11; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; arp.sha = 
> 11:11:11:11:11:11; arp.tpa = arp.spa; arp.spa = 192.168.0.254; outport = 
> inport; flags.loopback = 1; output;)
> +  table=??(ls_in_arp_rsp      ), priority=110  , match=(arp.tpa == 
> 192.168.0.255 && arp.op == 1), action=(eth.dst = eth.src; eth.src = 
> 11:11:11:11:11:11; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; arp.sha = 
> 11:11:11:11:11:11; arp.tpa = arp.spa; arp.spa = 192.168.0.255; outport = 
> inport; flags.loopback = 1; output;)
> +])
> +
> +# Check options propogations
> +hc_lport1_uuid=$(fetch_column nb:logical_switch_port_health_check _uuid 
> address="192.168.0.10" protocol="tcp")
> +
> +check ovn-nbctl set logical_switch_port_health_check $hc_lport1_uuid 
> options:interval=3
> +check ovn-nbctl set logical_switch_port_health_check $hc_lport1_uuid 
> options:timeout=30
> +check ovn-nbctl set logical_switch_port_health_check $hc_lport1_uuid 
> options:success_count=1
> +check ovn-nbctl set logical_switch_port_health_check $hc_lport1_uuid 
> options:failure_count=2

And here.

> +
> +sm_lport1_uuid=$(fetch_column sb:service_monitor _uuid protocol="tcp" 
> ip="192.168.0.10")
> +
> +AT_CHECK([ovn-sbctl get Service_Monitor $sm_lport1_uuid options:interval],
> +[0], ["3"
> +])
> +AT_CHECK([ovn-sbctl get Service_Monitor $sm_lport1_uuid 
> options:failure_count],
> +[0], ["2"
> +])
> +AT_CHECK([ovn-sbctl get Service_Monitor $sm_lport1_uuid 
> options:success_count],
> +[0], ["1"
> +])
> +AT_CHECK([ovn-sbctl get Service_Monitor $sm_lport1_uuid options:timeout],
> +[0], ["30"
> +])
> +
> +check ovn-nbctl lsp-del lport1

This needs a --wait=sb otherwise northd might not get a chance to run
and process the NB change.

> +check_row_count sb:Service_Monitor 0
> +AT_CHECK([ovn-sbctl lflow-list ls1 | grep 'ls_in_arp_rsp'| grep 
> "priority=110" | ovn_strip_lflows], [0], [dnl])
> +
> +# Create a port with health checks in one transaction - checking the 
> processing of incremental processing
> +check ovn-nbctl lsp-add ls1 lport1 -- \

Here too.

> +    lsp-set-addresses lport1 "f0:00:0f:01:02:04 192.168.0.10" 
> "f0:00:0f:01:02:06 192.168.0.11" -- \
> +    lsp-hc-add lport1 icmp 192.168.0.255 192.168.0.10 -- \
> +    lsp-hc-add lport1 icmp 192.168.0.255 192.168.0.11
> +
> +check_row_count sb:Service_Monitor 2
> +AT_CHECK([ovn-sbctl lflow-list ls1 | grep 'ls_in_arp_rsp'| grep 
> "priority=110" | ovn_strip_lflows], [0], [dnl
> +  table=??(ls_in_arp_rsp      ), priority=110  , match=(arp.tpa == 
> 192.168.0.255 && arp.op == 1), action=(eth.dst = eth.src; eth.src = 
> 11:11:11:11:11:11; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; arp.sha = 
> 11:11:11:11:11:11; arp.tpa = arp.spa; arp.spa = 192.168.0.255; outport = 
> inport; flags.loopback = 1; output;)
> +])
> +
> +check_row_count sb:Service_Monitor 2

This second check is not needed, we already check before the lflow-list
above, nothing changes in the SB in between.

> +
> +# Change the addresses on the switch port - expect records in SBDB to be 
> deleted.
> +lport1=$(fetch_column nb:logical_switch_port _uuid  name="lport1")
> +check ovn-nbctl clear logical_switch_port $lport1 addresses

Needs --wait=sb.

> +
> +check_row_count sb:Service_Monitor 0
> +AT_CHECK([ovn-sbctl lflow-list ls1 | grep 'ls_in_arp_rsp'| grep 
> "priority=110" | ovn_strip_lflows], [0], [])
> +
> +check ovn-nbctl lsp-set-addresses lport1 "f0:00:0f:01:02:04 192.168.0.10" 
> "f0:00:0f:01:02:06 192.168.0.11"

Here too.

> +check_row_count sb:Service_Monitor 2
> +
> +AT_CLEANUP
> +])

Stray '])'.

I took care of these minor changes and applied the patch to main.

Regards,
Dumitru

_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to