On Thu, Oct 15, 2026 at 10:35 AM Jacob Tanenbaum <[email protected]> wrote:
>
>
> I looked into this and there is no issue with waiting for a local_datapath to 
> open this gate. If there are no local RE datapaths there is nothing for 
> route_exchange_run do
>
> on the install side - no local datapaths means no VRFs created, no 
> re_nl_sync_routes() calls, no Learned_Route writes
> on the cleanup side - loops iterate on 
> _maintained_route_tables/_mainted_vrfs, which are re-initialized empty at 
> every start so no cleanup
>
>
> There was some confusion as to whether we needed to clean up routes that were 
> once local to this machine and that functionality did not exist in the old 
> code or in this patch.
> We only cleanup route_tables/vrfs that the current running ovn-controller 
> process has seen.

Thanks for the explanation Jacob. Since this is fixing the problem and
not introducing any regressions,

Acked-by: Mark Michelson <[email protected]>

>
> On Wed, Oct 14, 2026 at 9:32 AM Jacob Tanenbaum <[email protected]> wrote:
>>
>>
>>
>> On Tue, Oct 13, 2026 at 3:09 PM Mark Michelson <[email protected]> wrote:
>>>
>>> On Sun, Oct 11, 2026 at 8:48 PM Jacob Tanenbaum <[email protected]> wrote:
>>> >
>>> >
>>> >
>>> > On Thu, Oct 8, 2026 at 3:42 PM Mark Michelson <[email protected]> wrote:
>>> >>
>>> >> Hi Jacob, I had a look at this, and I think I get the gist of what's
>>> >> happening. I have a question below.
>>> >>
>>> >> On Fri, Oct 2, 2026 at 9:13 AM Jacob Tanenbaum via dev
>>> >> <[email protected]> wrote:
>>> >> >
>>> >> > On restart, ovn-controller monitored the whole cluster's
>>> >> > Advertised_Route table at startup, and only ran its kernel route
>>> >> > sync once all of it had been read.  The whole-cluster pull added
>>> >> > measurable startup latency in large deployments, and the guard was
>>> >> > coarser than it needed to be: the controller only ever deletes
>>> >> > routes that it installed itself, and it only needs the advertised
>>> >> > routes of the local route-exchange datapaths to know which ones
>>> >> > those are.
>>> >> >
>>> >> > Monitor no Advertised_Route rows at startup.  Once local datapaths
>>> >> > are known the monitor condition is already scoped to them, so the
>>> >> > dump the controller waits for is small.
>>> >> >
>>> >> > Defer the route-exchange kernel sync until the controller can be
>>> >> > confident that the Advertised_Route view is complete: the
>>> >> > condition seqno shows the dump was delivered, and the current
>>> >> > condition either covers the local route-exchange datapaths, or no
>>> >> > local route-exchange datapaths remain and the initial sync has
>>> >> > already run.  The kernel routes persist across the deferral, so
>>> >> > none are lost.  The datapath set changing or a southbound
>>> >> > reconnect re-arms the gate.
>>> >> >
>>> >> > The condition covering the local datapaths is what keeps this
>>> >> > safe.  Waiting only on the condition seqno could arm the sync
>>> >> > against the initial empty condition, and the first sync would
>>> >> > then delete the routes installed before the restart.
>>> >> >
>>> >> > With no local route-exchange datapaths there is no scoped dump to
>>> >> > wait for, so once the initial sync has run the view is complete
>>> >> > and the sync may run to clean up the VRFs and routes of the
>>> >> > removed datapaths.  Deferring it instead would leak the VRF and
>>> >> > routes of the last removed dynamic-routing router.
>>> >> >
>>> >> > The dynamic-routing system test now checks that the deferred sync
>>> >> > is enabled after a restart and that the installed OVN routes
>>> >> > survive a --restart of the controller.
>>> >> >
>>> >> > Reported-at: https://redhat.atlassian.net/browse/FDP-3992
>>> >> > Assisted-by: Qwen3.8-27B-FP8, opencode
>>> >> > Signed-off-by: Jacob Tanenbaum <[email protected]>
>>> >> >
>>> >> > ---
>>> >> >   v2:
>>> >> >     * Fixed issues with the sb_monitor_all case
>>> >> >     * Fixed the update to ovn-inc-proc-graph-dump.at
>>> >> >     * Allow the route sync to run once the local route-exchange
>>> >> >       datapath set becomes empty, so the VRF and routes of the
>>> >> >       last removed dynamic-routing router are cleaned up.
>>> >> > ---
>>> >> >  NEWS                             |   4 +
>>> >> >  TODO.rst                         |   5 --
>>> >> >  controller/ovn-controller.c      | 126 +++++++++++++++++++++++++++++--
>>> >> >  tests/ovn-inc-proc-graph-dump.at |   3 +-
>>> >> >  tests/system-ovn.at              |  11 ++-
>>> >> >  5 files changed, 137 insertions(+), 12 deletions(-)
>>> >> >
>>> >> > diff --git a/NEWS b/NEWS
>>> >> > index 7f94d0b14..207c5a03c 100644
>>> >> > --- a/NEWS
>>> >> > +++ b/NEWS
>>> >> > @@ -19,6 +19,10 @@ Post v26.09.0
>>> >> >     - Removed OVN's ovs-bugtool plugin and helper scripts.
>>> >> >     - Removed ovn-sim utility scripts.
>>> >> >     - Removed ovn-docker utility scripts.
>>> >> > +   - ovn-controller no longer monitors the whole cluster's 
>>> >> > Advertised_Route
>>> >> > +     table at startup; it defers its kernel route updates until the 
>>> >> > initial
>>> >> > +     dump of the advertised routes of the local route-exchange 
>>> >> > datapaths is
>>> >> > +     complete.
>>> >> >
>>> >> >  OVN v26.09.0 - xxx xx xxxx
>>> >> >  --------------------------
>>> >> > diff --git a/TODO.rst b/TODO.rst
>>> >> > index 023eb27f6..dc55c8895 100644
>>> >> > --- a/TODO.rst
>>> >> > +++ b/TODO.rst
>>> >> > @@ -145,11 +145,6 @@ OVN To-do List
>>> >> >    * Add incremental processing of en_dynamic_routes for stateful 
>>> >> > configuration
>>> >> >      changes.
>>> >> >
>>> >> > -  * The ovn-controller currently loads all Advertised_Route entries 
>>> >> > on startup.
>>> >> > -    This is to prevent deleting our routes on restart. If we defer 
>>> >> > updating
>>> >> > -    routes until we are sure to have loaded all necessary 
>>> >> > Advertised_Routes
>>> >> > -    this could be changed.
>>> >> > -
>>> >> >    * Improve handling of the Learned_Route table in ovn-controller 
>>> >> > conditional
>>> >> >      monitoring; once a new local datapath is added we need to wait for
>>> >> >      monitoring conditions to update before we actually try to learn 
>>> >> > routes.
>>> >> > diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c
>>> >> > index d57ff316d..f5ee8b75a 100644
>>> >> > --- a/controller/ovn-controller.c
>>> >> > +++ b/controller/ovn-controller.c
>>> >> > @@ -176,6 +176,16 @@ struct controller_engine_ctx {
>>> >> >      struct if_status_mgr *if_mgr;
>>> >> >      const unsigned int *ovnsb_expected_cond_seqno;
>>> >> >      const bool *sb_monitor_all;
>>> >> > +    /* True once every table that the current monitor conditions 
>>> >> > request has
>>> >> > +     * been fully received (the SB condition seqno has caught up).  
>>> >> > The
>>> >> > +     * route-exchange node uses this to defer its destructive kernel 
>>> >> > sync
>>> >> > +     * until the data it relies on is complete. */
>>> >> > +    bool sb_all_data_loaded;
>>> >> > +    /* True once the Advertised_Route monitor condition covers the 
>>> >> > local
>>> >> > +     * route-exchange datapaths (or, in monitor-all mode, 
>>> >> > everything).  It is
>>> >> > +     * false for the initial empty condition that is used before any 
>>> >> > local
>>> >> > +     * datapath is known. */
>>> >> > +    bool sb_ar_condition_scoped;
>>> >> >  };
>>> >> >
>>> >> >  /* Pending packet to be injected into connected OVS. */
>>> >> > @@ -360,11 +370,13 @@ update_sb_monitors(struct ovsdb_idl *ovnsb_idl,
>>> >> >          sbrec_port_binding_add_clause_type(&pb, OVSDB_F_EQ, 
>>> >> > "l2gateway");
>>> >> >          sbrec_port_binding_add_clause_type(&pb, OVSDB_F_EQ, 
>>> >> > "l3gateway");
>>> >> >
>>> >> > -        /* Monitor all advertised routes during startup.
>>> >> > -         * Otherwise, once we claim a port on startup we do not yet 
>>> >> > know the
>>> >> > -         * routes to advertise and might wrongly delete already 
>>> >> > installed
>>> >> > -         * ones. */
>>> >> > -        ovsdb_idl_condition_add_clause_true(&ar);
>>> >> > +        /* Do not monitor any Advertised_Route rows during startup. 
>>> >> > The
>>> >> > +         * route-exchange node defers its destructive kernel sync 
>>> >> > until it has
>>> >> > +         * seen the initial dump of the advertised routes of the local
>>> >> > +         * route-exchange datapaths, so there is no risk of deleting 
>>> >> > routes
>>> >> > +         * we installed previously. Once local datapaths are known the
>>> >> > +         * condition is scoped to them (below), which avoids pulling 
>>> >> > the whole
>>> >> > +         * cluster's advertised routes on startup. */
>>> >> >      }
>>> >> >      if (local_ifaces) {
>>> >> >          const char *name;
>>> >> > @@ -773,6 +785,11 @@ update_sb_db(struct ovsdb_idl *ovs_idl, struct 
>>> >> > ovsdb_idl *ovnsb_idl,
>>> >> >          if (sb_cond_seqno) {
>>> >> >              *sb_cond_seqno = next_cond_seqno;
>>> >> >          }
>>> >> > +        /* In monitor-all mode the Advertised_Route condition matches
>>> >> > +         * everything, so it already covers the local datapaths. */
>>> >> > +        if (ctx) {
>>> >> > +            ctx->sb_ar_condition_scoped = true;
>>> >> > +        }
>>> >> >      }
>>> >> >      if (monitor_all_p) {
>>> >> >          *monitor_all_p = monitor_all;
>>> >> > @@ -5739,6 +5756,22 @@ struct ed_type_route_exchange {
>>> >> >      bool sb_changes_pending;
>>> >> >      /* What the last run learned from the kernel routing tables. */
>>> >> >      struct route_exchange_state *state;
>>> >> > +    /* True once the initial Advertised_Route dump for the local
>>> >> > +     * route-exchange datapaths is complete.  Until then the 
>>> >> > destructive
>>> >> > +     * kernel sync is deferred so that we cannot delete routes that we
>>> >> > +     * installed previously but that are not yet present in our 
>>> >> > (partial)
>>> >> > +     * view of the SB database. */
>>> >> > +    bool initial_routes_loaded;
>>> >> > +    /* True once the first armed kernel sync has run.  Unlike
>>> >> > +     * initial_routes_loaded, it is not cleared when the local
>>> >> > +     * route-exchange datapath set changes or on SB reconnect, so the
>>> >> > +     * sync can still run (to clean up VRFs and routes) once the set
>>> >> > +     * becomes empty.  The start-up danger window in which an empty
>>> >> > +     * Advertised_Route view would delete routes installed before the
>>> >> > +     * restart is guarded by sb_all_data_loaded, which is false both
>>> >> > +     * during the start-up bootstrap and while the Advertised_Route
>>> >> > +     * table is reloaded after an SB reconnect. */
>>> >> > +    bool initial_sync_completed;
>>> >> >  };
>>> >> >
>>> >> >  static void
>>> >> > @@ -5789,6 +5822,51 @@ en_route_exchange_run(struct engine_node *node, 
>>> >> > void *data)
>>> >> >          return EN_STALE;
>>> >> >      }
>>> >> >
>>> >> > +    struct ed_type_route *route_data =
>>> >> > +        engine_get_input_data("route", node);
>>> >> > +    struct controller_engine_ctx *ctrl_ctx =
>>> >> > +        engine_get_context()->client_ctx;
>>> >> > +
>>> >> > +    /* The set of local route-exchange datapaths selects the 
>>> >> > Advertised_Route
>>> >> > +     * rows that are relevant to this chassis.  Until the initial 
>>> >> > dump of
>>> >> > +     * exactly those rows is complete, we must not run the 
>>> >> > destructive kernel
>>> >> > +     * sync: it deletes every OVN route it does not see, so a partial 
>>> >> > view
>>> >> > +     * would delete routes we installed before a restart that have not
>>> >> > +     * reached our view yet.  With no local route-exchange datapaths 
>>> >> > there is
>>> >> > +     * no scoped dump to wait for, so once the first armed sync has 
>>> >> > run the
>>> >> > +     * view is complete and the sync may run to clean up the VRFs and 
>>> >> > routes
>>> >> > +     * of the removed datapaths. */
>>> >> > +    bool have_local_re_datapaths =
>>> >> > +        !hmap_is_empty(&route_data->announce_routes);
>>> >> > +    bool ready = ctrl_ctx->sb_all_data_loaded
>>> >> > +                 && (have_local_re_datapaths
>>> >> > +                     ? ctrl_ctx->sb_ar_condition_scoped
>>> >>
>>> >> I might be missing something, but I don't understand the need for the
>>> >> sb_ar_condition_scoped boolean.
>>> >>
>>> >> Here, if route_data has announce_routes, then we determine readiness
>>> >> by checking if sb_ar_condition_scoped is true. sb_ar_condition_scoped
>>> >> will be true under two circumstances:
>>> >> 1) monitor_all is enabled.
>>> >> 2) There are local_datapaths in runtime_data.
>>> >>
>>> >> For (1), we can, for all intents and purposes, think of
>>> >> sb_ar_condition_scoped as being always true.
>>> >> For (2), in order for route_data to have announce_routes, it had to
>>> >> set those based on local_datapaths from the runtime_data.
>>> >>
>>> >> I did notice that sb_ar_condition_scoped can be set to false if the
>>> >> IDL disconnects from the OVSDB server. We set the var false, then on
>>> >> the next engine run, we'll potentially set it back to true for one of
>>> >> the two reasons listed above after the engine run is complete. This
>>> >> means that if the IDL disconnects, then we can have one of two
>>> >> situations occur during the first engine run after reconnecting:
>>> >>
>>> >> 1) The recompute results in no route_data->announce_routes.
>>> >> 2) The recompute results in route_data->announce_routes being true.
>>> >>
>>> >> In case (1), we may determine ourselves ready since
>>> >> re->initial_sync_completed may have been set true on a previous call
>>> >> to en_route_exchange_run(). In case (2), we'll always see ourselves as
>>> >> unready since sb_ar_condition_scoped was set false before the engine
>>> >> run.
>>> >>
>>> >> To me this seems strange. If the main issue is about startup latency,
>>> >> then why would we declare ourselves unready in the situation where we
>>> >> disconnect from the OVSDB server, and the next engine run after
>>> >> reconnecting results in announce_routes being present? Moreover, why
>>> >> would we consider ourselves ready if there are no announce_routes?
>>> >>
>>> >> IMO, I think the ready boolean should be set as:
>>> >>
>>> >> bool ready = ctrl_ctx->sb_all_data_loaded && (have_local_re_datapaths
>>> >> || re->initial_sync_completed);
>>> >>
>>> >> This way, we won't run if the OVSDB monitor conditions are out of
>>> >> whack. On startup, we'll run as soon as announce_routes are detected
>>> >> as present for the first time. Then on any IDL disconnects, since we
>>> >> finished an initial sync, we'll declare ourselves ready every time
>>> >> from then on. This would allow us to get rid of the
>>> >> sb_ar_condition_scoped field altogether.
>>> >>
>>> >> What do you think?
>>> >
>>> >
>>> > I think we need sb_ar_condition_scoped.
>>> >
>>> > sb_all_data_loaded represents if the dump of the current sb condition is
>>> > complete while sb_ar_condition_scoped is if the current condition is
>>> > one that actually selects the local datapaths route exchange
>>> > rows.
>>> >
>>> > At startup we initially add no monitor for advertised routes and build up 
>>> > the advertised route condition
>>> > per local datapath on runs of update_sb_monitors().  So it is possible 
>>> > that all the data for the current
>>> > condition is loaded but none of the advertised routes are because we have 
>>> > no condition for them. The
>>> > issue is ordering, On the iteration where a local route_exchange datapath 
>>> > first appears in the engine's view,
>>> > the engine already sees announce_routes non-empty, but the IDL's 
>>> > effective AR condition is still the last
>>> > iterations, the empty one, so sb_ar_condition_scoped is still false. Then 
>>> > update_sb_monitors() runs after the
>>> > engine_run() which updates sb_ar_condition_scoped and we can be sure we 
>>> > have all the needed data in the next
>>> > iteration. Does that make sense?
>>>
>>> OK, I get it now. I didn't realize that update_sb_monitors() was
>>> dynamically updating what we were monitoring. The idea is that on
>>> engine_run 1, a SB datapath may get classified as "local". However, on
>>> this first run, we are not monitoring the Advertised_Route table for
>>> routes pertaining to this datapath. After the engine run completes,
>>> update_sb_monitors() adds a monitor for Advertised_Routes for that
>>> particular datapath so that the next IDL run can potentially pull in
>>> the Advertised_Routes for that datapath. This way, on engine_run 2, we
>>> know that the Advertised_Routes are being monitored for this datapath.
>>> The sb_ar_condition_scoped variable gets set true when we have local
>>> datapaths, and therefore we are now known to be monitoring the
>>> Advertised_Route table in some capacity. The idea is that if any rows
>>> of Advertised_Route are being monitored at all, then it is safe for
>>> en_route_exchange_run() to continue.
>>>
>>> This makes a lot more sense now, and it also now has made me think of
>>> a different potential issue we could see. What happens if we restart
>>> and there are no local datapaths for quite a long time? We could be
>>> put in a position where we are keeping dead routes around that should
>>> be deleted. Should there be some sort of failsafe added so that if
>>> some period of time passes after startup with no local_datapaths, we
>>> can assume that our routes are dead and we should delete them?
>>
>>
>> I hadn't considered that; let me investigate and get back to you.
>>
>>>
>>>
>>> >
>>> >
>>> >>
>>> >>
>>> >> > +                     : re->initial_sync_completed);
>>> >> > +
>>> >> > +    if (re->initial_routes_loaded && !ready) {
>>> >> > +        /* The set of local route-exchange datapaths changed or the SB
>>> >> > +         * connection reset; defer the destructive sync again until 
>>> >> > the new
>>> >> > +         * scoped dump is complete. */
>>> >> > +        re->initial_routes_loaded = false;
>>> >> > +        VLOG_INFO("Route-exchange datapath set changed; deferring 
>>> >> > route "
>>> >> > +                  "sync until the Advertised_Route dump completes.");
>>> >> > +        return EN_UNCHANGED;
>>> >> > +    }
>>> >> > +    if (!re->initial_routes_loaded) {
>>> >> > +        if (!ready) {
>>> >> > +            return EN_UNCHANGED;   /* still loading; kernel untouched 
>>> >> > */
>>> >> > +        }
>>> >> > +        re->initial_routes_loaded = true;
>>> >> > +        /* Past the start-up danger window from now on: the view has 
>>> >> > been
>>> >> > +         * complete enough to run the destructive sync once, so a 
>>> >> > later
>>> >> > +         * transition to an empty local route-exchange datapath set 
>>> >> > (the
>>> >> > +         * last dynamic-routing router removed) may run it to clean 
>>> >> > up. */
>>> >> > +        re->initial_sync_completed = true;
>>> >> > +        VLOG_INFO("Advertised_Route dump complete for local 
>>> >> > route-exchange "
>>> >> > +                  "datapaths; enabling route sync.");
>>> >> > +        /* Fall through: run once now to reconcile the kernel. */
>>> >> > +    }
>>> >> > +
>>> >> >      vector_clear(&rt_notify->watches);
>>> >> >
>>> >> >      struct route_exchange_ctx_in r_ctx_in;
>>> >> > @@ -5811,6 +5889,13 @@ route_exchange_route_table_handler(struct 
>>> >> > engine_node *node, void *data)
>>> >> >      struct ed_type_route_table_notify *rt_notify =
>>> >> >          engine_get_input_data("route_table_notify", node);
>>> >> >
>>> >> > +    /* While the initial dump is still pending the full run has not 
>>> >> > happened
>>> >> > +     * yet, so no kernel route watches are registered and nothing was 
>>> >> > lost.
>>> >> > +     * The first armed run reconciles the kernel from scratch. */
>>> >> > +    if (!re->initial_routes_loaded) {
>>> >> > +        return EN_HANDLED_UNCHANGED;
>>> >> > +    }
>>> >> > +
>>> >> >      /* We were not told about every change, so the routes we know of 
>>> >> > are not
>>> >> >       * necessarily the ones the kernel has. */
>>> >> >      if (rt_notify->resync) {
>>> >> > @@ -7394,6 +7479,10 @@ inc_proc_ovn_controller_init(
>>> >> >      engine_add_input(&en_route_exchange, &en_route_exchange_status, 
>>> >> > NULL);
>>> >> >      engine_add_input(&en_route_exchange, &en_sb_ro,
>>> >> >                       route_exchange_sb_ro_handler);
>>> >> > +    /* Re-evaluate the initial-dump readiness gate whenever the SB 
>>> >> > monitor
>>> >> > +     * conditions change (including the final ack of the scoped
>>> >> > +     * Advertised_Route dump). */
>>> >> > +    engine_add_input(&en_route_exchange, &en_sb_cond_seqno, NULL);
>>> >> >
>>> >> >      engine_add_input(&en_addr_sets, &en_sb_address_set,
>>> >> >                       addr_sets_sb_address_set_handler);
>>> >> > @@ -8171,6 +8260,8 @@ main(int argc, char *argv[])
>>> >> >          .if_mgr = if_status_mgr_create(),
>>> >> >          .ovnsb_expected_cond_seqno = &ovnsb_expected_cond_seqno,
>>> >> >          .sb_monitor_all = &sb_monitor_all,
>>> >> > +        .sb_all_data_loaded = false,
>>> >> > +        .sb_ar_condition_scoped = false,
>>> >> >      };
>>> >> >      struct if_status_mgr *if_mgr = ctrl_engine_ctx.if_mgr;
>>> >> >
>>> >> > @@ -8240,6 +8331,10 @@ main(int argc, char *argv[])
>>> >> >              if (!new_ovnsb_cond_seqno) {
>>> >> >                  VLOG_INFO("OVNSB IDL reconnected, force recompute.");
>>> >> >                  engine_set_force_recompute();
>>> >> > +                /* The monitor conditions are re-applied on 
>>> >> > reconnect, so the
>>> >> > +                 * previously scoped Advertised_Route condition no 
>>> >> > longer
>>> >> > +                 * holds. */
>>> >> > +                ctrl_engine_ctx.sb_ar_condition_scoped = false;
>>> >> >              }
>>> >> >              ovnsb_cond_seqno = new_ovnsb_cond_seqno;
>>> >> >          }
>>> >> > @@ -8254,6 +8349,18 @@ main(int argc, char *argv[])
>>> >> >              daemon_started_recently_ignore();
>>> >> >          }
>>> >> >
>>> >> > +        /* Whether the rows selected by the current monitor 
>>> >> > conditions are all
>>> >> > +         * present in the IDL.  This is recomputed every loop 
>>> >> > iteration so
>>> >> > +         * that engine nodes can rely on it being up to date for the
>>> >> > +         * conditions that were applied so far.
>>> >> > +         * If we have sb_monitor_all that means we have all data that 
>>> >> > we would
>>> >> > +         * ever need. */
>>> >> > +        ctrl_engine_ctx.sb_all_data_loaded =
>>> >> > +            sb_monitor_all ||
>>> >> > +            (ovnsb_cond_seqno == ovnsb_expected_cond_seqno
>>> >> > +             && ovnsb_expected_cond_seqno != UINT_MAX
>>> >> > +             && ovnsb_cond_seqno != 0);
>>> >> > +
>>> >> >          struct engine_context eng_ctx = {
>>> >> >              .ovs_idl_txn = ovs_idl_txn,
>>> >> >              .ovnsb_idl_txn = ovnsb_idl_txn,
>>> >> > @@ -8574,6 +8681,15 @@ main(int argc, char *argv[])
>>> >> >                                      
>>> >> > &runtime_data->lbinding_data.bindings,
>>> >> >                                      &runtime_data->local_datapaths,
>>> >> >                                      sb_monitor_all);
>>> >> > +                            /* The Advertised_Route condition now 
>>> >> > covers the
>>> >> > +                             * local datapaths (or everything, in
>>> >> > +                             * monitor-all mode).  If there are no 
>>> >> > local
>>> >> > +                             * datapaths yet, there is nothing to 
>>> >> > cover and
>>> >> > +                             * the empty condition is sufficient. */
>>> >> > +                            ctrl_engine_ctx.sb_ar_condition_scoped =
>>> >> > +                                sb_monitor_all ||
>>> >> > +                                !hmap_is_empty(
>>> >> > +                                    &runtime_data->local_datapaths);
>>> >> >                              bool condition_changed = ovnsb_cond_seqno 
>>> >> > !=
>>> >> >                                                       
>>> >> > ovnsb_expected_cond_seqno;
>>> >> >                              if (had_all_data && condition_changed) {
>>> >> > diff --git a/tests/ovn-inc-proc-graph-dump.at 
>>> >> > b/tests/ovn-inc-proc-graph-dump.at
>>> >> > index 8348c3467..a388f3d25 100644
>>> >> > --- a/tests/ovn-inc-proc-graph-dump.at
>>> >> > +++ b/tests/ovn-inc-proc-graph-dump.at
>>> >> > @@ -469,6 +469,7 @@ digraph "Incremental-Processing-Engine" {
>>> >> >         SB_learned_route [[style=filled, shape=box, fillcolor=white, 
>>> >> > label="SB_learned_route"]];
>>> >> >         route_table_notify [[style=filled, shape=box, fillcolor=white, 
>>> >> > label="route_table_notify"]];
>>> >> >         route_exchange_status [[style=filled, shape=box, 
>>> >> > fillcolor=white, label="route_exchange_status"]];
>>> >> > +       sb_cond_seqno [[style=filled, shape=box, fillcolor=white, 
>>> >> > label="sb_cond_seqno"]];
>>> >> >         route_exchange [[style=filled, shape=box, fillcolor=white, 
>>> >> > label="route_exchange"]];
>>> >> >         OVS_open_vswitch -> route_exchange [[label=""]];
>>> >> >         SB_chassis -> route_exchange [[label=""]];
>>> >> > @@ -478,6 +479,7 @@ digraph "Incremental-Processing-Engine" {
>>> >> >         route_table_notify -> route_exchange 
>>> >> > [[label="route_exchange_route_table_handler"]];
>>> >> >         route_exchange_status -> route_exchange [[label=""]];
>>> >> >         sb_ro -> route_exchange 
>>> >> > [[label="route_exchange_sb_ro_handler"]];
>>> >> > +       sb_cond_seqno -> route_exchange [[label=""]];
>>> >> >         garp_rarp [[style=filled, shape=box, fillcolor=white, 
>>> >> > label="garp_rarp"]];
>>> >> >         OVS_open_vswitch -> garp_rarp [[label=""]];
>>> >> >         SB_chassis -> garp_rarp [[label=""]];
>>> >> > @@ -497,7 +499,6 @@ digraph "Incremental-Processing-Engine" {
>>> >> >         SB_acl_id [[style=filled, shape=box, fillcolor=white, 
>>> >> > label="SB_acl_id"]];
>>> >> >         acl_id [[style=filled, shape=box, fillcolor=white, 
>>> >> > label="acl_id"]];
>>> >> >         SB_acl_id -> acl_id [[label=""]];
>>> >> > -       sb_cond_seqno [[style=filled, shape=box, fillcolor=white, 
>>> >> > label="sb_cond_seqno"]];
>>> >> >         datapaths_updated [[style=filled, shape=box, fillcolor=white, 
>>> >> > label="datapaths_updated"]];
>>> >> >         sb_cond_seqno -> datapaths_updated 
>>> >> > [[label="datapaths_update_sb_cond_handler"]];
>>> >> >         runtime_data -> datapaths_updated 
>>> >> > [[label="datapaths_updated_runtime_data_handler"]];
>>> >> > diff --git a/tests/system-ovn.at b/tests/system-ovn.at
>>> >> > index 13e62bf9b..e0f1c6f0d 100644
>>> >> > --- a/tests/system-ovn.at
>>> >> > +++ b/tests/system-ovn.at
>>> >> > @@ -15739,6 +15739,12 @@ blackhole 198.51.100.0/24 proto ovn metric 
>>> >> > 1000
>>> >> >  233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30 
>>> >> > onlink
>>> >> >  233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40 
>>> >> > onlink])
>>> >> >
>>> >> > +# Route exchange defers its kernel sync until the initial 
>>> >> > Advertised_Route
>>> >> > +# dump for the local route-exchange datapaths is complete. The routes 
>>> >> > above
>>> >> > +# can only be installed once that happens, so verify the deferred 
>>> >> > sync was
>>> >> > +# actually enabled.
>>> >> > +OVS_WAIT_UNTIL([test "$(grep -c 'Advertised_Route dump complete for 
>>> >> > local route-exchange datapaths' ovn-controller.log)" -ge 1])
>>> >> > +
>>> >> >  # Changing the vrf name will switch to the new one.
>>> >> >  # The old vrf will be removed.
>>> >> >  check ovn-nbctl --wait=hv set Logical_Router_Port internet-phys \
>>> >> > @@ -15757,7 +15763,10 @@ blackhole 198.51.100.0/24 proto ovn metric 
>>> >> > 1000
>>> >> >  233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 30 
>>> >> > onlink
>>> >> >  233.253.0.0/24 via 192.168.20.20 dev hv1-mll proto zebra metric 40 
>>> >> > onlink])
>>> >> >
>>> >> > -# Stopping with --restart will not touch the routes.
>>> >> > +# Stopping with --restart will not touch the routes. While the 
>>> >> > restarted
>>> >> > +# controller loads its Advertised_Routes the kernel sync is deferred, 
>>> >> > so a
>>> >> > +# controller that synced from a partial view would have deleted the 
>>> >> > OVN routes
>>> >> > +# below; verify they all survive the restart.
>>> >> >  OVN_CONTROLLER_EXIT([],[--restart])
>>> >> >  OVN_ROUTE_EQUAL([ovnvrf1338], [dnl
>>> >> >  blackhole 192.0.2.1 proto ovn metric 100
>>> >> > --
>>> >> > 2.55.0
>>> >> >
>>> >> > _______________________________________________
>>> >> > dev mailing list
>>> >> > [email protected]
>>> >> > https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>>> >> >
>>> >>
>>>

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

Reply via email to