On Fri, Oct 16, 2026 at 1:21 PM Mark Michelson <[email protected]> wrote: > > 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]>
I pushed the change to main. The change does not apply cleanly on branch-26.09 or branch-26.03. Can you post patches that will apply on those branches? > > > > > 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
