On 6/22/26 6:26 PM, Lucas Vargas Dias wrote:
> Create a handler for static routes. It handles update, create or
> delete.
> Full recompute is triggered when update bfd column for static route.
> 
> Test with 2000 static routes created in the same logical router
> and add a new one:
> Without the incremental processing:
> ovn-nbctl --print-wait-time --wait=sb lr-route-add lr1-2 10.0.0.1/32 
> 192.168.20.2
> Time spent on processing nb_cfg 4:
>       ovn-northd delay before processing:     4ms
>       ovn-northd completion:                  62ms
> 
> With the incremental processing:
> ovn-nbctl --print-wait-time --wait=sb lr-route-add lr1-2 10.0.0.1/32 
> 192.168.20.2
> Time spent on processing nb_cfg 6:
>       ovn-northd delay before processing:     1ms
>       ovn-northd completion:                  18ms
> 
> Test with 2000 static routes created in the same logical router
> and delete one:
> Without the incremental processing:
> ovn-nbctl --print-wait-time --wait=sb lr-route-del lr1-2 10.0.0.1/32 
> 192.168.20.2
> Time spent on processing nb_cfg 5:
>       ovn-northd delay before processing:     3ms
>       ovn-northd completion:                  62ms
> 
> With the incremental processing:
> ovn-nbctl --print-wait-time --wait=sb lr-route-del lr1-2 10.0.0.1/32 
> 192.168.20.2
> Time spent on processing nb_cfg 9:
>       ovn-northd delay before processing:     10ms
>       ovn-northd completion:                  28ms
> 
> Signed-off-by: Lucas Vargas Dias <[email protected]>
> ---

Hi Lucas,

Thanks for the new revision!

>  northd/en-group-ecmp-route.c     |  52 ++++++++++
>  northd/en-group-ecmp-route.h     |   4 +
>  northd/en-lflow.c                |  27 +++++
>  northd/en-lflow.h                |   2 +
>  northd/en-northd.c               | 163 +++++++++++++++++++++++++++++--
>  northd/en-northd.h               |   7 ++
>  northd/inc-proc-northd.c         |  15 ++-
>  northd/northd.c                  | 135 ++++++++++++++++++++-----
>  northd/northd.h                  |  41 +++++++-
>  tests/ovn-inc-proc-graph-dump.at |   8 +-
>  tests/ovn-northd.at              | 133 +++++++++++++++++++++++--
>  11 files changed, 536 insertions(+), 51 deletions(-)
> 
> diff --git a/northd/en-group-ecmp-route.c b/northd/en-group-ecmp-route.c
> index c4c93fd84..5f9ffa84c 100644
> --- a/northd/en-group-ecmp-route.c
> +++ b/northd/en-group-ecmp-route.c
> @@ -519,3 +519,55 @@ group_ecmp_route_learned_route_change_handler(struct 
> engine_node *eng_node,
>      }
>      return EN_HANDLED_UNCHANGED;
>  }
> +
> +enum engine_input_handler_result
> +group_ecmp_static_route_change_handler(struct engine_node *eng_node,
> +                                       void *_data)
> +{
> +    struct routes_data *routes_data
> +        = engine_get_input_data("routes", eng_node);
> +    struct group_ecmp_route_data *data = _data;
> +    if (!routes_data->tracked) {
> +        data->tracked = false;
> +        return EN_UNHANDLED;
> +    }
> +
> +    struct parsed_route *pr;
> +    struct hmapx updated_routes = HMAPX_INITIALIZER(&updated_routes);
> +
> +    const struct hmapx_node *hmapx_node;
> +    HMAPX_FOR_EACH (hmapx_node,
> +                    &routes_data->trk_data.trk_deleted_parsed_route) {
> +        pr = hmapx_node->data;
> +        if (!handle_deleted_route(data, pr, &updated_routes)) {
> +            hmapx_destroy(&updated_routes);
> +            return EN_UNHANDLED;
> +        }
> +    }
> +
> +    HMAPX_FOR_EACH (hmapx_node,
> +                    &routes_data->trk_data.trk_crupdated_parsed_route) {
> +        pr = hmapx_node->data;
> +        handle_added_route(data, pr, &updated_routes);
> +    }
> +
> +    HMAPX_FOR_EACH (hmapx_node, &updated_routes) {
> +        struct group_ecmp_datapath *node = hmapx_node->data;
> +        if (hmap_is_empty(&node->unique_routes) &&
> +                hmap_is_empty(&node->ecmp_groups)) {
> +            hmapx_add(&data->trk_data.deleted_datapath_routes, node);
> +            hmap_remove(&data->datapaths, &node->hmap_node);
> +        } else {
> +            hmapx_add(&data->trk_data.crupdated_datapath_routes, node);
> +        }
> +    }
> +
> +    hmapx_destroy(&updated_routes);
> +
> +    if (!hmapx_is_empty(&data->trk_data.crupdated_datapath_routes) ||
> +        !hmapx_is_empty(&data->trk_data.deleted_datapath_routes)) {
> +        data->tracked = true;
> +        return EN_HANDLED_UPDATED;
> +    }
> +    return EN_HANDLED_UNCHANGED;
> +}
> diff --git a/northd/en-group-ecmp-route.h b/northd/en-group-ecmp-route.h
> index d4a3248d0..246ca06bf 100644
> --- a/northd/en-group-ecmp-route.h
> +++ b/northd/en-group-ecmp-route.h
> @@ -98,6 +98,10 @@ enum engine_input_handler_result
>  group_ecmp_route_learned_route_change_handler(struct engine_node *,
>                                                void *data);
>  
> +enum engine_input_handler_result
> +group_ecmp_static_route_change_handler(struct engine_node *,
> +                                       void *data);
> +
>  struct group_ecmp_datapath *group_ecmp_datapath_lookup(
>      const struct group_ecmp_route_data *data,
>      const struct ovn_datapath *od);
> diff --git a/northd/en-lflow.c b/northd/en-lflow.c
> index 8cb987777..893fdbad2 100644
> --- a/northd/en-lflow.c
> +++ b/northd/en-lflow.c
> @@ -268,6 +268,21 @@ lflow_multicast_igmp_handler(struct engine_node *node, 
> void *data)
>      return EN_HANDLED_UPDATED;
>  }
>  
> +enum engine_input_handler_result
> +lflow_group_route_change_handler(struct engine_node *node,
> +                                 void *data OVS_UNUSED)
> +{
> +    struct routes_data *route_data =
> +        engine_get_input_data("routes", node);
> +
> +    /* If we do not have tracked data we need to recompute. */
> +    if (!route_data->tracked) {
> +        return EN_UNHANDLED;
> +    }
> +
> +    return EN_HANDLED_UNCHANGED;
> +}

Route incremental processing is actually handled through the
en_group_ecmp_route routes change handler.  I don't think we need the
en_route -> en_lflow input handler at all.

If !route_data->tracked then en_group_ecmp_route recomputes which will
also cause en_lflow to recompute.

We should probably replace lflow_group_route_change_handler with
engine_noop_handler in inc-proc-northd.c and add a comment there about
why that's OK.

> +
>  enum engine_input_handler_result
>  lflow_group_ecmp_route_change_handler(struct engine_node *node,
>                                        void *data OVS_UNUSED)
> @@ -307,6 +322,8 @@ lflow_group_ecmp_route_change_handler(struct engine_node 
> *node,
>          }
>      }
>  
> +    struct ds match =  DS_EMPTY_INITIALIZER;

Nit: extra space.

> +    struct ds actions = DS_EMPTY_INITIALIZER;
>      /* Now we handle created or updated route nodes. */
>      struct hmapx *crupdated_datapath_routes =
>          &group_ecmp_route_data->trk_data.crupdated_datapath_routes;

Nit: I'd move the comment two lines higher and group the variable
definitions and also use reverse-xmas tree.

> @@ -317,6 +334,12 @@ lflow_group_ecmp_route_change_handler(struct engine_node 
> *node,
>              route_node->od, lflow_data->lflow_table,
>              route_node, lflow_input.bfd_ports);
>  
> +        build_arp_request_flows_for_lrouter(route_node->od,
> +                                            lflow_data->lflow_table,
> +                                            &match, &actions,
> +                                            lflow_input.meter_groups,
> +                                            route_node->lflow_ref);
> +
>          bool handled = lflow_ref_sync_lflows(
>              route_node->lflow_ref, lflow_data->lflow_table,
>              eng_ctx->ovnsb_idl_txn, lflow_input.dps,
> @@ -324,10 +347,14 @@ lflow_group_ecmp_route_change_handler(struct 
> engine_node *node,
>              lflow_input.sbrec_logical_flow_table,
>              lflow_input.sbrec_logical_dp_group_table);
>          if (!handled) {
> +            ds_destroy(&match);
> +            ds_destroy(&actions);
>              return EN_UNHANDLED;
>          }
>      }
>  
> +    ds_destroy(&match);
> +    ds_destroy(&actions);
>      return EN_HANDLED_UPDATED;
>  }
>  
> diff --git a/northd/en-lflow.h b/northd/en-lflow.h
> index 99bcfda15..31ccc0925 100644
> --- a/northd/en-lflow.h
> +++ b/northd/en-lflow.h
> @@ -29,5 +29,7 @@ lflow_multicast_igmp_handler(struct engine_node *node, void 
> *data);
>  enum engine_input_handler_result
>  lflow_group_ecmp_route_change_handler(struct engine_node *node, void *data);
>  enum engine_input_handler_result
> +lflow_group_route_change_handler(struct engine_node *node, void *data);
> +enum engine_input_handler_result
>  lflow_ic_learned_svc_mons_handler(struct engine_node *node, void *data);
>  #endif /* EN_LFLOW_H */
> diff --git a/northd/en-northd.c b/northd/en-northd.c
> index c34818dba..fb2242f11 100644
> --- a/northd/en-northd.c
> +++ b/northd/en-northd.c
> @@ -207,7 +207,8 @@ northd_nb_logical_router_handler(struct engine_node *node,
>      }
>  
>      if (northd_has_lr_nats_in_tracked_data(&nd->trk_data) ||
> -        northd_has_lrouters_in_tracked_data(&nd->trk_data)) {
> +        northd_has_lrouters_in_tracked_data(&nd->trk_data) ||
> +        northd_has_lr_route_in_tracked_data(&nd->trk_data)) {
>          return EN_HANDLED_UPDATED;
>      }
>  
> @@ -329,7 +330,7 @@ en_route_policies_run(struct engine_node *node, void 
> *data)
>  
>  enum engine_input_handler_result
>  routes_northd_change_handler(struct engine_node *node,
> -                                    void *data OVS_UNUSED)
> +                             void *data OVS_UNUSED)
>  {
>      struct northd_data *northd_data = engine_get_input_data("northd", node);
>      if (!northd_has_tracked_data(&northd_data->trk_data)) {
> @@ -347,14 +348,145 @@ routes_northd_change_handler(struct engine_node *node,
>       *      Note: When we add I-P to the created/deleted logical routers or
>       *      logical router ports, we need to revisit this handler.
>       *
> -     *      This node also accesses the static routes of the logical router.
> -     *      When these static routes gets updated, en_northd engine 
> recomputes
> -     *      and so does this node.
> -     *      Note: When we add I-P to handle static routes changes, we need
> -     *      to revisit this handler.
>       */
>      return EN_HANDLED_UNCHANGED;
>  }

Nit: missing newline.

> +enum engine_input_handler_result
> +routes_static_route_change_handler(struct engine_node *node,
> +                                   void *data)
> +{
> +    struct routes_data *routes_data = data;
> +    const struct nbrec_logical_router_static_route_table *
> +      nb_lr_static_route_table =

Nit: indentation.

> +    EN_OVSDB_GET(engine_get_input("NB_logical_router_static_route", node));
> +
> +    struct northd_data *northd_data = engine_get_input_data("northd", node);
> +    struct bfd_data *bfd_data = engine_get_input_data("bfd", node);
> +    struct parsed_route *pr;
> +    const struct nbrec_logical_router_static_route *changed_static_route;
> +    NBREC_LOGICAL_ROUTER_STATIC_ROUTE_TABLE_FOR_EACH_TRACKED (
> +                            changed_static_route, nb_lr_static_route_table) {
> +
> +        bool is_deleted = nbrec_logical_router_static_route_is_deleted(
> +                                                        
> changed_static_route);
> +        bool is_new = nbrec_logical_router_static_route_is_new(
> +                                                        
> changed_static_route);
> +
> +
> +        if (is_new && is_deleted) {
> +            continue;
> +        }
> +
> +        if (is_new) {
> +            struct ovn_datapath *od = ovn_datapath_find_by_static_route(
> +                                        
> &northd_data->trk_data.trk_lrs_routes,
> +                                        &changed_static_route->header_.uuid);

Routers reference static routes in the schema so when a route change
happens the corresponding router record is also marked as updated.

So, to avoid the performance issue induced by looking up the router for
each static route, could we can instead iterate the other way around
here?  That is:

- For each updated router:
  - for each route record on that router:
    - if it's newly added or updated perform incremental processing

Then a separate loop for deleted routes:
- for each deleted NB static route:
  - perform incremental processing (as in your current patch)

We could get rid of ovn_datapath_find_by_static_route() completely like
that.

> +            if (!od) {
> +                continue;
> +            }
> +            pr = parsed_routes_add_static(od, &northd_data->lr_ports,
> +                                        changed_static_route,
> +                                        &bfd_data->bfd_connections,
> +                                        &routes_data->parsed_routes,
> +                                        &routes_data->route_tables,
> +                                        
> &routes_data->bfd_active_connections);
> +            if (!pr) {
> +                return EN_UNHANDLED;
> +            }
> +            hmapx_add(&routes_data->trk_data.trk_crupdated_parsed_route,
> +                       pr);
> +        }
> +
> +        if (is_deleted) {
> +            pr = parsed_route_lookup_by_source(
> +                                            ROUTE_SOURCE_STATIC,
> +                                            &changed_static_route->header_,
> +                                            &routes_data->parsed_routes);
> +            if (!pr) {
> +                pr = parsed_route_lookup_by_source(ROUTE_SOURCE_IC_DYNAMIC,
> +                                            &changed_static_route->header_,
> +                                            &routes_data->parsed_routes);
> +            }
> +            if (!pr) {
> +                return EN_UNHANDLED;
> +            }
> +            hmapx_add(&routes_data->trk_data.trk_deleted_parsed_route, pr);
> +            hmap_remove(&routes_data->parsed_routes, &pr->key_node);
> +        }
> +
> +        if (!is_new &&
> +            (nbrec_logical_router_static_route_is_updated(
> +                    changed_static_route,
> +                    NBREC_LOGICAL_ROUTER_STATIC_ROUTE_COL_NEXTHOP)
> +            || nbrec_logical_router_static_route_is_updated(
> +                    changed_static_route,
> +                    NBREC_LOGICAL_ROUTER_STATIC_ROUTE_COL_IP_PREFIX)
> +            || nbrec_logical_router_static_route_is_updated(
> +                    changed_static_route,
> +                    NBREC_LOGICAL_ROUTER_STATIC_ROUTE_COL_OUTPUT_PORT)
> +            || nbrec_logical_router_static_route_is_updated(
> +                    changed_static_route,
> +                    NBREC_LOGICAL_ROUTER_STATIC_ROUTE_COL_POLICY)
> +            || nbrec_logical_router_static_route_is_updated(
> +                    changed_static_route,
> +                    NBREC_LOGICAL_ROUTER_STATIC_ROUTE_COL_ROUTE_TABLE)
> +            || nbrec_logical_router_static_route_is_updated(
> +                    changed_static_route,
> +                    NBREC_LOGICAL_ROUTER_STATIC_ROUTE_COL_SELECTION_FIELDS)
> +            ||  nbrec_logical_router_static_route_is_updated(
> +                    changed_static_route,
> +                    NBREC_LOGICAL_ROUTER_STATIC_ROUTE_COL_OPTIONS))) {
> +            pr = parsed_route_lookup_by_source(
> +                                            ROUTE_SOURCE_STATIC,
> +                                            &changed_static_route->header_,
> +                                            &routes_data->parsed_routes);
> +            if (!pr) {
> +                pr = parsed_route_lookup_by_source(
> +                                                ROUTE_SOURCE_IC_DYNAMIC,
> +                                                
> &changed_static_route->header_,
> +                                                &routes_data->parsed_routes);
> +            }
> +
> +            if (!pr || !pr->od) {
> +                return EN_UNHANDLED;
> +            }
> +            struct parsed_route *old_pr = pr;
> +            struct hmapx_node *route_node =
> +                hmapx_add(&routes_data->trk_data.trk_deleted_parsed_route,
> +                          old_pr);
> +            hmap_remove(&routes_data->parsed_routes, &old_pr->key_node);
> +            pr = parsed_routes_add_static(old_pr->od, &northd_data->lr_ports,
> +                                        changed_static_route,
> +                                        &bfd_data->bfd_connections,
> +                                        &routes_data->parsed_routes,
> +                                        &routes_data->route_tables,
> +                                        
> &routes_data->bfd_active_connections);
> +            if (!pr) {
> +                hmapx_delete(&routes_data->trk_data.trk_deleted_parsed_route,
> +                             route_node);

We leak old_pr here.

Also we don't need to remove it from the tracked data,
engine_recompute() will take care of calling the clear_tracked_data
handler which will properly destroy everything before the run() function
is called.

> +                return EN_UNHANDLED;
> +            }
> +
> +            hmapx_add(&routes_data->trk_data.trk_crupdated_parsed_route,
> +                       pr);
> +        }
> +
> +        if (!is_new && nbrec_logical_router_static_route_is_updated(
> +                    changed_static_route,
> +                    NBREC_LOGICAL_ROUTER_STATIC_ROUTE_COL_BFD))
> +        {
> +                return EN_UNHANDLED;
> +        }
> +    }
> +
> +    if (!hmapx_is_empty(&routes_data->trk_data.trk_crupdated_parsed_route) ||
> +        !hmapx_is_empty(&routes_data->trk_data.trk_deleted_parsed_route)) {
> +        routes_data->tracked = true;
> +        return EN_HANDLED_UPDATED;
> +    }
> +
> +    return EN_HANDLED_UNCHANGED;
> +}
>  
>  enum engine_node_state
>  en_routes_run(struct engine_node *node, void *data)
> @@ -421,6 +553,18 @@ bfd_sync_northd_change_handler(struct engine_node *node, 
> void *data OVS_UNUSED)
>      return EN_HANDLED_UNCHANGED;
>  }
>  
> +enum engine_input_handler_result
> +bfd_sync_route_change_handler(struct engine_node *node,
> +                              void *data OVS_UNUSED)
> +{
> +    struct routes_data *routes_data
> +        = engine_get_input_data("routes", node);
> +    if (!routes_data->tracked) {
> +        return EN_UNHANDLED;
> +    }
> +    return EN_HANDLED_UNCHANGED;
> +}
> +
>  enum engine_node_state
>  en_bfd_sync_run(struct engine_node *node, void *data)
>  {
> @@ -590,6 +734,11 @@ en_routes_cleanup(void *data)
>      routes_destroy(data);
>  }
>  
> +void
> +en_routes_clear_tracked_data(void *data)
> +{
> +    routes_clear_tracked(data);
> +}
>  void
>  en_bfd_cleanup(void *data)
>  {
> diff --git a/northd/en-northd.h b/northd/en-northd.h
> index 7794739b9..d4ef9e161 100644
> --- a/northd/en-northd.h
> +++ b/northd/en-northd.h
> @@ -39,9 +39,12 @@ enum engine_node_state en_route_policies_run(struct 
> engine_node *node,
>                                               void *data);
>  void *en_route_policies_init(struct engine_node *node OVS_UNUSED,
>                               struct engine_arg *arg OVS_UNUSED);
> +void en_routes_clear_tracked_data(void *data);
>  void en_routes_cleanup(void *data);
>  enum engine_input_handler_result
>  routes_northd_change_handler(struct engine_node *node, void *data 
> OVS_UNUSED);
> +enum engine_input_handler_result
> +routes_static_route_change_handler(struct engine_node *node, void *data);
>  enum engine_node_state en_routes_run(struct engine_node *node, void *data);
>  void *en_bfd_init(struct engine_node *node OVS_UNUSED,
>                    struct engine_arg *arg OVS_UNUSED);
> @@ -52,6 +55,10 @@ void *en_bfd_sync_init(struct engine_node *node OVS_UNUSED,
>  enum engine_input_handler_result
>  bfd_sync_northd_change_handler(struct engine_node *node,
>                                 void *data OVS_UNUSED);
> +enum engine_input_handler_result
> +bfd_sync_route_change_handler(struct engine_node *node,
> +                              void *data OVS_UNUSED);
> +
>  enum engine_node_state en_bfd_sync_run(struct engine_node *node, void *data);
>  void en_bfd_sync_cleanup(void *data OVS_UNUSED);
>  void *en_ic_learned_svc_monitors_init(struct engine_node *node OVS_UNUSED,
> diff --git a/northd/inc-proc-northd.c b/northd/inc-proc-northd.c
> index a2b464411..640c7abf1 100644
> --- a/northd/inc-proc-northd.c
> +++ b/northd/inc-proc-northd.c
> @@ -75,7 +75,9 @@ static unixctl_cb_func chassis_features_list;
>      NB_NODE(sampling_app) \
>      NB_NODE(network_function) \
>      NB_NODE(network_function_group) \
> -    NB_NODE(logical_switch_port_health_check)
> +    NB_NODE(logical_switch_port_health_check) \
> +    NB_NODE(logical_router_static_route)
> +
>  
>      enum nb_engine_node {
>  #define NB_NODE(NAME) NB_##NAME,
> @@ -177,7 +179,7 @@ static ENGINE_NODE(lr_nat, CLEAR_TRACKED_DATA);
>  static ENGINE_NODE(lr_stateful, CLEAR_TRACKED_DATA);
>  static ENGINE_NODE(ls_stateful, CLEAR_TRACKED_DATA);
>  static ENGINE_NODE(route_policies);
> -static ENGINE_NODE(routes);
> +static ENGINE_NODE(routes, CLEAR_TRACKED_DATA);
>  static ENGINE_NODE(bfd);
>  static ENGINE_NODE(bfd_sync, SB_WRITE);
>  static ENGINE_NODE(ecmp_nexthop, SB_WRITE);
> @@ -336,10 +338,12 @@ void inc_proc_northd_init(struct ovsdb_idl_loop *nb,
>      engine_add_input(&en_routes, &en_bfd, NULL);
>      engine_add_input(&en_routes, &en_northd,
>                       routes_northd_change_handler);
> +    engine_add_input(&en_routes, &en_nb_logical_router_static_route,
> +                     routes_static_route_change_handler);
>  
>      engine_add_input(&en_bfd_sync, &en_bfd, NULL);
>      engine_add_input(&en_bfd_sync, &en_nb_bfd, NULL);
> -    engine_add_input(&en_bfd_sync, &en_routes, NULL);
> +    engine_add_input(&en_bfd_sync, &en_routes, 
> bfd_sync_route_change_handler);
>      engine_add_input(&en_bfd_sync, &en_route_policies, NULL);
>      engine_add_input(&en_bfd_sync, &en_northd, 
> bfd_sync_northd_change_handler);
>  
> @@ -375,7 +379,8 @@ void inc_proc_northd_init(struct ovsdb_idl_loop *nb,
>      engine_add_input(&en_learned_route_sync, &en_northd,
>                       learned_route_sync_northd_change_handler);
>  
> -    engine_add_input(&en_group_ecmp_route, &en_routes, NULL);
> +    engine_add_input(&en_group_ecmp_route, &en_routes,
> +                     group_ecmp_static_route_change_handler);

The rule we try to follow when naming change handlers is:

<target-node>_<input_node>_change_handler.

So this should be:

group_ecmp_route_routes_change_handler()

>      engine_add_input(&en_group_ecmp_route, &en_learned_route_sync,
>                       group_ecmp_route_learned_route_change_handler);
>  
> @@ -394,7 +399,7 @@ void inc_proc_northd_init(struct ovsdb_idl_loop *nb,
>      engine_add_input(&en_lflow, &en_sb_logical_dp_group, NULL);
>      engine_add_input(&en_lflow, &en_bfd_sync, NULL);
>      engine_add_input(&en_lflow, &en_route_policies, NULL);
> -    engine_add_input(&en_lflow, &en_routes, NULL);
> +    engine_add_input(&en_lflow, &en_routes, 
> lflow_group_route_change_handler);
>      /* XXX: The incremental processing only supports changes to learned 
> routes.
>       * All other changes trigger a full recompute. */

This comment is not valid anymore, it should be removed.

>      engine_add_input(&en_lflow, &en_group_ecmp_route,
> diff --git a/northd/northd.c b/northd/northd.c
> index f5aa5cca3..982c102ee 100644
> --- a/northd/northd.c
> +++ b/northd/northd.c
> @@ -659,6 +659,26 @@ ovn_datapath_find_by_key(struct hmap *datapaths, 
> uint32_t dp_key)
>      return NULL;
>  }
>  
> +struct ovn_datapath *
> +ovn_datapath_find_by_static_route(struct hmapx *datapaths,
> +                                  const struct uuid *route_uuid)
> +{

This is quite an expensive lookup.  On scaled setups this might be a
problem.  It's O(N x M) where N == "number of datapaths" and M ==
"number of routes".  And we call it every time a new route is added.

> +
> +    struct hmapx_node *hmapx_node;
> +    HMAPX_FOR_EACH (hmapx_node, datapaths) {
> +        struct ovn_datapath *od = hmapx_node->data;
> +        if (od->nbr) {
> +            for (int i = 0; i < od->nbr->n_static_routes; i++) {

Please use size_t for iterators like this one.

> +                struct nbrec_logical_router_static_route *static_route =
> +                    od->nbr->static_routes[i];
> +                if (uuid_equals(route_uuid, &static_route->header_.uuid)) {
> +                    return od;
> +                }
> +            }
> +        }
> +    }
> +    return NULL;
> +}
>  struct ovn_datapath *
>  ovn_datapath_from_sbrec_(const struct hmap *datapaths,
>                           const struct sbrec_datapath_binding *sb)
> @@ -4522,6 +4542,7 @@ destroy_northd_data_tracked_changes(struct northd_data 
> *nd)
>      destroy_tracked_ovn_ports(&trk_changes->trk_lsps);
>      destroy_tracked_lbs(&trk_changes->trk_lbs);
>      hmapx_clear(&trk_changes->trk_nat_lrs);
> +    hmapx_clear(&trk_changes->trk_lrs_routes);
>      hmapx_clear(&trk_changes->ls_with_changed_lbs);
>      hmapx_clear(&trk_changes->ls_with_changed_acls);
>      hmapx_clear(&trk_changes->ls_with_changed_ipam);
> @@ -4545,6 +4566,7 @@ init_northd_tracked_data(struct northd_data *nd)
>      hmapx_init(&trk_data->trk_lbs.crupdated);
>      hmapx_init(&trk_data->trk_lbs.deleted);
>      hmapx_init(&trk_data->trk_nat_lrs);
> +    hmapx_init(&trk_data->trk_lrs_routes);
>      hmapx_init(&trk_data->ls_with_changed_lbs);
>      hmapx_init(&trk_data->ls_with_changed_acls);
>      hmapx_init(&trk_data->ls_with_changed_ipam);
> @@ -4563,6 +4585,7 @@ destroy_northd_tracked_data(struct northd_data *nd)
>      hmapx_destroy(&trk_data->trk_lbs.crupdated);
>      hmapx_destroy(&trk_data->trk_lbs.deleted);
>      hmapx_destroy(&trk_data->trk_nat_lrs);
> +    hmapx_destroy(&trk_data->trk_lrs_routes);
>      hmapx_destroy(&trk_data->ls_with_changed_lbs);
>      hmapx_destroy(&trk_data->ls_with_changed_acls);
>      hmapx_destroy(&trk_data->ls_with_changed_ipam);
> @@ -5387,7 +5410,8 @@ lr_changes_can_be_handled(const struct 
> nbrec_logical_router *lr)
>          if (nbrec_logical_router_is_updated(lr, col)) {
>              if (col == NBREC_LOGICAL_ROUTER_COL_LOAD_BALANCER
>                  || col == NBREC_LOGICAL_ROUTER_COL_LOAD_BALANCER_GROUP
> -                || col == NBREC_LOGICAL_ROUTER_COL_NAT) {
> +                || col == NBREC_LOGICAL_ROUTER_COL_NAT
> +                || col == NBREC_LOGICAL_ROUTER_COL_STATIC_ROUTES) {
>                  continue;
>              }
>              return false;
> @@ -5412,12 +5436,7 @@ lr_changes_can_be_handled(const struct 
> nbrec_logical_router *lr)
>              return false;
>          }
>      }
> -    for (size_t i = 0; i < lr->n_static_routes; i++) {
> -        if (nbrec_logical_router_static_route_row_get_seqno(
> -            lr->static_routes[i], OVSDB_IDL_CHANGE_MODIFY) > 0) {
> -            return false;
> -        }
> -    }
> +
>      return true;
>  }
>  
> @@ -5443,6 +5462,27 @@ is_lr_nats_changed(const struct nbrec_logical_router 
> *nbr) {
>              || is_lr_nats_seqno_changed(nbr));
>  }
>  
> +static bool
> +is_lr_static_routes_seqno_changed(const struct nbrec_logical_router *nbr)
> +{
> +    for (size_t i = 0; i < nbr->n_static_routes; i++) {
> +        if (nbrec_logical_router_static_route_row_get_seqno(
> +                        nbr->static_routes[i], OVSDB_IDL_CHANGE_MODIFY) > 0) 
> {
> +            return true;
> +        }
> +    }
> +
> +    return false;
> +}
> +
> +static bool
> +is_lr_static_routes_changed(const struct nbrec_logical_router *nbr) {

Curly braces go on the next line for function definitions.

> +    return nbrec_logical_router_is_updated(nbr,
> +                                   NBREC_LOGICAL_ROUTER_COL_STATIC_ROUTES)
> +           || is_lr_static_routes_seqno_changed(nbr);
> +}
> +
> +
>  /* Return true if changes are handled incrementally, false otherwise.
>   *
>   * Note: Changes to load balancer and load balancer groups associated with
> @@ -5514,6 +5554,22 @@ northd_handle_lr_changes(const struct northd_input *ni,
>  
>              hmapx_add(&nd->trk_data.trk_nat_lrs, od);
>          }
> +
> +        /* Static Route was added or deleted. */
> +        if (is_lr_static_routes_changed(changed_lr)) {
> +            struct ovn_datapath *od = ovn_datapath_find_(
> +                                    &nd->lr_datapaths.datapaths,
> +                                    &changed_lr->header_.uuid);
> +
> +            if (!od) {
> +                static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 
> 1);
> +                VLOG_WARN_RL(&rl, "Internal error: a tracked updated LR "
> +                            "doesn't exist in lr_datapaths: "UUID_FMT,
> +                            UUID_ARGS(&changed_lr->header_.uuid));
> +                goto fail;
> +            }
> +            hmapx_add(&nd->trk_data.trk_lrs_routes, od);
> +        }
>      }
>  
>      HMAPX_FOR_EACH (node, &ni->synced_lrs->deleted) {
> @@ -5554,6 +5610,9 @@ northd_handle_lr_changes(const struct northd_input *ni,
>      if (!hmapx_is_empty(&nd->trk_data.trk_nat_lrs)) {
>          nd->trk_data.type |= NORTHD_TRACKED_LR_NATS;
>      }
> +    if (!hmapx_is_empty(&nd->trk_data.trk_lrs_routes)) {
> +        nd->trk_data.type |= NORTHD_TRACKED_LR_ROUTES;
> +    }
>      if (!hmapx_is_empty(&nd->trk_data.trk_routers.crupdated) ||
>          !hmapx_is_empty(&nd->trk_data.trk_routers.deleted)) {
>          nd->trk_data.type |= NORTHD_TRACKED_ROUTERS;
> @@ -12337,7 +12396,7 @@ parsed_route_add(const struct ovn_datapath *od,
>      }
>  }
>  
> -static void
> +struct parsed_route *
>  parsed_routes_add_static(const struct ovn_datapath *od,
>                           const struct hmap *lr_ports,
>                           const struct nbrec_logical_router_static_route 
> *route,
> @@ -12358,8 +12417,9 @@ parsed_routes_add_static(const struct ovn_datapath 
> *od,
>                           UUID_FMT, route->nexthop,
>                           UUID_ARGS(&route->header_.uuid));
>              free(nexthop);
> -            return;
> +            return NULL;
>          }
> +
>          if ((IN6_IS_ADDR_V4MAPPED(nexthop) && plen != 32) ||
>              (!IN6_IS_ADDR_V4MAPPED(nexthop) && plen != 128)) {
>              static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
> @@ -12367,7 +12427,7 @@ parsed_routes_add_static(const struct ovn_datapath 
> *od,
>                           UUID_FMT, route->nexthop,
>                           UUID_ARGS(&route->header_.uuid));
>              free(nexthop);
> -            return;
> +            return NULL;
>          }
>      }
>  
> @@ -12379,7 +12439,7 @@ parsed_routes_add_static(const struct ovn_datapath 
> *od,
>                       UUID_FMT, route->ip_prefix,
>                       UUID_ARGS(&route->header_.uuid));
>          free(nexthop);
> -        return;
> +        return NULL;
>      }
>  
>      /* Verify that ip_prefix and nexthop are on the same network. */
> @@ -12391,7 +12451,7 @@ parsed_routes_add_static(const struct ovn_datapath 
> *od,
>                                     : IN6_IS_ADDR_V4MAPPED(&prefix),
>                                     &lrp_addr_s, &out_port)) {
>          free(nexthop);
> -        return;
> +        return NULL;
>      }
>  
>      const struct nbrec_bfd *nb_bt = route->bfd;
> @@ -12401,7 +12461,7 @@ parsed_routes_add_static(const struct ovn_datapath 
> *od,
>                                                    nb_bt->dst_ip);
>          if (!bfd_e) {
>              free(nexthop);
> -            return;
> +            return NULL;
>          }
>  
>          /* This static route is linked to an active bfd session. */
> @@ -12418,10 +12478,9 @@ parsed_routes_add_static(const struct ovn_datapath 
> *od,
>              bfd_set_status(bfd_sr, "down");
>          }
>  
> -
>          if (!strcmp(bfd_sr->status, "down")) {
> -            free(nexthop);
> -            return;
> +           free(nexthop);
> +           return NULL;
>          }
>      }
>  
> @@ -12465,12 +12524,17 @@ parsed_routes_add_static(const struct ovn_datapath 
> *od,
>      bool dynamic_routing_advertise = smap_get_bool(&route->options,
>                                                     
> "dynamic-routing-advertise",
>                                                     true);
> -    parsed_route_add(od, nexthop, &prefix, plen, is_discard_route, 
> lrp_addr_s,
> -                     out_port, route_table_id, is_src_route,
> -                     ecmp_symmetric_reply, override_connected,
> -                     &ecmp_selection_fields, source, 
> dynamic_routing_advertise,
> -                     &route->header_, NULL, routes);
> +    struct parsed_route *pr = parsed_route_add(od, nexthop, &prefix, plen,
> +                                               is_discard_route, lrp_addr_s,
> +                                               out_port, route_table_id,
> +                                               is_src_route,
> +                                               ecmp_symmetric_reply,
> +                                               override_connected,
> +                                               &ecmp_selection_fields, 
> source,
> +                                               dynamic_routing_advertise,
> +                                               &route->header_, NULL, 
> routes);
>      sset_destroy(&ecmp_selection_fields);
> +    return pr;
>  }
>  
>  static void
> @@ -16374,12 +16438,12 @@ build_lr_gateway_redirect_flows_for_nats(
>   * In the common case where the Ethernet destination has been resolved,
>   * this table outputs the packet (priority 0).  Otherwise, it composes
>   * and sends an ARP/IPv6 NA request (priority 100). */
> -static void
> +void
>  build_arp_request_flows_for_lrouter(
> -        struct ovn_datapath *od, struct lflow_table *lflows,
> -        struct ds *match, struct ds *actions,
> -        const struct shash *meter_groups,
> -        struct lflow_ref *lflow_ref)
> +    const struct ovn_datapath *od, struct lflow_table *lflows,
> +    struct ds *match, struct ds *actions,
> +    const struct shash *meter_groups,
> +    struct lflow_ref *lflow_ref)
>  {
>      ovs_assert(od->nbr);
>      for (int i = 0; i < od->nbr->n_static_routes; i++) {
> @@ -21076,6 +21140,9 @@ routes_init(struct routes_data *data)
>      hmap_init(&data->parsed_routes);
>      simap_init(&data->route_tables);
>      hmap_init(&data->bfd_active_connections);
> +    data->tracked = false;
> +    hmapx_init(&data->trk_data.trk_deleted_parsed_route);
> +    hmapx_init(&data->trk_data.trk_crupdated_parsed_route);
>  }
>  
>  void
> @@ -21206,6 +21273,22 @@ routes_destroy(struct routes_data *data)
>  
>      simap_destroy(&data->route_tables);
>      __bfd_destroy(&data->bfd_active_connections);
> +    data->tracked = false;
> +    hmapx_destroy(&data->trk_data.trk_crupdated_parsed_route);
> +    hmapx_destroy(&data->trk_data.trk_deleted_parsed_route);
> +}
> +
> +void
> +routes_clear_tracked(struct routes_data *data)
> +{
> +    data->tracked = false;
> +    hmapx_clear(&data->trk_data.trk_crupdated_parsed_route);
> +    struct hmapx_node *hmapx_node;
> +    HMAPX_FOR_EACH_SAFE (hmapx_node,
> +                         &data->trk_data.trk_deleted_parsed_route) {
> +        parsed_route_free(hmapx_node->data);
> +        hmapx_delete(&data->trk_data.trk_deleted_parsed_route, hmapx_node);
> +    }
>  }
>  
>  void
> diff --git a/northd/northd.h b/northd/northd.h
> index 726a416e4..4b8c2bd37 100644
> --- a/northd/northd.h
> +++ b/northd/northd.h
> @@ -110,6 +110,9 @@ ods_size(const struct ovn_datapaths *datapaths)
>  struct ovn_datapath *
>  ovn_datapath_find_by_key(struct hmap *datapaths, uint32_t dp_key);
>  
> +struct ovn_datapath *
> +ovn_datapath_find_by_static_route(struct hmapx *, const struct uuid *);
> +
>  bool od_has_lb_vip(const struct ovn_datapath *od);
>  
>  /* List of routing and routing-related protocols which
> @@ -159,6 +162,7 @@ enum northd_tracked_data_type {
>      NORTHD_TRACKED_LS_ACLS  = (1 << 4),
>      NORTHD_TRACKED_SWITCHES = (1 << 5),
>      NORTHD_TRACKED_ROUTERS  = (1 << 6),
> +    NORTHD_TRACKED_LR_ROUTES  = (1 << 7),
>  };
>  
>  /* Track what's changed in the northd engine node.
> @@ -176,6 +180,10 @@ struct northd_tracked_data {
>       * hmapx node is 'struct ovn_datapath *'. */
>      struct hmapx trk_nat_lrs;
>  
> +    /* Tracked logical routers whose static routes have changed.
> +     * hmapx node is 'struct ovn_datapath *'. */
> +    struct hmapx trk_lrs_routes;
> +
>      /* Tracked logical switches whose load balancers have changed.
>       * hmapx node is 'struct ovn_datapath *'. */
>      struct hmapx ls_with_changed_lbs;
> @@ -216,10 +224,23 @@ struct route_policy {
>      uint32_t jump_chain_id;
>  };
>  
> +struct route_tracked_data {
> +    /* Contains references to group_ecmp_route_node. Each of the referenced

s/group_ecmp_route_node/parsed_route/

> +     * datapaths contains at least one route. */
> +    struct hmapx trk_crupdated_parsed_route;
> +
> +    /* Contains references to group_ecmp_route_node. Each of the referenced

s/group_ecmp_route_node/parsed_route/

> +     * datapath previously had some routes. The datapath now no longer
> +     * contains any route.*/

Nit: missing space after '.'.

Actually, the comments are misleading and probably just copied from
en-group-ecmp-route.h.

Maybe we should just skip the comments all together, the field names are
explicit enough.

> +    struct hmapx trk_deleted_parsed_route;
> +};
> +
>  struct routes_data {
>      struct hmap parsed_routes; /* Stores struct parsed_route. */
>      struct simap route_tables;
>      struct hmap bfd_active_connections;
> +    bool tracked;
> +    struct route_tracked_data trk_data;
>  };
>  
>  struct route_policies_data {
> @@ -886,6 +907,14 @@ struct parsed_route *parsed_route_add(
>      const struct ovn_port *tracked_port,
>      struct hmap *routes);
>  
> +struct  parsed_route * parsed_routes_add_static(

Nit: extra space after '*'.

> +    const struct ovn_datapath *od,
> +    const struct hmap *lr_ports,
> +    const struct nbrec_logical_router_static_route *route,
> +    const struct hmap *bfd_connections,
> +    struct hmap *routes, struct simap *route_tables,
> +    struct hmap *bfd_active_connections);
> +
>  struct svc_monitors_map_data {
>      const struct hmap *local_svc_monitors_map;
>      const struct hmap *ic_learned_svc_monitors_map;
> @@ -930,7 +959,7 @@ void build_parsed_routes(const struct ovn_datapath *, 
> const struct hmap *,
>  uint32_t get_route_table_id(struct simap *, const char *);
>  void routes_init(struct routes_data *);
>  void routes_destroy(struct routes_data *);
> -
> +void routes_clear_tracked(struct routes_data *);
>  void bfd_init(struct bfd_data *);
>  void bfd_destroy(struct bfd_data *);
>  
> @@ -956,6 +985,11 @@ void build_route_data_flows_for_lrouter(
>      const struct ovn_datapath *od, struct lflow_table *lflows,
>      const struct group_ecmp_datapath *route_node,
>      const struct sset *bfd_ports);
> +void build_arp_request_flows_for_lrouter(
> +    const struct ovn_datapath *od, struct lflow_table *lflows,
> +    struct ds *match, struct ds *actions,
> +    const struct shash *meter_groups,
> +    struct lflow_ref *lflow_ref);
>  
>  bool lflow_handle_northd_lr_changes(struct ovsdb_idl_txn *ovnsh_txn,
>                                       struct tracked_dps *,
> @@ -1042,6 +1076,11 @@ northd_has_lr_nats_in_tracked_data(struct 
> northd_tracked_data *trk_nd_changes)
>  {
>      return trk_nd_changes->type & NORTHD_TRACKED_LR_NATS;
>  }
> +static inline bool
> +northd_has_lr_route_in_tracked_data(struct northd_tracked_data 
> *trk_nd_changes)
> +{
> +    return trk_nd_changes->type & NORTHD_TRACKED_LR_ROUTES;
> +}
>  
>  static inline bool
>  northd_has_ls_lbs_in_tracked_data(struct northd_tracked_data *trk_nd_changes)
> diff --git a/tests/ovn-inc-proc-graph-dump.at 
> b/tests/ovn-inc-proc-graph-dump.at
> index 3750339d0..101f4c6f3 100644
> --- a/tests/ovn-inc-proc-graph-dump.at
> +++ b/tests/ovn-inc-proc-graph-dump.at
> @@ -151,16 +151,18 @@ digraph "Incremental-Processing-Engine" {
>       bfd [[style=filled, shape=box, fillcolor=white, label="bfd"]];
>       NB_bfd -> bfd [[label=""]];
>       SB_bfd -> bfd [[label=""]];
> +     NB_logical_router_static_route [[style=filled, shape=box, 
> fillcolor=white, label="NB_logical_router_static_route"]];
>       routes [[style=filled, shape=box, fillcolor=white, label="routes"]];
>       bfd -> routes [[label=""]];
>       northd -> routes [[label="routes_northd_change_handler"]];
> +     NB_logical_router_static_route -> routes 
> [[label="routes_static_route_change_handler"]];
>       route_policies [[style=filled, shape=box, fillcolor=white, 
> label="route_policies"]];
>       bfd -> route_policies [[label=""]];
>       northd -> route_policies 
> [[label="route_policies_northd_change_handler"]];
>       bfd_sync [[style=filled, shape=box, fillcolor=white, label="bfd_sync"]];
>       bfd -> bfd_sync [[label=""]];
>       NB_bfd -> bfd_sync [[label=""]];
> -     routes -> bfd_sync [[label=""]];
> +     routes -> bfd_sync [[label="bfd_sync_route_change_handler"]];
>       route_policies -> bfd_sync [[label=""]];
>       northd -> bfd_sync [[label="bfd_sync_northd_change_handler"]];
>       SB_learned_route [[style=filled, shape=box, fillcolor=white, 
> label="SB_learned_route"]];
> @@ -168,7 +170,7 @@ digraph "Incremental-Processing-Engine" {
>       SB_learned_route -> learned_route_sync 
> [[label="learned_route_sync_sb_learned_route_change_handler"]];
>       northd -> learned_route_sync 
> [[label="learned_route_sync_northd_change_handler"]];
>       group_ecmp_route [[style=filled, shape=box, fillcolor=white, 
> label="group_ecmp_route"]];
> -     routes -> group_ecmp_route [[label=""]];
> +     routes -> group_ecmp_route 
> [[label="group_ecmp_static_route_change_handler"]];
>       learned_route_sync -> group_ecmp_route 
> [[label="group_ecmp_route_learned_route_change_handler"]];
>       ls_stateful [[style=filled, shape=box, fillcolor=white, 
> label="ls_stateful"]];
>       northd -> ls_stateful [[label="ls_stateful_northd_handler"]];
> @@ -186,7 +188,7 @@ digraph "Incremental-Processing-Engine" {
>       SB_logical_dp_group -> lflow [[label=""]];
>       bfd_sync -> lflow [[label=""]];
>       route_policies -> lflow [[label=""]];
> -     routes -> lflow [[label=""]];
> +     routes -> lflow [[label="lflow_group_route_change_handler"]];
>       group_ecmp_route -> lflow 
> [[label="lflow_group_ecmp_route_change_handler"]];
>       global_config -> lflow [[label="node_global_config_handler"]];
>       sampling_app -> lflow [[label=""]];
> diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
> index 7f4a88d4e..7d3490384 100644
> --- a/tests/ovn-northd.at
> +++ b/tests/ovn-northd.at
> @@ -4383,7 +4383,7 @@ check ovn-nbctl --bfd=$uuid lr-route-add r0 100.0.0.0/8 
> 192.168.1.2
>  wait_column down bfd status logical_port=r0-sw1
>  AT_CHECK([ovn-nbctl lr-route-list r0 | grep 192.168.1.2 | grep -q bfd], [0], 
> [], [ignore])
>  
> -check_engine_stats northd recompute nocompute
> +check_engine_stats northd norecompute compute
>  check_engine_stats bfd recompute nocompute
>  check_engine_stats routes recompute nocompute
>  check_engine_stats lflow recompute nocompute
> @@ -4399,7 +4399,7 @@ check ovn-nbctl --bfd lr-route-add r0 240.0.0.0/8 
> 192.168.5.2 r0-sw5
>  wait_column down bfd status logical_port=r0-sw5
>  AT_CHECK([ovn-nbctl lr-route-list r0 | grep 192.168.5.2 | grep -q bfd], [0], 
> [], [ignore])
>  
> -check_engine_stats northd recompute nocompute
> +check_engine_stats northd norecompute compute
>  check_engine_stats bfd recompute nocompute
>  check_engine_stats routes recompute nocompute
>  check_engine_stats lflow recompute nocompute
> @@ -4411,7 +4411,7 @@ check ovn-nbctl --bfd --policy=src-ip lr-route-add r0 
> 192.168.6.1/32 192.168.10.
>  wait_column down bfd status logical_port=r0-sw6
>  AT_CHECK([ovn-nbctl lr-route-list r0 | grep 192.168.6.1 | grep -q bfd], [0], 
> [], [ignore])
>  
> -check_engine_stats northd recompute nocompute
> +check_engine_stats northd norecompute compute
>  check_engine_stats bfd recompute nocompute
>  check_engine_stats route_policies recompute nocompute
>  check_engine_stats lflow recompute nocompute
> @@ -4446,7 +4446,7 @@ wait_column down bfd status logical_port=r0-sw8
>  bfd_route_policy_uuid=$(fetch_column nb:bfd _uuid logical_port=r0-sw8)
>  AT_CHECK([ovn-nbctl list logical_router_policy | grep -q 
> $bfd_route_policy_uuid])
>  
> -check_engine_stats northd recompute nocompute
> +check_engine_stats northd recompute incremental
>  check_engine_stats bfd recompute nocompute
>  check_engine_stats routes recompute nocompute
>  check_engine_stats lflow recompute nocompute
> @@ -16847,12 +16847,12 @@ CHECK_NO_CHANGE_AFTER_RECOMPUTE
>  
>  check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
>  check ovn-nbctl --wait=sb lr-route-add lr0 192.168.0.0/24 10.0.0.10
> -check_engine_compute northd recompute
> -check_engine_compute routes recompute
> +check_engine_compute northd incremental
> +check_engine_compute routes incremental
>  check_engine_compute advertised_route_sync recompute
> -check_engine_compute learned_route_sync recompute
> -check_engine_compute group_ecmp_route recompute
> -check_engine_compute lflow recompute
> +check_engine_compute learned_route_sync incremental
> +check_engine_compute group_ecmp_route incremental
> +check_engine_compute lflow incremental
>  CHECK_NO_CHANGE_AFTER_RECOMPUTE
>  
>  check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
> @@ -20962,3 +20962,118 @@ check_column "$global_svc_mon_mac" 
> sb:Service_Monitor src_mac port=2
>  OVN_CLEANUP_NORTHD
>  AT_CLEANUP
>  ])
> +
> +OVN_FOR_EACH_NORTHD_NO_HV([
> +AT_SETUP([Static Route incremental processing])
> +ovn_start
> +
> +check ovn-nbctl lr-add r0
> +
> +check ovn-nbctl --wait=sb lrp-add r0 r0-lrp1 00:00:00:00:00:01 
> 192.168.1.1/24 fe80::1/64
> +
> +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
> +check ovn-nbctl --wait=sb lr-route-add r0 10.0.0.0/24 192.168.1.2
> +
> +check_engine_compute northd incremental
> +check_engine_compute routes incremental
> +check_engine_compute bfd_sync incremental
> +check_engine_compute group_ecmp_route incremental
> +check_engine_compute lflow incremental
> +
> +static_route_uuid=`ovn-nbctl --bare --columns _uuid find 
> Logical_Router_Static_Route nexthop=192.168.1.2`
> +AT_CHECK([test "$static_route_uuid" != ""])
> +
> +sr_lflow_uuid=$(fetch_column Logical_flow _uuid match='"reg7 == 0 && ip4.dst 
> == 10.0.0.0/24"')
> +AT_CHECK([test "$sr_lflow_uuid" != ""])
> +
> +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
> +check ovn-nbctl --wait=sb set logical_router_static_route $static_route_uuid 
> nexthop=192.168.1.3
> +check_engine_compute northd incremental
> +check_engine_compute routes incremental
> +check_engine_compute bfd_sync incremental
> +check_engine_compute group_ecmp_route incremental
> +check_engine_compute lflow incremental
> +sr_lflow_uuid=$(fetch_column Logical_flow _uuid match='"reg7 == 0 && ip4.dst 
> == 10.0.0.0/24"')
> +AT_CHECK([test "$sr_lflow_uuid" != ""])
> +
> +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
> +check ovn-nbctl --wait=sb set logical_router_static_route $static_route_uuid 
> ip_prefix=10.0.1.0/24
> +check_engine_compute northd incremental
> +check_engine_compute routes incremental
> +check_engine_compute bfd_sync incremental
> +check_engine_compute group_ecmp_route incremental
> +check_engine_compute lflow incremental
> +sr_lflow_uuid_old=$(fetch_column Logical_flow _uuid match='"reg7 == 0 && 
> ip4.dst == 10.0.0.0/24"')
> +AT_CHECK([test "$sr_lflow_uuid_old" == ""])
> +
> +sr_lflow_uuid=$(fetch_column Logical_flow _uuid match='"reg7 == 0 && ip4.dst 
> == 10.0.1.0/24"')
> +AT_CHECK([test "$sr_lflow_uuid" != ""])
> +
> +check ovn-nbctl --wait=sb lrp-add r0 r0-lrp2 00:00:00:00:00:02 
> 192.168.1.10/24
> +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
> +check ovn-nbctl --wait=sb set logical_router_static_route $static_route_uuid 
> output_port=r0-lrp2
> +check_engine_compute northd incremental
> +check_engine_compute routes incremental
> +check_engine_compute bfd_sync incremental
> +check_engine_compute group_ecmp_route incremental
> +check_engine_compute lflow incremental
> +sr_lflow_uuid=$(fetch_column Logical_flow _uuid match='"reg7 == 0 && ip4.dst 
> == 10.0.1.0/24"')
> +AT_CHECK([test "$sr_lflow_uuid" != ""])
> +
> +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
> +check ovn-nbctl --wait=sb set logical_router_static_route $static_route_uuid 
> policy=src-ip
> +check_engine_compute northd incremental
> +check_engine_compute routes incremental
> +check_engine_compute bfd_sync incremental
> +check_engine_compute group_ecmp_route incremental
> +check_engine_compute lflow incremental
> +sr_lflow_uuid=$(fetch_column Logical_flow _uuid match='"reg7 == 0 && ip4.src 
> == 10.0.1.0/24"')
> +AT_CHECK([test "$sr_lflow_uuid" != ""])
> +
> +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
> +check ovn-nbctl --wait=sb set logical_router_static_route $static_route_uuid 
> policy=dst-ip
> +check ovn-nbctl --wait=sb set logical_router_static_route $static_route_uuid 
> selection_fields="ip_proto,ip_src,ip_dst"
> +check_engine_compute northd incremental
> +check_engine_compute routes incremental
> +check_engine_compute bfd_sync incremental
> +check_engine_compute group_ecmp_route incremental
> +check_engine_compute lflow incremental
> +sr_lflow_uuid=$(fetch_column Logical_flow _uuid match='"reg7 == 0 && ip4.dst 
> == 10.0.1.0/24"')
> +AT_CHECK([test "$sr_lflow_uuid" != ""])
> +
> +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
> +check ovn-nbctl --wait=sb set logical_router_static_route $static_route_uuid 
> options:ecmp_symmetric_reply=true
> +check_engine_compute northd incremental
> +check_engine_compute routes incremental
> +check_engine_compute bfd_sync incremental
> +check_engine_compute group_ecmp_route incremental
> +check_engine_compute lflow incremental
> +sr_lflow_uuid=$(fetch_column Logical_flow _uuid match='"reg7 == 0 && ip4.dst 
> == 10.0.1.0/24"')
> +AT_CHECK([test "$sr_lflow_uuid" != ""])
> +
> +
> +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
> +check ovn-nbctl --wait=sb set logical_router_static_route $static_route_uuid 
> nexthop=\"fe80::2\"
> +check_engine_compute northd incremental
> +check_engine_compute routes incremental
> +check_engine_compute bfd_sync incremental
> +check_engine_compute group_ecmp_route incremental
> +check_engine_compute lflow incremental
> +sr_lflow_uuid=$(fetch_column Logical_flow _uuid match='"reg7 == 0 && ip4.dst 
> == 10.0.1.0/24"')
> +AT_CHECK([test "$sr_lflow_uuid" != ""])
> +sr_lflow_uuid_nexthop_ipv6=$(fetch_column Logical_flow _uuid match='"eth.dst 
> == 00:00:00:00:00:00 && reg9[[9]] == 0 && xxreg0 == fe80::2"')
> +AT_CHECK([test "$sr_lflow_uuid_nexthop_ipv6" != ""])
> +
> +check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
> +check ovn-nbctl remove logical_router r0 static_routes $static_route_uuid
> +check_engine_compute northd incremental
> +check_engine_compute routes incremental
> +check_engine_compute bfd_sync incremental
> +check_engine_compute group_ecmp_route incremental
> +check_engine_compute lflow incremental
> +sr_lflow_uuid=$(fetch_column Logical_flow _uuid match='"reg7 == 0 && ip4.src 
> == 10.0.1.0/24"')
> +AT_CHECK([test "$sr_lflow_uuid" == ""])
> +

Maybe we should also call CHECK_NO_CHANGE_AFTER_RECOMPUTE here.

> +OVN_CLEANUP_NORTHD
> +AT_CLEANUP
> +])

Regards,
Dumitru

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

Reply via email to