Commit d2ddf7cfec for frr
commit d2ddf7cfece7c6a912f0ce45208c12d5e72477bb
Author: Chirag Shah <chirag@nvidia.com>
Date: Sat Nov 7 13:37:05 2026 -0800
tests: [EVPN] wait for the ES before the MAC move baseline
test_evpn_mac_move_to_es_nhg_inactive[ipv6] failed in move_baseline()
with "ES ... VTEPs on torm11 are [], expected [...]".
The test before it, test_evpn_uplink_tracking, drops torm11's two uplinks
and brings them back. That resets torm11's BGP sessions and withdraws
every ES it learned. zebra frees a remote ES once its EAD and MAC routes
are gone and rebuilds it only after bgpd re-advertises them, so for a
short window "show evpn es <esi>" is empty. The uplink test only waited
for the protodown reason to clear, which happens as soon as the links are
up, and move_baseline() then read the ES exactly once. IPv4 read it after
zebra had rebuilt it; IPv6 read it inside the window.
Fix both ends:
- move_baseline() polls check_move_es_nhg_active() instead of reading
once. It is the check stage 4 already uses: bgpd and zebra both list
every VTEP and the NHG is active. The nhg_active control case relies on
an active NHG from its first stage, so this also makes it start from the
state it claims to.
- test_evpn_uplink_tracking waits for check_es() after restoring the
uplinks, so the next test starts from a settled network and a failure
is reported against the test that caused it.
Signed-off-by: Chirag Shah <chirag@nvidia.com>
diff --git a/tests/topotests/bgp_evpn_mh_v4_v6_num/test_evpn_mh_v4_v6_num.py b/tests/topotests/bgp_evpn_mh_v4_v6_num/test_evpn_mh_v4_v6_num.py
index a192b66aff..4661cbc5ec 100644
--- a/tests/topotests/bgp_evpn_mh_v4_v6_num/test_evpn_mh_v4_v6_num.py
+++ b/tests/topotests/bgp_evpn_mh_v4_v6_num/test_evpn_mh_v4_v6_num.py
@@ -1474,6 +1474,17 @@ def test_evpn_uplink_tracking(tgen_and_ip_version):
assertmsg = '"{}" protodown rc incorrect'.format(dut_name)
assert result is None, assertmsg
+ # The uplink flap reset the BGP sessions, which withdrew every ES on the
+ # DUT. Protodown clears as soon as the links are up, long before the ESs
+ # are back, so wait for them here. Otherwise the next test starts from a
+ # half-built network and fails on a race this test caused.
+ test_fn = partial(check_es, dut)
+ _, result = topotest.run_and_expect(test_fn, None, count=30, wait=3)
+ assertmsg = '"{}" ES content incorrect after uplink restore. Result: {}'.format(
+ dut_name, result
+ )
+ assert result is None, assertmsg
+
#####################################################
##
@@ -1692,12 +1703,26 @@ def delete_mac_from_port(leaf, port):
def move_baseline(tgen):
"""
- Read-only: record the state the cleanup must restore, and check the test
- starts from a clean network (ES fully known on the observer, MAC nowhere).
+ Wait for the ES to be fully known on the observer, then record the state
+ the cleanup must restore and check the MAC is nowhere. Changes nothing.
"""
obs = tgen.gears[MOVE_OBSERVER]
leaves = [tgen.gears[MOVE_LEARN_LEAF], tgen.gears[MOVE_PEER_LEAF]]
esi = host_es_map[MOVE_ES_HOST]
+ expected_vteps = sorted(tor_ips[n] for n in (MOVE_LEARN_LEAF, MOVE_PEER_LEAF))
+
+ # Wait for the ES instead of reading it once. zebra frees a remote ES when
+ # its EAD and MAC routes are withdrawn and rebuilds it only after bgpd
+ # re-advertises them, so a session reset leaves a window where
+ # "show evpn es" is empty. The same check proves the NHG is active, which
+ # the nhg_active control case relies on from its first stage.
+ _, result = topotest.run_and_expect(
+ partial(check_move_es_nhg_active, obs, esi, expected_vteps),
+ None,
+ count=30,
+ wait=3,
+ )
+ assert result is None, "baseline: %s" % result
saved = {
"esi": esi,
@@ -1715,15 +1740,6 @@ def move_baseline(tgen):
"obs_es_vteps": evpn_lib.evpn_zebra_es_state(obs, esi)["vteps"],
}
- expected_vteps = sorted(tor_ips[n] for n in (MOVE_LEARN_LEAF, MOVE_PEER_LEAF))
- assert (
- saved["obs_es_vteps"] == expected_vteps
- ), "baseline: ES %s VTEPs on %s are %s, expected %s" % (
- esi,
- obs.name,
- saved["obs_es_vteps"],
- expected_vteps,
- )
for router in [obs] + leaves:
result = evpn_lib.evpn_check_mac_absent(router, MOVE_VNI, MOVE_MAC)
assert result is None, "baseline: %s" % result