Skip to content

Commit 2138f00

Browse files
Fix route_check.py: intersection-based retry in check_frr_pending_routes
## What I did Replaced the overwrite-on-each-iteration retry loop with an intersection accumulator so that only routes stuck in *every* poll window are mitigated. ## Problem with the previous logic The retry loop called get_frr_routes_parallel() up to FRR_CHECK_RETRIES times and simply overwrote missed_rt on every iteration: for i in range(retries): missed_rt, failed_rt = get_frr_routes_parallel(namespace) if not missed_rt and not failed_rt: break time.sleep(FRR_WAIT_TIME) # mitigate whatever happened to be in missed_rt at loop exit Consequence: a route stuck only in the last iteration is mitigated even though it may be in the middle of normal BGP convergence (false positive / premature mitigation). ## New logic Accumulate a running intersection across iterations. On the first iteration the accumulator is seeded with the full result set. On each subsequent iteration, any prefix no longer present is removed. iter 0: {A, B, C, D} <- seed iter 1: {A, B, C} <- D removed (converged) iter 2: {A, B} <- C removed (converged) result: {A, B} <- truly stuck, safe to mitigate If the current iteration returns nothing, the intersection becomes empty and the loop exits early — same fast-path as before for the healthy case. Signed-off-by: mike-dubrovsky <mdubrovs@cisco.com>
1 parent 212bd03 commit 2138f00

1 file changed

Lines changed: 38 additions & 12 deletions

File tree

scripts/route_check.py

Lines changed: 38 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -730,20 +730,46 @@ def is_feature_bgp_enabled(namespace):
730730

731731
def check_frr_pending_routes(namespace):
732732
"""
733-
Check FRR routes for offload flag presence by executing "show ip route json"
734-
Returns a list of routes that have no offload flag.
735-
"""
736-
737-
missed_rt = []
738-
failed_rt = []
739-
retries = FRR_CHECK_RETRIES
740-
for i in range(retries):
741-
missed_rt, failed_rt = get_frr_routes_parallel(namespace)
742-
743-
if not missed_rt and not failed_rt:
733+
Check FRR routes for offload flag presence by streaming vtysh text output.
734+
Returns lists of route dicts {'prefix', 'protocol'} that are persistently
735+
non-offloaded across all retry iterations (intersection logic).
736+
737+
Intersection approach: only routes that appear as stuck in *every* poll
738+
are returned for mitigation. A route that clears between iterations is
739+
converging normally and should not be mitigated.
740+
741+
Example with FRR_CHECK_RETRIES=3:
742+
iter 0: {A, B, C, D}
743+
iter 1: {A, B, C} (D converged)
744+
iter 2: {A, B} (C converged)
745+
result: {A, B} <- only truly stuck routes
746+
"""
747+
acc_miss = []
748+
acc_fail = []
749+
750+
for i in range(FRR_CHECK_RETRIES):
751+
curr_miss, curr_fail = get_frr_routes_parallel(namespace)
752+
753+
if i == 0:
754+
# Seed: first iteration defines the candidate set
755+
acc_miss = curr_miss
756+
acc_fail = curr_fail
757+
else:
758+
# Intersect: keep only prefixes still stuck in this iteration
759+
curr_miss_keys = {e['prefix'] for e in curr_miss}
760+
curr_fail_keys = {e['prefix'] for e in curr_fail}
761+
acc_miss = [e for e in acc_miss if e['prefix'] in curr_miss_keys]
762+
acc_fail = [e for e in acc_fail if e['prefix'] in curr_fail_keys]
763+
764+
if not curr_miss and not curr_fail:
765+
# Nothing queued right now; intersection already empty -- stop early
744766
break
745767

746-
time.sleep(FRR_WAIT_TIME)
768+
if i < FRR_CHECK_RETRIES - 1:
769+
time.sleep(FRR_WAIT_TIME)
770+
771+
missed_rt = acc_miss
772+
failed_rt = acc_fail
747773
print_message(syslog.LOG_DEBUG, f"FRR missed routes: {missed_rt}")
748774
print_message(syslog.LOG_DEBUG, f"FRR failed routes: {failed_rt}")
749775
return missed_rt, failed_rt

0 commit comments

Comments
 (0)