Response inline.

On 10/14/26 9:38 AM, Lucas Vargas Dias wrote:
Hi Rosemarie,
Thanks for your review.


Em qua., 14 de out. de 2026 às 09:15, Rosemarie O'Riorden <[email protected] <mailto:[email protected]>> escreveu:

    Hi Lucas, thanks for the new version!

    I had a lot of thoughts about your last version that took me too
    long to
    organize. So I'm sorry for sending such a massive review too late to
    catch you on v2.

    Basically, as soon as I saw ecmp_route_list_node_cmp() for the first
    time, it felt off to me, but I couldn't exactly articulate why. After
    much consideration, I believe the whole route ordering system is overly
    complicated and could be much simpler and easier. Basically, we want
    routes to be in *some* order, but it doesn't really matter which order.
    Sure, the flow dumps might be more readable if routes are sorted by .10
    -> .20 -> .30, but the point of this patch is to make the routes
    ordered
    deterministically between full recomute and icremental processing.

    For this reason, route ordering could be accomplished using only UUID.
    It's the only characteristic of each route that is guaranteed to be
    unique, so checking the others is ultimately unnecessary. This would
    eliminate over 30 lines of repetitive code from just that one function.


I agree


    The same principle can be applied to groups. Groups don't themselves
    have UUIDs so what unique characteristic can we use to order them?
    Well,
    they contain the vector with the ordered routes, which have UUIDs.
    So we
    could just use the first UUID of the first route of each group.

I agree

    Now, I want to talk about the IDs assigned to routes and route groups.
    Say we have multiple routes ordered by UUID, and we remove one from the
    middle, or add one whose UUID is lexicographically in the middle. This
    means that all of the following routes are now assigned new IDs, which
    means those flows have to be rewritten. Maybe we could try to keep IDs
    unique per route/group and unrelated to order, so these flows don't
    have
    to be completely rewritten when nothing about them even changed,
    besides
    the number of routes before them... So now if we can't rely on
    processing order, nor details of the routes beyond UUID, how do we
    translate that into an index...?

    We could use a similar system as a hashmap, where elements are hashed
    into different locations based on their contents. The only guaranteed
    unique content of each route is its UUID. So we could "hash" this by
    taking the UUID % (size of register) and making that value the ID. The
    same concept could be applied to route groups, using their first
    route's
    UUID as I mentioned above. This might look strange to have two routes
    and for their IDs to be 4879 and 29387 rather than 1 and 2, but I think
    it's ok for the efficiency benefit :)

    The only issue here is collisions, as we can't use buckets like in a
    hashmap... And although they'd be very unlikely, we still need to
    account for that. So maybe we could use a simpler way like if an index
    is already in use, giving its collision the index (index +
    num_of_collisions). But this means we need a way to track collisions,
    and we need to make sure that indices we choose aren't already in
    use by
    another route/group. I didn't fully think out the details of this so
    I'm
    interested to hear your thoughts.

    Overall, the biggest con to this idea is that random indices may
    complicate testing a bit, as would the UUID-based ordering, but I feel
    it would be a minor inconvenience in exchange for the simplicity of the
    code.

    Thanks for taking the time to hear out my thoughts! I'm excited to hear
    what you think.


I agree with your points. I didn't think about using UUID and something like a hashmap.
I have some points:
- Members: the UUID alone isn't unique within a group. Redistributed NAT and LB routes use the NAT or Load_Balancer row as source_hint, and one row produces one route per path to the peer router, e.g. when the peer is reachable through two ports. I think the member hash could be the source_hint UUID, the nexthop and the output port.

Then I think for the ordering, you can compare source UUID first and have nexthop and output port as the fallback for collisions. So most of the time the cmp function would return early just after the UUID cmp.

And then I guess that makes sense to use those three inputs to create the "hash" for the ID value since that should be enough to establish uniqueness and avoid collisions. Maybe you could also check for collisions in the test by adding redistributed NAT or LB routes.

- Groups: instead of the first route's UUID I'll hash the group key (prefix, plen, route table, src/dst and source class, the same key ecmp_groups_find() uses). The first member changes when it's removed or when a member with a smaller UUID is added, and then the id of the whole group would change. The key is unique within a datapath and stays the same as members come and go.

Ack, that seems to make sense.

- Collisions: resolving them with "index + number of collisions" would make the ids depend on which element got the slot first, i.e. on processing order again,  which is what this patch fixes. Instead, when two elements derive the same id, both retry in the next round with an id derived from their hash and the round number. The result only depends on the set of routes. Adding an element can occasionally move a colliding one, but that stays local and rare. - The members are listed in the select() action in id order, otherwise the action text would still depend on insertion order.

I'm interested to see how this comes out.

Take care,
Rosemarie


Sorry for not CCing you before. I'll do it from now on.


Regards,
Lucas


    BTW in the future please try to CC your reviewers so we can stay up to
    date on the series :)

    Take care,
    Rosemarie O'Riorden

    On 10/13/26 3:59 PM, Lucas Vargas Dias via dev wrote:
     > The ECMP group id and the member ids are written into the
     > lr_in_ip_routing and lr_in_ip_routing_ecmp logical flows, but they
     > were assigned from the order in which routes reached
     > en_group_ecmp_route: the group id was hmap_count() at creation time
     > and the member id was the insertion position.  On a full recompute
     > the routes are walked in hmap order, while incremental processing
     > appends them in event order, so the same set of routes could get a
     > different numbering.  Every forced or fallback recompute could then
     > delete and re-insert Logical_Flow rows in the Southbound DB even
     > though nothing had changed.
     >
     > Fixes: 6979a96138a2 ("northd: Support I+P for group_ecmp_route
    engine.")
     > Fixes: 5395e12f247c ("northd: Drop traffic for ECMP group with
    "discard" route.")
     > Assisted-by: Claude Opus 5.5, Claude Code
     > Signed-off-by: Lucas Vargas Dias <[email protected]>
     > ---
     > v3:
     >    - Dropped nullable_strcmp() and the patch converting its
    callers; use
     >      nullable_string_is_equal() in the member comparison instead.
     >    - Removed the unused 'route_count' field from struct
    ecmp_groups_node.
     > v2:
     >    - Moved nullable_strcmp() to lib/ovn-util.h and documented its
    NULL
     >      handling.
     >    - Clarified in ecmp_groups_node_cmp() that it can't return 0
    for two
     >      distinct groups.
     >
     >   northd/en-group-ecmp-route.c | 167 ++++++++++++++++++++++++++++
    +------
     >   northd/en-group-ecmp-route.h |   1 -
     >   tests/ovn-northd.at <http://ovn-northd.at>          |  66 +++++
    +++++++++
     >   3 files changed, 207 insertions(+), 27 deletions(-)
     >
     > diff --git a/northd/en-group-ecmp-route.c b/northd/en-group-ecmp-
    route.c
     > index aca197318..f84155608 100644
     > --- a/northd/en-group-ecmp-route.c
     > +++ b/northd/en-group-ecmp-route.c
     > @@ -228,14 +228,12 @@ ecmp_groups_add_route(struct
    ecmp_groups_node *group,
     >                         const struct parsed_route *route)
     >   {
     >       static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
     > -    if (group->route_count == UINT16_MAX) {
     > +    if (vector_len(&group->route_list) == UINT16_MAX) {
     >           VLOG_WARN_RL(&rl, "too many routes in a single ecmp
    group.");
     >           return;
     >       }
     >
     >       if (route->is_discard_route) {
     > -        group->has_discard_route = true;
     > -
     >           char *prefix = normalize_v46_prefix(&route->prefix,
    route->plen);
     >           VLOG_WARN_RL(&rl, "The ECMP route \"%s\" contains
    \"discard\" "
     >                        "route, the whole group will drop
    traffic.", prefix);
     > @@ -244,22 +242,13 @@ ecmp_groups_add_route(struct
    ecmp_groups_node *group,
     >
     >       struct ecmp_route_list_node er = (struct
    ecmp_route_list_node) {
     >           .route = route,
     > -        .id = ++group->route_count,
     >       };
     >
     > -    if (group->route_count == 1) {
     > -        sset_clone(&group->selection_fields, &route-
     >ecmp_selection_fields);
     > -    } else {
     > -        sset_intersect(&group->selection_fields,
     > -                       &route->ecmp_selection_fields);
     > -    }
     > -
     >       vector_push(&group->route_list, &er);
     >   }
     >
     > -/* Removes a route from an ecmp group. If the ecmp group should
    persist
     > - * afterwards you must call ecmp_groups_update_ids before any
    further
     > - * insertions. */
     > +/* Removes a route from an ecmp group.  The ids of the group and
    of its
     > + * members are refreshed by group_ecmp_datapath_finalize(). */
     >   static const struct parsed_route *
     >   ecmp_groups_remove_route(struct ecmp_groups_node *group,
     >                            const struct parsed_route *pr)
     > @@ -278,15 +267,140 @@ ecmp_groups_remove_route(struct
    ecmp_groups_node *group,
     >       return NULL;
     >   }
     >
     > +/* Orders the members of an ecmp group by their content only, so
    that the
     > + * member ids do not depend on the order in which the routes
    were added. */
     > +static int
     > +ecmp_route_list_node_cmp(const void *a_, const void *b_)
     > +{
     > +    const struct parsed_route *a =
     > +        ((const struct ecmp_route_list_node *) a_)->route;
     > +    const struct parsed_route *b =
     > +        ((const struct ecmp_route_list_node *) b_)->route;
     > +    int cmp;
     > +
     > +    if (a->source != b->source) {
     > +        return a->source < b->source ? -1 : 1;
     > +    }
     > +    if (a->is_discard_route != b->is_discard_route) {
     > +        return a->is_discard_route ? -1 : 1;
     > +    }
     > +    if (!a->nexthop || !b->nexthop) {
     > +        cmp = !!a->nexthop - !!b->nexthop;
     > +    } else {
     > +        cmp = memcmp(a->nexthop, b->nexthop, sizeof *a->nexthop);
     > +    }
     > +    if (cmp) {
     > +        return cmp;
     > +    }
     > +    const char *a_port = a->out_port ? a->out_port->key : NULL;
     > +    const char *b_port = b->out_port ? b->out_port->key : NULL;
     > +    if (!nullable_string_is_equal(a_port, b_port)) {
     > +        return !a_port ? -1 : !b_port ? 1 : strcmp(a_port, b_port);
     > +    }
     > +    if (!nullable_string_is_equal(a->lrp_addr_s, b->lrp_addr_s)) {
     > +        return !a->lrp_addr_s ? -1
     > +               : !b->lrp_addr_s ? 1
     > +               : strcmp(a->lrp_addr_s, b->lrp_addr_s);
     > +    }
     > +    if (a->ecmp_symmetric_reply != b->ecmp_symmetric_reply) {
     > +        return a->ecmp_symmetric_reply ? -1 : 1;
     > +    }
     > +    if (!a->source_hint || !b->source_hint) {
     > +        return !!a->source_hint - !!b->source_hint;
     > +    }
     > +    return uuid_compare_3way(&a->source_hint->uuid, &b-
     >source_hint->uuid);
     > +}
     > +
     > +/* NAT and LB routes for the same prefix share a group, see
     > + * route_sources_ecmp_compatible(). */
     > +static enum route_source
     > +ecmp_group_source_class(enum route_source source)
     > +{
     > +    return source == ROUTE_SOURCE_LB ? ROUTE_SOURCE_NAT : source;
     > +}
     > +
     > +/* Orders the ecmp groups of a datapath by their key only.  The
    key is unique
     > + * within a datapath, see ecmp_groups_find(). */
     > +static int
     > +ecmp_groups_node_cmp(const void *a_, const void *b_)
     > +{
     > +    const struct ecmp_groups_node *a =
     > +        *(const struct ecmp_groups_node *const *) a_;
     > +    const struct ecmp_groups_node *b =
     > +        *(const struct ecmp_groups_node *const *) b_;
     > +
     > +    if (a->route_table_id != b->route_table_id) {
     > +        return a->route_table_id < b->route_table_id ? -1 : 1;
     > +    }
     > +    if (a->is_src_route != b->is_src_route) {
     > +        return a->is_src_route ? 1 : -1;
     > +    }
     > +    if (a->plen != b->plen) {
     > +        return a->plen < b->plen ? -1 : 1;
     > +    }
     > +    int cmp = memcmp(&a->prefix, &b->prefix, sizeof a->prefix);
     > +    if (cmp) {
     > +        return cmp;
     > +    }
     > +    /* Equal source classes mean the whole key is equal, i.e.
    'a' and 'b' are
     > +     * the same group, so this never returns 0 for two distinct
    groups. */
     > +    int a_class = ecmp_group_source_class(a->source);
     > +    int b_class = ecmp_group_source_class(b->source);
     > +    return a_class < b_class ? -1 : a_class > b_class;
     > +}
     > +
     > +/* Recomputes everything in 'group' that is derived from its
    members, so that
     > + * the result only depends on the set of members. */
     >   static void
     > -ecmp_group_update_ids(struct ecmp_groups_node *group)
     > +ecmp_group_finalize(struct ecmp_groups_node *group)
     >   {
     > +    vector_qsort(&group->route_list, ecmp_route_list_node_cmp);
     > +
     >       struct ecmp_route_list_node *er;
     > -    size_t i = 0;
     > +    uint16_t id = 0;
     > +    group->has_discard_route = false;
     >       VECTOR_FOR_EACH_PTR (&group->route_list, er) {
     > -        er->id = i++;
     > +        er->id = ++id;
     > +        if (er->route->is_discard_route) {
     > +            group->has_discard_route = true;
     > +        }
     > +        if (id == 1) {
     > +            sset_destroy(&group->selection_fields);
     > +            sset_clone(&group->selection_fields,
     > +                       &er->route->ecmp_selection_fields);
     > +            group->source = er->route->source;
     > +        } else {
     > +            sset_intersect(&group->selection_fields,
     > +                           &er->route->ecmp_selection_fields);
     > +        }
     > +    }
     > +}
     > +
> +/* Assigns the group and member ids of all ecmp groups of 'gn'. Must be
     > + * called after the ecmp groups of 'gn' changed, both on full
    recompute and on
     > + * incremental processing, so that both yield the same ids for
    the same set
     > + * of routes and the generated logical flows do not change on
    recompute. */
     > +static void
     > +group_ecmp_datapath_finalize(struct group_ecmp_datapath *gn)
     > +{
     > +    size_t n = hmap_count(&gn->ecmp_groups);
     > +    if (!n) {
     > +        return;
     > +    }
     > +
     > +    struct ecmp_groups_node **groups = xmalloc(n * sizeof *groups);
     > +    struct ecmp_groups_node *eg;
     > +    size_t i = 0;
     > +    HMAP_FOR_EACH (eg, hmap_node, &gn->ecmp_groups) {
     > +        ecmp_group_finalize(eg);
     > +        groups[i++] = eg;
     > +    }
     > +
     > +    qsort(groups, n, sizeof *groups, ecmp_groups_node_cmp);
     > +    for (i = 0; i < n; i++) {
     > +        groups[i]->id = i + 1;
     >       }
     > -    group->route_count = i;
     > +    free(groups);
     >   }
     >
     >   static struct ecmp_groups_node *
     > @@ -302,7 +416,6 @@ ecmp_groups_add(struct group_ecmp_datapath *gn,
     >       struct ecmp_groups_node *eg = xzalloc(sizeof *eg);
     >       hmap_insert(&gn->ecmp_groups, &eg->hmap_node, route->hash);
     >
     > -    eg->id = hmap_count(&gn->ecmp_groups);
     >       eg->prefix = route->prefix;
     >       eg->plen = route->plen;
     >       eg->is_src_route = route->is_src_route;
     > @@ -396,6 +509,10 @@ group_ecmp_route(struct
    group_ecmp_route_data *data,
     >           gn = group_ecmp_datapath_lookup_or_add(data, pr->od);
     >           add_route(gn, pr);
     >       }
     > +
     > +    HMAP_FOR_EACH (gn, hmap_node, &data->datapaths) {
     > +        group_ecmp_datapath_finalize(gn);
     > +    }
     >   }
     >
     >   enum engine_node_state
     > @@ -472,9 +589,7 @@ handle_deleted_route(struct
    group_ecmp_route_data *data,
     >                * unique route. Otherwise it stays an ecmp group
    with just one
     >                * member. */
     >               ecmp_groups_remove_route(eg, pr);
     > -            if (ecmp_group_has_symmetric_reply(eg)) {
     > -                ecmp_group_update_ids(eg);
     > -            } else {
     > +            if (!ecmp_group_has_symmetric_reply(eg)) {
     >                   const struct ecmp_route_list_node *er =
     >                       vector_get_ptr(&eg->route_list, 0);
     >                   unique_routes_add(node, er->route);
     > @@ -482,11 +597,8 @@ handle_deleted_route(struct
    group_ecmp_route_data *data,
     >                   ecmp_groups_node_free(eg);
     >               }
     >           } else {
     > -            /* We can just remove the member from the group. We
    need to update
     > -             * the indices of all routes so that future
    insertions directly
     > -             * have a new index. */
     > +            /* We can just remove the member from the group. */
     >               ecmp_groups_remove_route(eg, pr);
     > -            ecmp_group_update_ids(eg);
     >           }
     >       }
     >
     > @@ -537,6 +649,7 @@
    group_ecmp_route_learned_route_change_handler(struct engine_node
    *eng_node,
     >               hmapx_add(&data->trk_data.deleted_datapath_routes,
    node);
     >               hmap_remove(&data->datapaths, &node->hmap_node);
     >           } else {
     > +            group_ecmp_datapath_finalize(node);
     >               hmapx_add(&data-
     >trk_data.crupdated_datapath_routes, node);
     >           }
     >       }
     > @@ -589,6 +702,7 @@ group_ecmp_route_routes_change_handler(struct
    engine_node *eng_node,
     >               hmapx_add(&data->trk_data.deleted_datapath_routes,
    node);
     >               hmap_remove(&data->datapaths, &node->hmap_node);
     >           } else {
     > +            group_ecmp_datapath_finalize(node);
     >               hmapx_add(&data-
     >trk_data.crupdated_datapath_routes, node);
     >           }
     >       }
     > @@ -645,6 +759,7 @@
    group_ecmp_route_dynamic_routes_change_handler(struct engine_node
    *eng_node,
     >               hmapx_add(&gdata->trk_data.deleted_datapath_routes,
    node);
     >               hmap_remove(&gdata->datapaths, &node->hmap_node);
     >           } else {
     > +            group_ecmp_datapath_finalize(node);
     >               hmapx_add(&gdata-
     >trk_data.crupdated_datapath_routes, node);
     >           }
     >       }
     > diff --git a/northd/en-group-ecmp-route.h b/northd/en-group-ecmp-
    route.h
     > index b40935be2..13f84a036 100644
     > --- a/northd/en-group-ecmp-route.h
     > +++ b/northd/en-group-ecmp-route.h
     > @@ -37,7 +37,6 @@ struct ecmp_groups_node {
     >       bool has_discard_route;
     >       enum route_source source;
     >       uint32_t route_table_id;
     > -    uint16_t route_count;
     >       struct vector route_list; /* Contains ecmp_route_list_node */
     >       struct sset selection_fields;
     >   };
     > diff --git a/tests/ovn-northd.at <http://ovn-northd.at> b/tests/
    ovn-northd.at <http://ovn-northd.at>
     > index 7a0a400c4..4c9fec29f 100644
     > --- a/tests/ovn-northd.at <http://ovn-northd.at>
     > +++ b/tests/ovn-northd.at <http://ovn-northd.at>
     > @@ -24092,6 +24092,72 @@ done
     >   OVN_CLEANUP_NORTHD
     >   AT_CLEANUP
     >
     > +OVN_FOR_EACH_NORTHD_NO_HV([
     > +AT_SETUP([Static routes - ECMP ids stable across recompute])
     > +AT_KEYWORDS([ecmp])
     > +ovn_start
     > +
     > +check ovn-nbctl lr-add lr0
     > +for i in 1 2 3 4; do
     > +    check ovn-nbctl lrp-add lr0 lr0-sw$i 00:00:00:00:0$i:01
    10.0.$i.1/24
     > +done
     > +check ovn-nbctl --wait=sb sync
     > +
     > +dnl Add the routes in "reverse" order, the ids must not depend
    on it.
     > +for p in 192.168.30.0/24 <http://192.168.30.0/24>
    192.168.20.0/24 <http://192.168.20.0/24> 192.168.10.0/24
    <http://192.168.10.0/24>; do
     > +    for i in 3 2 1; do
     > +        check ovn-nbctl --wait=sb --ecmp lr-route-add lr0 $p
    10.0.$i.10
     > +    done
     > +done
     > +CHECK_NO_CHANGE_AFTER_RECOMPUTE
     > +
     > +AT_CHECK([ovn-sbctl dump-flows lr0 | grep -w lr_in_ip_routing |
    grep select | ovn_strip_lflows], [0], [dnl
     > +  table=??(lr_in_ip_routing   ), priority=1840 , match=(reg7 ==
    0 && ip4.dst == 192.168.10.0/24 <http://192.168.10.0/24>),
    action=(ip.ttl--; flags.loopback = 1; reg8[[0..15]] = 1;
    reg8[[16..31]] = select(1, 2, 3);)
     > +  table=??(lr_in_ip_routing   ), priority=1840 , match=(reg7 ==
    0 && ip4.dst == 192.168.20.0/24 <http://192.168.20.0/24>),
    action=(ip.ttl--; flags.loopback = 1; reg8[[0..15]] = 2;
    reg8[[16..31]] = select(1, 2, 3);)
     > +  table=??(lr_in_ip_routing   ), priority=1840 , match=(reg7 ==
    0 && ip4.dst == 192.168.30.0/24 <http://192.168.30.0/24>),
    action=(ip.ttl--; flags.loopback = 1; reg8[[0..15]] = 3;
    reg8[[16..31]] = select(1, 2, 3);)
     > +])
     > +AT_CHECK([ovn-sbctl dump-flows lr0 | grep lr_in_ip_routing_ecmp
    | grep -F "reg8[[0..15]] == 1 &&" | ovn_strip_lflows], [0], [dnl
     > +  table=??(lr_in_ip_routing_ecmp), priority=100  ,
    match=(reg8[[0..15]] == 1 && reg8[[16..31]] == 1), action=(reg0 =
    10.0.1.10; reg5 = 10.0.1.1; eth.src = 00:00:00:00:01:01; outport =
    "lr0-sw1"; reg9[[9]] = 1; next;)
     > +  table=??(lr_in_ip_routing_ecmp), priority=100  ,
    match=(reg8[[0..15]] == 1 && reg8[[16..31]] == 2), action=(reg0 =
    10.0.2.10; reg5 = 10.0.2.1; eth.src = 00:00:00:00:02:01; outport =
    "lr0-sw2"; reg9[[9]] = 1; next;)
     > +  table=??(lr_in_ip_routing_ecmp), priority=100  ,
    match=(reg8[[0..15]] == 1 && reg8[[16..31]] == 3), action=(reg0 =
    10.0.3.10; reg5 = 10.0.3.1; eth.src = 00:00:00:00:03:01; outport =
    "lr0-sw3"; reg9[[9]] = 1; next;)
     > +])
     > +
     > +dnl Remove a member from the middle of a group with more than 2
    members.
     > +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
     > +check ovn-nbctl --wait=sb lr-route-del lr0 192.168.10.0/24
    <http://192.168.10.0/24> 10.0.2.10
     > +check_engine_compute group_ecmp_route incremental
     > +CHECK_NO_CHANGE_AFTER_RECOMPUTE
     > +
     > +dnl Remove a whole group and then add a new one, group ids must
    stay unique.
     > +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
     > +check ovn-nbctl --wait=sb lr-route-del lr0 192.168.20.0/24
    <http://192.168.20.0/24>
     > +check ovn-nbctl --wait=sb --ecmp lr-route-add lr0
    192.168.40.0/24 <http://192.168.40.0/24> 10.0.1.10
     > +check ovn-nbctl --wait=sb --ecmp lr-route-add lr0
    192.168.40.0/24 <http://192.168.40.0/24> 10.0.4.10
     > +check_engine_compute group_ecmp_route incremental
     > +CHECK_NO_CHANGE_AFTER_RECOMPUTE
     > +AT_CHECK([ovn-sbctl dump-flows lr0 | grep -w lr_in_ip_routing |
    grep select | ovn_strip_lflows], [0], [dnl
     > +  table=??(lr_in_ip_routing   ), priority=1840 , match=(reg7 ==
    0 && ip4.dst == 192.168.10.0/24 <http://192.168.10.0/24>),
    action=(ip.ttl--; flags.loopback = 1; reg8[[0..15]] = 1;
    reg8[[16..31]] = select(1, 2);)
     > +  table=??(lr_in_ip_routing   ), priority=1840 , match=(reg7 ==
    0 && ip4.dst == 192.168.30.0/24 <http://192.168.30.0/24>),
    action=(ip.ttl--; flags.loopback = 1; reg8[[0..15]] = 2;
    reg8[[16..31]] = select(1, 2, 3);)
     > +  table=??(lr_in_ip_routing   ), priority=1840 , match=(reg7 ==
    0 && ip4.dst == 192.168.40.0/24 <http://192.168.40.0/24>),
    action=(ip.ttl--; flags.loopback = 1; reg8[[0..15]] = 3;
    reg8[[16..31]] = select(1, 2);)
     > +])
     > +
     > +dnl Once no member is a discard route anymore the group must
    stop dropping.
     > +route=$(ovn-nbctl --bare --columns _uuid find
    Logical_Router_Static_Route \
     > +        ip_prefix="192.168.30.0/24 <http://192.168.30.0/24>"
    nexthop="10.0.3.10")
     > +check ovn-nbctl --wait=sb set Logical_Router_Static_Route $route
    nexthop=discard
     > +AT_CHECK([ovn-sbctl dump-flows lr0 | grep lr_in_ip_routing_ecmp
    | grep -c "drop;"], [0], [4
     > +])
     > +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
     > +check ovn-nbctl --wait=sb set Logical_Router_Static_Route $route
    nexthop=10.0.3.10
     > +check_engine_compute group_ecmp_route incremental
     > +AT_CHECK([ovn-sbctl dump-flows lr0 | grep lr_in_ip_routing_ecmp
    | grep -c "drop;"], [0], [1
     > +])
     > +CHECK_NO_CHANGE_AFTER_RECOMPUTE
     > +
     > +OVN_CLEANUP_NORTHD
     > +AT_CLEANUP
     > +])
     > +
     >   OVN_FOR_EACH_NORTHD_NO_HV([
     >   AT_SETUP([Static routes - ECMP with discard])
     >   ovn_start



/‘Esta mensagem é direcionada apenas para os endereços constantes no cabeçalho inicial. Se você não está listado nos endereços constantes no cabeçalho, pedimos-lhe que desconsidere completamente o conteúdo dessa mensagem e cuja cópia, encaminhamento e/ou execução das ações citadas estão imediatamente anuladas e proibidas’./

///‘Apesar do Magazine Luiza tomar todas as precauções razoáveis para assegurar que nenhum vírus esteja presente nesse e-mail, a empresa não poderá aceitar a responsabilidade por quaisquer perdas ou danos causados por esse e-mail ou por seus anexos’./


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

Reply via email to