diff mbox series

[ovs-dev] northd: Consolidate load balancer processing functions.

Message ID 20220316161047.16392-1-dceara@redhat.com
State Accepted
Headers show
Series [ovs-dev] northd: Consolidate load balancer processing functions. | expand

Checks

Context Check Description
ovsrobot/apply-robot success apply and check: success
ovsrobot/github-robot-_Build_and_Test fail github build: failed
ovsrobot/github-robot-_ovn-kubernetes success github build: passed

Commit Message

Dumitru Ceara March 16, 2022, 4:10 p.m. UTC
This commit doesn't change any functionality, it just performs the
following refactor work:

a. consolidates the old build_ovn_lbs(), build_lrouter_lbs() and
   build_ovn_lr_lbs() functions into a single function, build_lbs(),
   that builds all the load balancer port-independent information.
   Part of this was not possible before because we were logging a
   warning message in build_ovn_lr_lbs() if a load balancer is
   applied to a non-gateway router with more than 1 distributed
   gateway ports.  We now move the log to build_lrouter_lbs_check()
   which is called after ports are processed.

b. consolidates all the load balancer processing that depends on logical
   ports being parsed too into the build_lb_port_related_data()
   function.

c. split out the part that syncs load balancers to the Southbound
   database.

This has two advantages:

1. it allows a simpler overview of what is being processed, that is:
   - build datapaths
   - build load balancer state that depends on datapaths
   - build ports
   - add load balancer state that depends on ports

2. it may enable further optimization as now the part that builds the
   datapath-dependent load balancer state can be processed separately,
   potentially in an incremental fashion.  This part of the northd
   processing has been shown to be quite time consuming in the past.

Signed-off-by: Dumitru Ceara <dceara@redhat.com>
---
 northd/northd.c | 319 ++++++++++++++++++++++++------------------------
 tests/ovn.at    |  14 ++-
 2 files changed, 167 insertions(+), 166 deletions(-)

Comments

Numan Siddique March 17, 2022, 4:45 p.m. UTC | #1
On Wed, Mar 16, 2022 at 12:11 PM Dumitru Ceara <dceara@redhat.com> wrote:
>
> This commit doesn't change any functionality, it just performs the
> following refactor work:
>
> a. consolidates the old build_ovn_lbs(), build_lrouter_lbs() and
>    build_ovn_lr_lbs() functions into a single function, build_lbs(),
>    that builds all the load balancer port-independent information.
>    Part of this was not possible before because we were logging a
>    warning message in build_ovn_lr_lbs() if a load balancer is
>    applied to a non-gateway router with more than 1 distributed
>    gateway ports.  We now move the log to build_lrouter_lbs_check()
>    which is called after ports are processed.
>
> b. consolidates all the load balancer processing that depends on logical
>    ports being parsed too into the build_lb_port_related_data()
>    function.
>
> c. split out the part that syncs load balancers to the Southbound
>    database.
>
> This has two advantages:
>
> 1. it allows a simpler overview of what is being processed, that is:
>    - build datapaths
>    - build load balancer state that depends on datapaths
>    - build ports
>    - add load balancer state that depends on ports
>
> 2. it may enable further optimization as now the part that builds the
>    datapath-dependent load balancer state can be processed separately,
>    potentially in an incremental fashion.  This part of the northd
>    processing has been shown to be quite time consuming in the past.
>
> Signed-off-by: Dumitru Ceara <dceara@redhat.com>

Thanks for the refactor.  The patch LGTM.  I went ahead and applied to
the main branch.

Numan

> ---
>  northd/northd.c | 319 ++++++++++++++++++++++++------------------------
>  tests/ovn.at    |  14 ++-
>  2 files changed, 167 insertions(+), 166 deletions(-)
>
> diff --git a/northd/northd.c b/northd/northd.c
> index b264fb850b..8c871171f5 100644
> --- a/northd/northd.c
> +++ b/northd/northd.c
> @@ -3732,53 +3732,28 @@ build_lb_vip_actions(struct ovn_lb_vip *lb_vip,
>  }
>
>  static void
> -build_ovn_lr_lbs(struct hmap *datapaths, struct hmap *lbs)
> +build_lrouter_lb_ips(struct ovn_datapath *od, const struct ovn_northd_lb *lb)
>  {
> -    struct ovn_northd_lb *lb;
> -    struct ovn_datapath *od;
> -
> -    HMAP_FOR_EACH (od, key_node, datapaths) {
> -        if (!od->nbr) {
> -            continue;
> -        }
> -        if (!smap_get(&od->nbr->options, "chassis")
> -            && od->n_l3dgw_ports != 1) {
> -            if (od->n_l3dgw_ports > 1 && od->has_lb_vip) {
> -                static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
> -                VLOG_WARN_RL(&rl, "Load-balancers are configured on logical "
> -                             "router %s, which has %"PRIuSIZE" distributed "
> -                             "gateway ports. Load-balancer is not supported "
> -                             "yet when there is more than one distributed "
> -                             "gateway port on the router.",
> -                             od->nbr->name, od->n_l3dgw_ports);
> -            }
> -            continue;
> -        }
> +    bool is_routable = smap_get_bool(&lb->nlb->options, "add_route",  false);
> +    const char *ip_address;
>
> -        for (size_t i = 0; i < od->nbr->n_load_balancer; i++) {
> -            const struct uuid *lb_uuid =
> -                &od->nbr->load_balancer[i]->header_.uuid;
> -            lb = ovn_northd_lb_find(lbs, lb_uuid);
> -            ovn_northd_lb_add_lr(lb, od);
> +    SSET_FOR_EACH (ip_address, &lb->ips_v4) {
> +        sset_add(&od->lb_ips_v4, ip_address);
> +        if (is_routable) {
> +            sset_add(&od->lb_ips_v4_routable, ip_address);
>          }
> -
> -        for (size_t i = 0; i < od->nbr->n_load_balancer_group; i++) {
> -            const struct nbrec_load_balancer_group *lbg =
> -                od->nbr->load_balancer_group[i];
> -            for (size_t j = 0; j < lbg->n_load_balancer; j++) {
> -                const struct uuid *lb_uuid =
> -                    &lbg->load_balancer[j]->header_.uuid;
> -                lb = ovn_northd_lb_find(lbs, lb_uuid);
> -                ovn_northd_lb_add_lr(lb, od);
> -            }
> +    }
> +    SSET_FOR_EACH (ip_address, &lb->ips_v6) {
> +        sset_add(&od->lb_ips_v6, ip_address);
> +        if (is_routable) {
> +            sset_add(&od->lb_ips_v6_routable, ip_address);
>          }
>      }
>  }
>
>  static void
> -build_ovn_lbs(struct northd_input *input_data,
> -              struct ovsdb_idl_txn *ovnsb_txn,
> -              struct hmap *datapaths, struct hmap *lbs)
> +build_lbs(struct northd_input *input_data, struct hmap *datapaths,
> +          struct hmap *lbs)
>  {
>      struct ovn_northd_lb *lb;
>
> @@ -3817,94 +3792,38 @@ build_ovn_lbs(struct northd_input *input_data,
>          }
>      }
>
> -    /* Delete any stale SB load balancer rows. */
> -    struct hmapx existing_lbs = HMAPX_INITIALIZER(&existing_lbs);
> -    const struct sbrec_load_balancer *sbrec_lb, *next;
> -    SBREC_LOAD_BALANCER_TABLE_FOR_EACH_SAFE (sbrec_lb, next,
> -                            input_data->sbrec_load_balancer_table) {
> -        const char *nb_lb_uuid = smap_get(&sbrec_lb->external_ids, "lb_id");
> -        struct uuid lb_uuid;
> -        if (!nb_lb_uuid || !uuid_from_string(&lb_uuid, nb_lb_uuid)) {
> -            sbrec_load_balancer_delete(sbrec_lb);
> -            continue;
> -        }
> -
> -        /* Delete any SB load balancer entries that refer to NB load balancers
> -         * that don't exist anymore or are not applied to switches anymore.
> -         *
> -         * There is also a special case in which duplicate LBs might be created
> -         * in the SB, e.g., due to the fact that OVSDB only ensures
> -         * "at-least-once" consistency for clustered database tables that
> -         * are not indexed in any way.
> -         */
> -        lb = ovn_northd_lb_find(lbs, &lb_uuid);
> -        if (!lb || !lb->n_nb_ls || !hmapx_add(&existing_lbs, lb)) {
> -            sbrec_load_balancer_delete(sbrec_lb);
> -        } else {
> -            lb->slb = sbrec_lb;
> -        }
> -    }
> -    hmapx_destroy(&existing_lbs);
> -
> -    /* Create SB Load balancer records if not present and sync
> -     * the SB load balancer columns. */
> -    HMAP_FOR_EACH (lb, hmap_node, lbs) {
> -
> -        if (!lb->n_nb_ls) {
> +    HMAP_FOR_EACH (od, key_node, datapaths) {
> +        if (!od->nbr) {
>              continue;
>          }
>
> -        /* Store the fact that northd provides the original (destination IP +
> -         * transport port) tuple.
> -         */
> -        struct smap options;
> -        smap_clone(&options, &lb->nlb->options);
> -        smap_replace(&options, "hairpin_orig_tuple", "true");
> -
> -        struct sbrec_datapath_binding **lb_dps =
> -            xmalloc(lb->n_nb_ls * sizeof *lb_dps);
> -        for (size_t i = 0; i < lb->n_nb_ls; i++) {
> -            lb_dps[i] = CONST_CAST(struct sbrec_datapath_binding *,
> -                                   lb->nb_ls[i]->sb);
> -        }
> -
> -        if (!lb->slb) {
> -            sbrec_lb = sbrec_load_balancer_insert(ovnsb_txn);
> -            lb->slb = sbrec_lb;
> -            char *lb_id = xasprintf(
> -                UUID_FMT, UUID_ARGS(&lb->nlb->header_.uuid));
> -            const struct smap external_ids =
> -                SMAP_CONST1(&external_ids, "lb_id", lb_id);
> -            sbrec_load_balancer_set_external_ids(sbrec_lb, &external_ids);
> -            free(lb_id);
> -        }
> -        sbrec_load_balancer_set_name(lb->slb, lb->nlb->name);
> -        sbrec_load_balancer_set_vips(lb->slb, &lb->nlb->vips);
> -        sbrec_load_balancer_set_protocol(lb->slb, lb->nlb->protocol);
> -        sbrec_load_balancer_set_datapaths(lb->slb, lb_dps, lb->n_nb_ls);
> -        sbrec_load_balancer_set_options(lb->slb, &options);
> -        smap_destroy(&options);
> -        free(lb_dps);
> -    }
> -
> -    /* Datapath_Binding.load_balancers is not used anymore, it's still in the
> -     * schema for compatibility reasons.  Reset it to empty, just in case.
> -     */
> -    HMAP_FOR_EACH (od, key_node, datapaths) {
> -        if (!od->nbs) {
> -            continue;
> +        for (size_t i = 0; i < od->nbr->n_load_balancer; i++) {
> +            const struct uuid *lb_uuid =
> +                &od->nbr->load_balancer[i]->header_.uuid;
> +            lb = ovn_northd_lb_find(lbs, lb_uuid);
> +            ovn_northd_lb_add_lr(lb, od);
> +            build_lrouter_lb_ips(od, lb);
>          }
>
> -        if (od->sb->n_load_balancers) {
> -            sbrec_datapath_binding_set_load_balancers(od->sb, NULL, 0);
> +        for (size_t i = 0; i < od->nbr->n_load_balancer_group; i++) {
> +            const struct nbrec_load_balancer_group *lbg =
> +                od->nbr->load_balancer_group[i];
> +            for (size_t j = 0; j < lbg->n_load_balancer; j++) {
> +                const struct uuid *lb_uuid =
> +                    &lbg->load_balancer[j]->header_.uuid;
> +                lb = ovn_northd_lb_find(lbs, lb_uuid);
> +                ovn_northd_lb_add_lr(lb, od);
> +                build_lrouter_lb_ips(od, lb);
> +            }
>          }
>      }
>  }
>
>  static void
> -build_ovn_lb_svcs(struct northd_input *input_data,
> -                  struct ovsdb_idl_txn *ovnsb_txn,
> -                  struct hmap *ports, struct hmap *lbs)
> +build_lb_svcs(struct northd_input *input_data,
> +              struct ovsdb_idl_txn *ovnsb_txn,
> +              struct hmap *ports,
> +              struct hmap *lbs)
>  {
>      struct hmap monitor_map = HMAP_INITIALIZER(&monitor_map);
>
> @@ -3936,26 +3855,6 @@ build_ovn_lb_svcs(struct northd_input *input_data,
>      hmap_destroy(&monitor_map);
>  }
>
> -static void
> -build_lrouter_lb_ips(struct ovn_datapath *od, const struct ovn_northd_lb *lb)
> -{
> -    bool is_routable = smap_get_bool(&lb->nlb->options, "add_route",  false);
> -    const char *ip_address;
> -
> -    SSET_FOR_EACH (ip_address, &lb->ips_v4) {
> -        sset_add(&od->lb_ips_v4, ip_address);
> -        if (is_routable) {
> -            sset_add(&od->lb_ips_v4_routable, ip_address);
> -        }
> -    }
> -    SSET_FOR_EACH (ip_address, &lb->ips_v6) {
> -        sset_add(&od->lb_ips_v6, ip_address);
> -        if (is_routable) {
> -            sset_add(&od->lb_ips_v6_routable, ip_address);
> -        }
> -    }
> -}
> -
>  static bool lrouter_port_ipv4_reachable(const struct ovn_port *op,
>                                          ovs_be32 addr);
>  static bool lrouter_port_ipv6_reachable(const struct ovn_port *op,
> @@ -3989,7 +3888,7 @@ build_lrouter_lb_reachable_ips(struct ovn_datapath *od,
>  }
>
>  static void
> -build_lrouter_lbs(struct hmap *datapaths, struct hmap *lbs)
> +build_lrouter_lbs_check(const struct hmap *datapaths)
>  {
>      struct ovn_datapath *od;
>
> @@ -3998,22 +3897,15 @@ build_lrouter_lbs(struct hmap *datapaths, struct hmap *lbs)
>              continue;
>          }
>
> -        for (size_t i = 0; i < od->nbr->n_load_balancer; i++) {
> -            struct ovn_northd_lb *lb =
> -                ovn_northd_lb_find(lbs,
> -                                   &od->nbr->load_balancer[i]->header_.uuid);
> -            build_lrouter_lb_ips(od, lb);
> -        }
> -
> -        for (size_t i = 0; i < od->nbr->n_load_balancer_group; i++) {
> -            const struct nbrec_load_balancer_group *lbg =
> -                od->nbr->load_balancer_group[i];
> -            for (size_t j = 0; j < lbg->n_load_balancer; j++) {
> -                struct ovn_northd_lb *lb =
> -                    ovn_northd_lb_find(lbs,
> -                                       &lbg->load_balancer[j]->header_.uuid);
> -                build_lrouter_lb_ips(od, lb);
> -            }
> +        if (od->has_lb_vip && od->n_l3dgw_ports > 1
> +                && !smap_get(&od->nbr->options, "chassis")) {
> +            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
> +            VLOG_WARN_RL(&rl, "Load-balancers are configured on logical "
> +                         "router %s, which has %"PRIuSIZE" distributed "
> +                         "gateway ports. Load-balancer is not supported "
> +                         "yet when there is more than one distributed "
> +                         "gateway port on the router.",
> +                         od->nbr->name, od->n_l3dgw_ports);
>          }
>      }
>  }
> @@ -4048,6 +3940,114 @@ build_lrouter_lbs_reachable_ips(struct hmap *datapaths, struct hmap *lbs)
>      }
>  }
>
> +/* This must be called after all ports have been processed, i.e., after
> + * build_ports() because the reachability check requires the router ports
> + * networks to have been parsed.
> + */
> +static void
> +build_lb_port_related_data(struct hmap *datapaths, struct hmap *ports,
> +                           struct hmap *lbs, struct northd_input *input_data,
> +                           struct ovsdb_idl_txn *ovnsb_txn)
> +{
> +    build_lrouter_lbs_check(datapaths);
> +    build_lrouter_lbs_reachable_ips(datapaths, lbs);
> +    build_lb_svcs(input_data, ovnsb_txn, ports, lbs);
> +}
> +
> +/* Syncs relevant load balancers (applied to logical switches) to the
> + * Southbound database.
> + */
> +static void
> +sync_lbs(struct northd_input *input_data, struct ovsdb_idl_txn *ovnsb_txn,
> +         struct hmap *datapaths, struct hmap *lbs)
> +{
> +    struct ovn_northd_lb *lb;
> +
> +    /* Delete any stale SB load balancer rows. */
> +    struct hmapx existing_lbs = HMAPX_INITIALIZER(&existing_lbs);
> +    const struct sbrec_load_balancer *sbrec_lb, *next;
> +    SBREC_LOAD_BALANCER_TABLE_FOR_EACH_SAFE (sbrec_lb, next,
> +                            input_data->sbrec_load_balancer_table) {
> +        const char *nb_lb_uuid = smap_get(&sbrec_lb->external_ids, "lb_id");
> +        struct uuid lb_uuid;
> +        if (!nb_lb_uuid || !uuid_from_string(&lb_uuid, nb_lb_uuid)) {
> +            sbrec_load_balancer_delete(sbrec_lb);
> +            continue;
> +        }
> +
> +        /* Delete any SB load balancer entries that refer to NB load balancers
> +         * that don't exist anymore or are not applied to switches anymore.
> +         *
> +         * There is also a special case in which duplicate LBs might be created
> +         * in the SB, e.g., due to the fact that OVSDB only ensures
> +         * "at-least-once" consistency for clustered database tables that
> +         * are not indexed in any way.
> +         */
> +        lb = ovn_northd_lb_find(lbs, &lb_uuid);
> +        if (!lb || !lb->n_nb_ls || !hmapx_add(&existing_lbs, lb)) {
> +            sbrec_load_balancer_delete(sbrec_lb);
> +        } else {
> +            lb->slb = sbrec_lb;
> +        }
> +    }
> +    hmapx_destroy(&existing_lbs);
> +
> +    /* Create SB Load balancer records if not present and sync
> +     * the SB load balancer columns. */
> +    HMAP_FOR_EACH (lb, hmap_node, lbs) {
> +
> +        if (!lb->n_nb_ls) {
> +            continue;
> +        }
> +
> +        /* Store the fact that northd provides the original (destination IP +
> +         * transport port) tuple.
> +         */
> +        struct smap options;
> +        smap_clone(&options, &lb->nlb->options);
> +        smap_replace(&options, "hairpin_orig_tuple", "true");
> +
> +        struct sbrec_datapath_binding **lb_dps =
> +            xmalloc(lb->n_nb_ls * sizeof *lb_dps);
> +        for (size_t i = 0; i < lb->n_nb_ls; i++) {
> +            lb_dps[i] = CONST_CAST(struct sbrec_datapath_binding *,
> +                                   lb->nb_ls[i]->sb);
> +        }
> +
> +        if (!lb->slb) {
> +            sbrec_lb = sbrec_load_balancer_insert(ovnsb_txn);
> +            lb->slb = sbrec_lb;
> +            char *lb_id = xasprintf(
> +                UUID_FMT, UUID_ARGS(&lb->nlb->header_.uuid));
> +            const struct smap external_ids =
> +                SMAP_CONST1(&external_ids, "lb_id", lb_id);
> +            sbrec_load_balancer_set_external_ids(sbrec_lb, &external_ids);
> +            free(lb_id);
> +        }
> +        sbrec_load_balancer_set_name(lb->slb, lb->nlb->name);
> +        sbrec_load_balancer_set_vips(lb->slb, &lb->nlb->vips);
> +        sbrec_load_balancer_set_protocol(lb->slb, lb->nlb->protocol);
> +        sbrec_load_balancer_set_datapaths(lb->slb, lb_dps, lb->n_nb_ls);
> +        sbrec_load_balancer_set_options(lb->slb, &options);
> +        smap_destroy(&options);
> +        free(lb_dps);
> +    }
> +
> +    /* Datapath_Binding.load_balancers is not used anymore, it's still in the
> +     * schema for compatibility reasons.  Reset it to empty, just in case.
> +     */
> +    struct ovn_datapath *od;
> +    HMAP_FOR_EACH (od, key_node, datapaths) {
> +        if (!od->nbs) {
> +            continue;
> +        }
> +
> +        if (od->sb->n_load_balancers) {
> +            sbrec_datapath_binding_set_load_balancers(od->sb, NULL, 0);
> +        }
> +    }
> +}
> +
>  static bool
>  ovn_port_add_tnlid(struct ovn_port *op, uint32_t tunnel_key)
>  {
> @@ -15121,14 +15121,12 @@ ovnnb_db_run(struct northd_input *input_data,
>                                       "ignore_lsp_down", true);
>
>      build_datapaths(input_data, ovnsb_txn, &data->datapaths, &data->lr_list);
> -    build_ovn_lbs(input_data, ovnsb_txn, &data->datapaths, &data->lbs);
> -    build_lrouter_lbs(&data->datapaths, &data->lbs);
> +    build_lbs(input_data, &data->datapaths, &data->lbs);
>      build_ports(input_data, ovnsb_txn, sbrec_chassis_by_name,
>                  sbrec_chassis_by_hostname,
>                  &data->datapaths, &data->ports);
> -    build_lrouter_lbs_reachable_ips(&data->datapaths, &data->lbs);
> -    build_ovn_lr_lbs(&data->datapaths, &data->lbs);
> -    build_ovn_lb_svcs(input_data, ovnsb_txn, &data->ports, &data->lbs);
> +    build_lb_port_related_data(&data->datapaths, &data->ports, &data->lbs,
> +                               input_data, ovnsb_txn);
>      build_ipam(&data->datapaths, &data->ports);
>      build_port_group_lswitches(input_data, &data->port_groups, &data->ports);
>      build_lrouter_groups(&data->ports, &data->lr_list);
> @@ -15138,7 +15136,8 @@ ovnnb_db_run(struct northd_input *input_data,
>      stopwatch_start(CLEAR_LFLOWS_CTX_STOPWATCH_NAME, time_msec());
>      ovn_update_ipv6_prefix(&data->ports);
>
> -    sync_address_sets(input_data,  ovnsb_txn, &data->datapaths);
> +    sync_lbs(input_data, ovnsb_txn, &data->datapaths, &data->lbs);
> +    sync_address_sets(input_data, ovnsb_txn, &data->datapaths);
>      sync_port_groups(input_data, ovnsb_txn, &data->port_groups);
>      sync_meters(input_data, ovnsb_txn, &data->meter_groups);
>      sync_dns_entries(input_data, ovnsb_txn, &data->datapaths);
> diff --git a/tests/ovn.at b/tests/ovn.at
> index 9b13a980da..2b13dd1b8f 100644
> --- a/tests/ovn.at
> +++ b/tests/ovn.at
> @@ -24834,8 +24834,10 @@ ovn-nbctl acl-add pg1 to-lport 1002 "outport == @pg1 && ip4 && ip4.src == 0.0.0.
>  ovn-nbctl acl-add pg1 to-lport 1002 "outport == @pg1 && ip4 && ip4.src == 0.0.0.0/0 && tcp && tcp.dst == 80" allow-related
>  ovn-nbctl acl-add pg1 to-lport 1002 "outport == @pg1 && ip4 && ip4.src == 0.0.0.0/0 && udp && udp.dst == 80" allow-related
>
> -# Create a logical router and attach both logical switches
> +# Create a logical router and attach both logical switches.
> +# Make the logical router a GW router for load balancing to be supported.
>  ovn-nbctl lr-add lr0
> +ovn-nbctl set logical_router lr0 options:chassis=hv1
>  ovn-nbctl lrp-add lr0 lr0-sw0 00:00:00:00:ff:01 10.0.0.1/24
>  ovn-nbctl lsp-add sw0 sw0-lr0
>  ovn-nbctl lsp-set-type sw0-lr0 router
> @@ -24856,7 +24858,7 @@ ovn-nbctl lr-lb-add lr0 lb1
>
>  OVS_WAIT_UNTIL([
>      test $(as hv1 ovs-ofctl dump-groups br-int | \
> -    grep "selection_method=dp_hash" -c) -eq 1
> +    grep "selection_method=dp_hash" -c) -eq 2
>  ])
>
>  OVS_WAIT_UNTIL([
> @@ -24870,7 +24872,7 @@ ovn-nbctl set load_balancer $lb1_uuid selection_fields="ip_src,ip_dst,tp_src,tp_
>
>  OVS_WAIT_UNTIL([
>      test $(as hv1 ovs-ofctl dump-groups br-int | \
> -    grep "selection_method=hash,fields(ip_src,ip_dst,tcp_src,tcp_dst)" -c) -eq 1
> +    grep "selection_method=hash,fields(ip_src,ip_dst,tcp_src,tcp_dst)" -c) -eq 2
>  ])
>
>  OVS_WAIT_UNTIL([
> @@ -24882,7 +24884,7 @@ OVS_WAIT_UNTIL([
>  ovn-nbctl set load_balancer $lb1_uuid protocol=udp
>  OVS_WAIT_UNTIL([
>      test $(as hv1 ovs-ofctl dump-groups br-int | \
> -    grep "selection_method=hash,fields(ip_src,ip_dst,udp_src,udp_dst)" -c) -eq 1
> +    grep "selection_method=hash,fields(ip_src,ip_dst,udp_src,udp_dst)" -c) -eq 2
>  ])
>
>  OVS_WAIT_UNTIL([
> @@ -24894,7 +24896,7 @@ OVS_WAIT_UNTIL([
>  ovn-nbctl set load_balancer $lb1_uuid protocol=sctp
>  OVS_WAIT_UNTIL([
>      test $(as hv1 ovs-ofctl dump-groups br-int | \
> -    grep "selection_method=hash,fields(ip_src,ip_dst,sctp_src,sctp_dst)" -c) -eq 1
> +    grep "selection_method=hash,fields(ip_src,ip_dst,sctp_src,sctp_dst)" -c) -eq 2
>  ])
>
>  OVS_WAIT_UNTIL([
> @@ -24905,7 +24907,7 @@ OVS_WAIT_UNTIL([
>  ovn-nbctl set load_balancer $lb1_uuid selection_fields="ip_src,ip_dst,tp_dst"
>  OVS_WAIT_UNTIL([
>      test $(as hv1 ovs-ofctl dump-groups br-int | \
> -    grep "selection_method=hash,fields(ip_src,ip_dst,sctp_dst)" -c) -eq 1
> +    grep "selection_method=hash,fields(ip_src,ip_dst,sctp_dst)" -c) -eq 2
>  ])
>
>  OVS_WAIT_UNTIL([
> --
> 2.27.0
>
> _______________________________________________
> dev mailing list
> dev@openvswitch.org
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>
diff mbox series

Patch

diff --git a/northd/northd.c b/northd/northd.c
index b264fb850b..8c871171f5 100644
--- a/northd/northd.c
+++ b/northd/northd.c
@@ -3732,53 +3732,28 @@  build_lb_vip_actions(struct ovn_lb_vip *lb_vip,
 }
 
 static void
-build_ovn_lr_lbs(struct hmap *datapaths, struct hmap *lbs)
+build_lrouter_lb_ips(struct ovn_datapath *od, const struct ovn_northd_lb *lb)
 {
-    struct ovn_northd_lb *lb;
-    struct ovn_datapath *od;
-
-    HMAP_FOR_EACH (od, key_node, datapaths) {
-        if (!od->nbr) {
-            continue;
-        }
-        if (!smap_get(&od->nbr->options, "chassis")
-            && od->n_l3dgw_ports != 1) {
-            if (od->n_l3dgw_ports > 1 && od->has_lb_vip) {
-                static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
-                VLOG_WARN_RL(&rl, "Load-balancers are configured on logical "
-                             "router %s, which has %"PRIuSIZE" distributed "
-                             "gateway ports. Load-balancer is not supported "
-                             "yet when there is more than one distributed "
-                             "gateway port on the router.",
-                             od->nbr->name, od->n_l3dgw_ports);
-            }
-            continue;
-        }
+    bool is_routable = smap_get_bool(&lb->nlb->options, "add_route",  false);
+    const char *ip_address;
 
-        for (size_t i = 0; i < od->nbr->n_load_balancer; i++) {
-            const struct uuid *lb_uuid =
-                &od->nbr->load_balancer[i]->header_.uuid;
-            lb = ovn_northd_lb_find(lbs, lb_uuid);
-            ovn_northd_lb_add_lr(lb, od);
+    SSET_FOR_EACH (ip_address, &lb->ips_v4) {
+        sset_add(&od->lb_ips_v4, ip_address);
+        if (is_routable) {
+            sset_add(&od->lb_ips_v4_routable, ip_address);
         }
-
-        for (size_t i = 0; i < od->nbr->n_load_balancer_group; i++) {
-            const struct nbrec_load_balancer_group *lbg =
-                od->nbr->load_balancer_group[i];
-            for (size_t j = 0; j < lbg->n_load_balancer; j++) {
-                const struct uuid *lb_uuid =
-                    &lbg->load_balancer[j]->header_.uuid;
-                lb = ovn_northd_lb_find(lbs, lb_uuid);
-                ovn_northd_lb_add_lr(lb, od);
-            }
+    }
+    SSET_FOR_EACH (ip_address, &lb->ips_v6) {
+        sset_add(&od->lb_ips_v6, ip_address);
+        if (is_routable) {
+            sset_add(&od->lb_ips_v6_routable, ip_address);
         }
     }
 }
 
 static void
-build_ovn_lbs(struct northd_input *input_data,
-              struct ovsdb_idl_txn *ovnsb_txn,
-              struct hmap *datapaths, struct hmap *lbs)
+build_lbs(struct northd_input *input_data, struct hmap *datapaths,
+          struct hmap *lbs)
 {
     struct ovn_northd_lb *lb;
 
@@ -3817,94 +3792,38 @@  build_ovn_lbs(struct northd_input *input_data,
         }
     }
 
-    /* Delete any stale SB load balancer rows. */
-    struct hmapx existing_lbs = HMAPX_INITIALIZER(&existing_lbs);
-    const struct sbrec_load_balancer *sbrec_lb, *next;
-    SBREC_LOAD_BALANCER_TABLE_FOR_EACH_SAFE (sbrec_lb, next,
-                            input_data->sbrec_load_balancer_table) {
-        const char *nb_lb_uuid = smap_get(&sbrec_lb->external_ids, "lb_id");
-        struct uuid lb_uuid;
-        if (!nb_lb_uuid || !uuid_from_string(&lb_uuid, nb_lb_uuid)) {
-            sbrec_load_balancer_delete(sbrec_lb);
-            continue;
-        }
-
-        /* Delete any SB load balancer entries that refer to NB load balancers
-         * that don't exist anymore or are not applied to switches anymore.
-         *
-         * There is also a special case in which duplicate LBs might be created
-         * in the SB, e.g., due to the fact that OVSDB only ensures
-         * "at-least-once" consistency for clustered database tables that
-         * are not indexed in any way.
-         */
-        lb = ovn_northd_lb_find(lbs, &lb_uuid);
-        if (!lb || !lb->n_nb_ls || !hmapx_add(&existing_lbs, lb)) {
-            sbrec_load_balancer_delete(sbrec_lb);
-        } else {
-            lb->slb = sbrec_lb;
-        }
-    }
-    hmapx_destroy(&existing_lbs);
-
-    /* Create SB Load balancer records if not present and sync
-     * the SB load balancer columns. */
-    HMAP_FOR_EACH (lb, hmap_node, lbs) {
-
-        if (!lb->n_nb_ls) {
+    HMAP_FOR_EACH (od, key_node, datapaths) {
+        if (!od->nbr) {
             continue;
         }
 
-        /* Store the fact that northd provides the original (destination IP +
-         * transport port) tuple.
-         */
-        struct smap options;
-        smap_clone(&options, &lb->nlb->options);
-        smap_replace(&options, "hairpin_orig_tuple", "true");
-
-        struct sbrec_datapath_binding **lb_dps =
-            xmalloc(lb->n_nb_ls * sizeof *lb_dps);
-        for (size_t i = 0; i < lb->n_nb_ls; i++) {
-            lb_dps[i] = CONST_CAST(struct sbrec_datapath_binding *,
-                                   lb->nb_ls[i]->sb);
-        }
-
-        if (!lb->slb) {
-            sbrec_lb = sbrec_load_balancer_insert(ovnsb_txn);
-            lb->slb = sbrec_lb;
-            char *lb_id = xasprintf(
-                UUID_FMT, UUID_ARGS(&lb->nlb->header_.uuid));
-            const struct smap external_ids =
-                SMAP_CONST1(&external_ids, "lb_id", lb_id);
-            sbrec_load_balancer_set_external_ids(sbrec_lb, &external_ids);
-            free(lb_id);
-        }
-        sbrec_load_balancer_set_name(lb->slb, lb->nlb->name);
-        sbrec_load_balancer_set_vips(lb->slb, &lb->nlb->vips);
-        sbrec_load_balancer_set_protocol(lb->slb, lb->nlb->protocol);
-        sbrec_load_balancer_set_datapaths(lb->slb, lb_dps, lb->n_nb_ls);
-        sbrec_load_balancer_set_options(lb->slb, &options);
-        smap_destroy(&options);
-        free(lb_dps);
-    }
-
-    /* Datapath_Binding.load_balancers is not used anymore, it's still in the
-     * schema for compatibility reasons.  Reset it to empty, just in case.
-     */
-    HMAP_FOR_EACH (od, key_node, datapaths) {
-        if (!od->nbs) {
-            continue;
+        for (size_t i = 0; i < od->nbr->n_load_balancer; i++) {
+            const struct uuid *lb_uuid =
+                &od->nbr->load_balancer[i]->header_.uuid;
+            lb = ovn_northd_lb_find(lbs, lb_uuid);
+            ovn_northd_lb_add_lr(lb, od);
+            build_lrouter_lb_ips(od, lb);
         }
 
-        if (od->sb->n_load_balancers) {
-            sbrec_datapath_binding_set_load_balancers(od->sb, NULL, 0);
+        for (size_t i = 0; i < od->nbr->n_load_balancer_group; i++) {
+            const struct nbrec_load_balancer_group *lbg =
+                od->nbr->load_balancer_group[i];
+            for (size_t j = 0; j < lbg->n_load_balancer; j++) {
+                const struct uuid *lb_uuid =
+                    &lbg->load_balancer[j]->header_.uuid;
+                lb = ovn_northd_lb_find(lbs, lb_uuid);
+                ovn_northd_lb_add_lr(lb, od);
+                build_lrouter_lb_ips(od, lb);
+            }
         }
     }
 }
 
 static void
-build_ovn_lb_svcs(struct northd_input *input_data,
-                  struct ovsdb_idl_txn *ovnsb_txn,
-                  struct hmap *ports, struct hmap *lbs)
+build_lb_svcs(struct northd_input *input_data,
+              struct ovsdb_idl_txn *ovnsb_txn,
+              struct hmap *ports,
+              struct hmap *lbs)
 {
     struct hmap monitor_map = HMAP_INITIALIZER(&monitor_map);
 
@@ -3936,26 +3855,6 @@  build_ovn_lb_svcs(struct northd_input *input_data,
     hmap_destroy(&monitor_map);
 }
 
-static void
-build_lrouter_lb_ips(struct ovn_datapath *od, const struct ovn_northd_lb *lb)
-{
-    bool is_routable = smap_get_bool(&lb->nlb->options, "add_route",  false);
-    const char *ip_address;
-
-    SSET_FOR_EACH (ip_address, &lb->ips_v4) {
-        sset_add(&od->lb_ips_v4, ip_address);
-        if (is_routable) {
-            sset_add(&od->lb_ips_v4_routable, ip_address);
-        }
-    }
-    SSET_FOR_EACH (ip_address, &lb->ips_v6) {
-        sset_add(&od->lb_ips_v6, ip_address);
-        if (is_routable) {
-            sset_add(&od->lb_ips_v6_routable, ip_address);
-        }
-    }
-}
-
 static bool lrouter_port_ipv4_reachable(const struct ovn_port *op,
                                         ovs_be32 addr);
 static bool lrouter_port_ipv6_reachable(const struct ovn_port *op,
@@ -3989,7 +3888,7 @@  build_lrouter_lb_reachable_ips(struct ovn_datapath *od,
 }
 
 static void
-build_lrouter_lbs(struct hmap *datapaths, struct hmap *lbs)
+build_lrouter_lbs_check(const struct hmap *datapaths)
 {
     struct ovn_datapath *od;
 
@@ -3998,22 +3897,15 @@  build_lrouter_lbs(struct hmap *datapaths, struct hmap *lbs)
             continue;
         }
 
-        for (size_t i = 0; i < od->nbr->n_load_balancer; i++) {
-            struct ovn_northd_lb *lb =
-                ovn_northd_lb_find(lbs,
-                                   &od->nbr->load_balancer[i]->header_.uuid);
-            build_lrouter_lb_ips(od, lb);
-        }
-
-        for (size_t i = 0; i < od->nbr->n_load_balancer_group; i++) {
-            const struct nbrec_load_balancer_group *lbg =
-                od->nbr->load_balancer_group[i];
-            for (size_t j = 0; j < lbg->n_load_balancer; j++) {
-                struct ovn_northd_lb *lb =
-                    ovn_northd_lb_find(lbs,
-                                       &lbg->load_balancer[j]->header_.uuid);
-                build_lrouter_lb_ips(od, lb);
-            }
+        if (od->has_lb_vip && od->n_l3dgw_ports > 1
+                && !smap_get(&od->nbr->options, "chassis")) {
+            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
+            VLOG_WARN_RL(&rl, "Load-balancers are configured on logical "
+                         "router %s, which has %"PRIuSIZE" distributed "
+                         "gateway ports. Load-balancer is not supported "
+                         "yet when there is more than one distributed "
+                         "gateway port on the router.",
+                         od->nbr->name, od->n_l3dgw_ports);
         }
     }
 }
@@ -4048,6 +3940,114 @@  build_lrouter_lbs_reachable_ips(struct hmap *datapaths, struct hmap *lbs)
     }
 }
 
+/* This must be called after all ports have been processed, i.e., after
+ * build_ports() because the reachability check requires the router ports
+ * networks to have been parsed.
+ */
+static void
+build_lb_port_related_data(struct hmap *datapaths, struct hmap *ports,
+                           struct hmap *lbs, struct northd_input *input_data,
+                           struct ovsdb_idl_txn *ovnsb_txn)
+{
+    build_lrouter_lbs_check(datapaths);
+    build_lrouter_lbs_reachable_ips(datapaths, lbs);
+    build_lb_svcs(input_data, ovnsb_txn, ports, lbs);
+}
+
+/* Syncs relevant load balancers (applied to logical switches) to the
+ * Southbound database.
+ */
+static void
+sync_lbs(struct northd_input *input_data, struct ovsdb_idl_txn *ovnsb_txn,
+         struct hmap *datapaths, struct hmap *lbs)
+{
+    struct ovn_northd_lb *lb;
+
+    /* Delete any stale SB load balancer rows. */
+    struct hmapx existing_lbs = HMAPX_INITIALIZER(&existing_lbs);
+    const struct sbrec_load_balancer *sbrec_lb, *next;
+    SBREC_LOAD_BALANCER_TABLE_FOR_EACH_SAFE (sbrec_lb, next,
+                            input_data->sbrec_load_balancer_table) {
+        const char *nb_lb_uuid = smap_get(&sbrec_lb->external_ids, "lb_id");
+        struct uuid lb_uuid;
+        if (!nb_lb_uuid || !uuid_from_string(&lb_uuid, nb_lb_uuid)) {
+            sbrec_load_balancer_delete(sbrec_lb);
+            continue;
+        }
+
+        /* Delete any SB load balancer entries that refer to NB load balancers
+         * that don't exist anymore or are not applied to switches anymore.
+         *
+         * There is also a special case in which duplicate LBs might be created
+         * in the SB, e.g., due to the fact that OVSDB only ensures
+         * "at-least-once" consistency for clustered database tables that
+         * are not indexed in any way.
+         */
+        lb = ovn_northd_lb_find(lbs, &lb_uuid);
+        if (!lb || !lb->n_nb_ls || !hmapx_add(&existing_lbs, lb)) {
+            sbrec_load_balancer_delete(sbrec_lb);
+        } else {
+            lb->slb = sbrec_lb;
+        }
+    }
+    hmapx_destroy(&existing_lbs);
+
+    /* Create SB Load balancer records if not present and sync
+     * the SB load balancer columns. */
+    HMAP_FOR_EACH (lb, hmap_node, lbs) {
+
+        if (!lb->n_nb_ls) {
+            continue;
+        }
+
+        /* Store the fact that northd provides the original (destination IP +
+         * transport port) tuple.
+         */
+        struct smap options;
+        smap_clone(&options, &lb->nlb->options);
+        smap_replace(&options, "hairpin_orig_tuple", "true");
+
+        struct sbrec_datapath_binding **lb_dps =
+            xmalloc(lb->n_nb_ls * sizeof *lb_dps);
+        for (size_t i = 0; i < lb->n_nb_ls; i++) {
+            lb_dps[i] = CONST_CAST(struct sbrec_datapath_binding *,
+                                   lb->nb_ls[i]->sb);
+        }
+
+        if (!lb->slb) {
+            sbrec_lb = sbrec_load_balancer_insert(ovnsb_txn);
+            lb->slb = sbrec_lb;
+            char *lb_id = xasprintf(
+                UUID_FMT, UUID_ARGS(&lb->nlb->header_.uuid));
+            const struct smap external_ids =
+                SMAP_CONST1(&external_ids, "lb_id", lb_id);
+            sbrec_load_balancer_set_external_ids(sbrec_lb, &external_ids);
+            free(lb_id);
+        }
+        sbrec_load_balancer_set_name(lb->slb, lb->nlb->name);
+        sbrec_load_balancer_set_vips(lb->slb, &lb->nlb->vips);
+        sbrec_load_balancer_set_protocol(lb->slb, lb->nlb->protocol);
+        sbrec_load_balancer_set_datapaths(lb->slb, lb_dps, lb->n_nb_ls);
+        sbrec_load_balancer_set_options(lb->slb, &options);
+        smap_destroy(&options);
+        free(lb_dps);
+    }
+
+    /* Datapath_Binding.load_balancers is not used anymore, it's still in the
+     * schema for compatibility reasons.  Reset it to empty, just in case.
+     */
+    struct ovn_datapath *od;
+    HMAP_FOR_EACH (od, key_node, datapaths) {
+        if (!od->nbs) {
+            continue;
+        }
+
+        if (od->sb->n_load_balancers) {
+            sbrec_datapath_binding_set_load_balancers(od->sb, NULL, 0);
+        }
+    }
+}
+
 static bool
 ovn_port_add_tnlid(struct ovn_port *op, uint32_t tunnel_key)
 {
@@ -15121,14 +15121,12 @@  ovnnb_db_run(struct northd_input *input_data,
                                      "ignore_lsp_down", true);
 
     build_datapaths(input_data, ovnsb_txn, &data->datapaths, &data->lr_list);
-    build_ovn_lbs(input_data, ovnsb_txn, &data->datapaths, &data->lbs);
-    build_lrouter_lbs(&data->datapaths, &data->lbs);
+    build_lbs(input_data, &data->datapaths, &data->lbs);
     build_ports(input_data, ovnsb_txn, sbrec_chassis_by_name,
                 sbrec_chassis_by_hostname,
                 &data->datapaths, &data->ports);
-    build_lrouter_lbs_reachable_ips(&data->datapaths, &data->lbs);
-    build_ovn_lr_lbs(&data->datapaths, &data->lbs);
-    build_ovn_lb_svcs(input_data, ovnsb_txn, &data->ports, &data->lbs);
+    build_lb_port_related_data(&data->datapaths, &data->ports, &data->lbs,
+                               input_data, ovnsb_txn);
     build_ipam(&data->datapaths, &data->ports);
     build_port_group_lswitches(input_data, &data->port_groups, &data->ports);
     build_lrouter_groups(&data->ports, &data->lr_list);
@@ -15138,7 +15136,8 @@  ovnnb_db_run(struct northd_input *input_data,
     stopwatch_start(CLEAR_LFLOWS_CTX_STOPWATCH_NAME, time_msec());
     ovn_update_ipv6_prefix(&data->ports);
 
-    sync_address_sets(input_data,  ovnsb_txn, &data->datapaths);
+    sync_lbs(input_data, ovnsb_txn, &data->datapaths, &data->lbs);
+    sync_address_sets(input_data, ovnsb_txn, &data->datapaths);
     sync_port_groups(input_data, ovnsb_txn, &data->port_groups);
     sync_meters(input_data, ovnsb_txn, &data->meter_groups);
     sync_dns_entries(input_data, ovnsb_txn, &data->datapaths);
diff --git a/tests/ovn.at b/tests/ovn.at
index 9b13a980da..2b13dd1b8f 100644
--- a/tests/ovn.at
+++ b/tests/ovn.at
@@ -24834,8 +24834,10 @@  ovn-nbctl acl-add pg1 to-lport 1002 "outport == @pg1 && ip4 && ip4.src == 0.0.0.
 ovn-nbctl acl-add pg1 to-lport 1002 "outport == @pg1 && ip4 && ip4.src == 0.0.0.0/0 && tcp && tcp.dst == 80" allow-related
 ovn-nbctl acl-add pg1 to-lport 1002 "outport == @pg1 && ip4 && ip4.src == 0.0.0.0/0 && udp && udp.dst == 80" allow-related
 
-# Create a logical router and attach both logical switches
+# Create a logical router and attach both logical switches.
+# Make the logical router a GW router for load balancing to be supported.
 ovn-nbctl lr-add lr0
+ovn-nbctl set logical_router lr0 options:chassis=hv1
 ovn-nbctl lrp-add lr0 lr0-sw0 00:00:00:00:ff:01 10.0.0.1/24
 ovn-nbctl lsp-add sw0 sw0-lr0
 ovn-nbctl lsp-set-type sw0-lr0 router
@@ -24856,7 +24858,7 @@  ovn-nbctl lr-lb-add lr0 lb1
 
 OVS_WAIT_UNTIL([
     test $(as hv1 ovs-ofctl dump-groups br-int | \
-    grep "selection_method=dp_hash" -c) -eq 1
+    grep "selection_method=dp_hash" -c) -eq 2
 ])
 
 OVS_WAIT_UNTIL([
@@ -24870,7 +24872,7 @@  ovn-nbctl set load_balancer $lb1_uuid selection_fields="ip_src,ip_dst,tp_src,tp_
 
 OVS_WAIT_UNTIL([
     test $(as hv1 ovs-ofctl dump-groups br-int | \
-    grep "selection_method=hash,fields(ip_src,ip_dst,tcp_src,tcp_dst)" -c) -eq 1
+    grep "selection_method=hash,fields(ip_src,ip_dst,tcp_src,tcp_dst)" -c) -eq 2
 ])
 
 OVS_WAIT_UNTIL([
@@ -24882,7 +24884,7 @@  OVS_WAIT_UNTIL([
 ovn-nbctl set load_balancer $lb1_uuid protocol=udp
 OVS_WAIT_UNTIL([
     test $(as hv1 ovs-ofctl dump-groups br-int | \
-    grep "selection_method=hash,fields(ip_src,ip_dst,udp_src,udp_dst)" -c) -eq 1
+    grep "selection_method=hash,fields(ip_src,ip_dst,udp_src,udp_dst)" -c) -eq 2
 ])
 
 OVS_WAIT_UNTIL([
@@ -24894,7 +24896,7 @@  OVS_WAIT_UNTIL([
 ovn-nbctl set load_balancer $lb1_uuid protocol=sctp
 OVS_WAIT_UNTIL([
     test $(as hv1 ovs-ofctl dump-groups br-int | \
-    grep "selection_method=hash,fields(ip_src,ip_dst,sctp_src,sctp_dst)" -c) -eq 1
+    grep "selection_method=hash,fields(ip_src,ip_dst,sctp_src,sctp_dst)" -c) -eq 2
 ])
 
 OVS_WAIT_UNTIL([
@@ -24905,7 +24907,7 @@  OVS_WAIT_UNTIL([
 ovn-nbctl set load_balancer $lb1_uuid selection_fields="ip_src,ip_dst,tp_dst"
 OVS_WAIT_UNTIL([
     test $(as hv1 ovs-ofctl dump-groups br-int | \
-    grep "selection_method=hash,fields(ip_src,ip_dst,sctp_dst)" -c) -eq 1
+    grep "selection_method=hash,fields(ip_src,ip_dst,sctp_dst)" -c) -eq 2
 ])
 
 OVS_WAIT_UNTIL([