Skip to content

Commit f27c9c5

Browse files
committed
tests: Add a test to ensure that the received NHG id works as expected
Add a simple test that does some basic exercising of the receivedNHGId and that it worked as expected. Signed-off-by: Donald Sharp <[email protected]>
1 parent 0ae9747 commit f27c9c5

File tree

2 files changed

+281
-0
lines changed

2 files changed

+281
-0
lines changed
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
!
2+
interface r1-eth0
3+
ip address 192.168.1.1/24
4+
!
5+
!
6+
ip route 10.0.0.0/32 192.168.1.1
7+
ip route 10.0.0.1/32 10.0.0.0
8+
ip route 10.0.0.2/32 10.0.0.0
9+
ip route 10.0.0.3/32 10.0.0.0
10+
ip route 10.0.0.4/32 10.0.0.0
11+
ip route 10.0.0.5/32 10.0.0.0
12+
!
13+
line vty
14+
!
Lines changed: 267 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,267 @@
1+
#!/usr/bin/env python
2+
# SPDX-License-Identifier: ISC
3+
4+
#
5+
# test_zebra_received_nhe_kept.py
6+
#
7+
# Copyright (c) 2025 by Donald Sharp, Nvidia Inc.
8+
#
9+
10+
"""
11+
test_zebra_received_nhe_kept.py: Test of Zebra Next Hop Entry Kept
12+
"""
13+
14+
import os
15+
import sys
16+
import pytest
17+
import json
18+
from functools import partial
19+
20+
CWD = os.path.dirname(os.path.realpath(__file__))
21+
sys.path.append(os.path.join(CWD, "../"))
22+
23+
# pylint: disable=C0413
24+
from lib import topotest
25+
from lib.topogen import Topogen, TopoRouter, get_topogen
26+
from lib.topolog import logger
27+
from lib.common_config import step
28+
29+
def build_topo(tgen):
30+
"Build function"
31+
32+
# Create router r1
33+
tgen.add_router("r1")
34+
35+
# Create a switch to connect to r1's interface
36+
switch = tgen.add_switch("s1")
37+
switch.add_link(tgen.gears["r1"])
38+
39+
40+
def setup_module(mod):
41+
"Sets up the pytest environment"
42+
tgen = Topogen(build_topo, mod.__name__)
43+
tgen.start_topology()
44+
45+
# For all registered routers, load the zebra configuration file
46+
for rname, router in tgen.routers().items():
47+
logger.info("Loading router %s" % rname)
48+
router.load_frr_config(os.path.join(CWD, "{}/frr.conf".format(rname)))
49+
50+
# After loading the configurations, this function loads configured daemons.
51+
tgen.start_router()
52+
53+
54+
def teardown_module():
55+
"Teardown the pytest environment"
56+
tgen = get_topogen()
57+
58+
# This function tears down the whole topology.
59+
tgen.stop_topology()
60+
61+
62+
def test_zebra_received_nhe_kept():
63+
"Test that zebra keeps received next hop entries"
64+
tgen = get_topogen()
65+
if tgen.routers_have_failure():
66+
pytest.skip(tgen.errors)
67+
68+
r1 = tgen.gears["r1"]
69+
70+
step("Get the route information")
71+
# Get the route information
72+
route_info = r1.vtysh_cmd("show ip route json")
73+
route_json = json.loads(route_info)
74+
75+
# Get the NHG ID for 10.0.0.1
76+
nhg_id = None
77+
for prefix, route in route_json.items():
78+
if prefix == "10.0.0.1/32":
79+
nhg_id = route[0].get("receivedNexthopGroupId")
80+
break
81+
82+
step("Verify all routes 10.0.0.1-.5 have the same NHG ID {}".format(nhg_id))
83+
# Verify all routes 10.0.0.1-.5 have the same NHG ID
84+
for i in range(1, 6):
85+
prefix = f"10.0.0.{i}/32"
86+
assert prefix in route_json, f"Route {prefix} not found"
87+
route = route_json[prefix][0]
88+
assert "receivedNexthopGroupId" in route, f"Route {prefix} missing receivedNexthopGroupId"
89+
assert route["receivedNexthopGroupId"] == nhg_id, f"Route {prefix} has different NHG ID"
90+
91+
step("Verify NHG {} has refcount of 5".format(nhg_id))
92+
# Get the NHG information
93+
nhg_info = r1.vtysh_cmd("show nexthop-group rib {} json".format(nhg_id))
94+
nhg_json = json.loads(nhg_info)
95+
assert nhg_json[str(nhg_id)]["refCount"] == 5, "NHG refcount is not 5"
96+
97+
98+
def test_zebra_received_nhe_kept_remove_routes():
99+
"Test that zebra updates refcount when routes are removed"
100+
tgen = get_topogen()
101+
if tgen.routers_have_failure():
102+
pytest.skip(tgen.errors)
103+
104+
r1 = tgen.gears["r1"]
105+
106+
step("Remove routes 10.0.0.3, 10.0.0.4, and 10.0.0.5")
107+
# Remove the routes
108+
for i in range(3, 6):
109+
r1.vtysh_cmd("configure terminal\n no ip route 10.0.0.{}/32 10.0.0.0\n end".format(i))
110+
111+
# Wait for zebra to process the route removals
112+
def check_routes_removed():
113+
route_info = r1.vtysh_cmd("show ip route json")
114+
route_json = json.loads(route_info)
115+
116+
# Check routes 10.0.0.1 and 10.0.0.2 exist
117+
for i in range(1, 3):
118+
prefix = f"10.0.0.{i}/32"
119+
if prefix not in route_json:
120+
return False
121+
122+
# Check routes 10.0.0.3 through 10.0.0.5 don't exist
123+
for i in range(3, 6):
124+
prefix = f"10.0.0.{i}/32"
125+
if prefix in route_json:
126+
return False
127+
128+
return True
129+
130+
step("Wait for routes to be removed")
131+
_, result = topotest.run_and_expect(check_routes_removed, True, count=30, wait=1)
132+
assert result, "Routes were not properly removed"
133+
134+
step("Get the route information")
135+
# Get the route information
136+
route_info = r1.vtysh_cmd("show ip route json")
137+
route_json = json.loads(route_info)
138+
139+
# Get the NHG ID for 10.0.0.1
140+
nhg_id = None
141+
for prefix, route in route_json.items():
142+
if prefix == "10.0.0.1/32":
143+
nhg_id = route[0].get("receivedNexthopGroupId")
144+
break
145+
146+
step("Verify NHG {} has refcount of 2".format(nhg_id))
147+
# Get the NHG information
148+
nhg_info = r1.vtysh_cmd("show nexthop-group rib {} json".format(nhg_id))
149+
nhg_json = json.loads(nhg_info)
150+
assert nhg_json[str(nhg_id)]["refCount"] == 2, "NHG refcount is not 2"
151+
152+
153+
def test_zebra_received_nhe_kept_add_routes():
154+
"Test that zebra updates refcount when routes are added"
155+
tgen = get_topogen()
156+
if tgen.routers_have_failure():
157+
pytest.skip(tgen.errors)
158+
159+
r1 = tgen.gears["r1"]
160+
161+
step("Add routes 10.0.0.3 through 10.0.0.10")
162+
# Add the routes
163+
for i in range(3, 11):
164+
r1.vtysh_cmd("configure terminal\n ip route 10.0.0.{}/32 10.0.0.0\n end".format(i))
165+
166+
# Wait for zebra to process the route additions
167+
def check_routes_added():
168+
route_info = r1.vtysh_cmd("show ip route json")
169+
route_json = json.loads(route_info)
170+
171+
# Check all routes 10.0.0.1 through 10.0.0.10 exist
172+
for i in range(1, 11):
173+
prefix = f"10.0.0.{i}/32"
174+
if prefix not in route_json:
175+
return False
176+
route = route_json[prefix][0]
177+
if "receivedNexthopGroupId" not in route:
178+
return False
179+
180+
return True
181+
182+
step("Wait for routes to be added")
183+
_, result = topotest.run_and_expect(check_routes_added, True, count=30, wait=1)
184+
assert result, "Routes were not properly added"
185+
186+
step("Get the route information")
187+
# Get the route information
188+
route_info = r1.vtysh_cmd("show ip route json")
189+
route_json = json.loads(route_info)
190+
191+
# Get the NHG ID for 10.0.0.1
192+
nhg_id = None
193+
for prefix, route in route_json.items():
194+
if prefix == "10.0.0.1/32":
195+
nhg_id = route[0].get("receivedNexthopGroupId")
196+
break
197+
198+
step("Verify all routes 10.0.0.1-.10 have the same NHG ID {}".format(nhg_id))
199+
# Verify all routes 10.0.0.1-.10 have the same NHG ID
200+
for i in range(1, 11):
201+
prefix = f"10.0.0.{i}/32"
202+
assert prefix in route_json, f"Route {prefix} not found"
203+
route = route_json[prefix][0]
204+
assert "receivedNexthopGroupId" in route, f"Route {prefix} missing receivedNexthopGroupId"
205+
assert route["receivedNexthopGroupId"] == nhg_id, f"Route {prefix} has different NHG ID"
206+
207+
step("Verify NHG {} has refcount of 10".format(nhg_id))
208+
# Get the NHG information
209+
nhg_info = r1.vtysh_cmd("show nexthop-group rib {} json".format(nhg_id))
210+
nhg_json = json.loads(nhg_info)
211+
assert nhg_json[str(nhg_id)]["refCount"] == 10, "NHG refcount is not 10"
212+
213+
214+
def test_zebra_received_nhe_kept_remove_all_routes():
215+
"Test that zebra removes NHG when all routes are removed"
216+
tgen = get_topogen()
217+
if tgen.routers_have_failure():
218+
pytest.skip(tgen.errors)
219+
220+
r1 = tgen.gears["r1"]
221+
222+
step("Get the NHG ID before removing routes")
223+
# Get the route information
224+
route_info = r1.vtysh_cmd("show ip route 10.0.0.1/32 json")
225+
route_json = json.loads(route_info)
226+
227+
# Get the NHG ID for 10.0.0.1
228+
nhg_id = None
229+
for prefix, route in route_json.items():
230+
if prefix == "10.0.0.1/32":
231+
nhg_id = route[0].get("receivedNexthopGroupId")
232+
break
233+
234+
step("Remove all routes 10.0.0.1 through 10.0.0.10")
235+
# Remove all routes
236+
for i in range(1, 11):
237+
r1.vtysh_cmd("configure terminal\n no ip route 10.0.0.{}/32 10.0.0.0\n end".format(i))
238+
239+
# Wait for zebra to process the route removals
240+
def check_all_routes_removed():
241+
route_info = r1.vtysh_cmd("show ip route json")
242+
route_json = json.loads(route_info)
243+
244+
# Check no 10.0.0.x routes exist
245+
for i in range(1, 11):
246+
prefix = f"10.0.0.{i}/32"
247+
if prefix in route_json:
248+
return False
249+
250+
return True
251+
252+
step("Wait for all routes to be removed")
253+
_, result = topotest.run_and_expect(check_all_routes_removed, True, count=30, wait=1)
254+
assert result, "Routes were not properly removed"
255+
256+
step("Get NHG information")
257+
# Get all NHG information
258+
nhg_info = r1.vtysh_cmd("show nexthop-group rib json")
259+
nhg_json = json.loads(nhg_info)
260+
261+
# Verify the specific NHG we looked up is no longer present
262+
assert str(nhg_id) not in nhg_json, f"NHG {nhg_id} still exists after removing all routes"
263+
264+
265+
if __name__ == "__main__":
266+
args = ["-s"] + sys.argv[1:]
267+
sys.exit(pytest.main(args))

0 commit comments

Comments
 (0)