]>
Commit | Line | Data |
---|---|---|
b0449478 | 1 | #!/usr/bin/env python |
acddc0ed | 2 | # SPDX-License-Identifier: ISC |
b0449478 DS |
3 | # |
4 | # Copyright (c) 2019 by VMware, Inc. ("VMware") | |
5 | # Used Copyright (c) 2018 by Network Device Education Foundation, Inc. ("NetDEF") | |
6 | # in this file. | |
7 | # | |
b0449478 DS |
8 | |
9 | """ | |
17be83bf | 10 | Following tests are covered to test BGP Graceful Restart functionality. |
b0449478 DS |
11 | Basic Common Test steps for all the test case below : |
12 | - Create topology (setup module) | |
13 | Creating 2 routers topology, r1, r2 in IBGP | |
14 | - Bring up topology | |
15 | - Verify for bgp to converge | |
16 | - Configure BGP Garceful Restart on both the routers. | |
17 | ||
18 | 1. Transition from Peer-level helper to Global Restarting | |
19 | 2. Transition from Peer-level helper to Global inherit helper | |
20 | 3. Transition from Peer-level restarting to Global inherit helper | |
21 | 4. Default GR functional mode is Helper. | |
22 | 5. Verify that the restarting node sets "R" bit while sending the | |
23 | BGP open messages after the node restart, only if GR is enabled. | |
24 | 6. Verify if restarting node resets R bit in BGP open message | |
25 | during normal BGP session flaps as well, even when GR restarting | |
26 | mode is enabled. Here link flap happen due to interface UP/DOWN. | |
27 | 7. Verify if restarting node resets R bit in BGP | |
28 | open message during normal BGP session flaps when GR is disabled. | |
29 | 8. Verify that restarting nodes set "F" bit while sending | |
30 | the BGP open messages after it restarts, only when BGP GR is enabled. | |
31 | 9. Verify that only GR helper routers keep the stale | |
32 | route entries, not any GR disabled router. | |
33 | 10. Verify that GR helper routers keeps all the routes received | |
34 | from restarting node if both the routers are configured as | |
35 | GR restarting node. | |
36 | 11. Verify that GR helper routers delete all the routes | |
37 | received from a node if both the routers are configured as GR | |
38 | helper node. | |
39 | 12. After BGP neighborship is established and GR capability is exchanged, | |
40 | transition restarting router to disabled state and vice versa. | |
41 | 13. After BGP neighborship is established and GR capability is exchanged, | |
42 | transition restarting router to disabled state and vice versa. | |
43 | 14. Verify that restarting nodes reset "F" bit while sending | |
44 | the BGP open messages after it's restarts, when BGP GR is **NOT** enabled. | |
45 | 15. Verify that only GR helper routers keep the stale | |
46 | route entries, not any GR disabled router. | |
47 | 16. Transition from Global Restarting to Disable and then Global | |
48 | Disable to Restarting. | |
49 | 17. Transition from Global Helper to Disable and then Global | |
50 | Disable to Helper. | |
51 | 18. Transition from Global Restart to Helper and then Global | |
52 | Helper to Restart, Global Mode : GR Restarting | |
53 | PerPeer Mode : GR Helper | |
54 | GR Mode effective : GR Helper | |
55 | 19. Transition from Peer-level helper to Global Restarting, | |
56 | Global Mode : GR Restarting | |
57 | PerPeer Mode : GR Restarting | |
58 | GR Mode effective : GR Restarting | |
59 | 20. Transition from Peer-level restart to Global Restart | |
60 | Global Mode : GR Restarting | |
61 | PerPeer Mode : GR Restarting | |
62 | GR Mode effective : GR Restarting | |
63 | 21. Transition from Peer-level disabled to Global Restart | |
64 | Global Mode : GR Restarting | |
65 | PerPeer Mode : GR Disabled | |
66 | GR Mode effective : GR Disabled | |
67 | 22. Peer-level inherit from Global Restarting | |
68 | Global Mode : GR Restart | |
69 | PerPeer Mode : None | |
70 | GR Mode effective : GR Restart | |
d94ee272 | 71 | 23. Transition from Peer-level disable to Global inherit helper |
b0449478 DS |
72 | Global Mode : None |
73 | PerPeer Mode : GR Disable | |
74 | GR Mode effective : GR Disable | |
75 | ||
76 | These tests have been broken up into 4 sub python scripts because | |
77 | the totality of this run was fairly significant. | |
78 | """ | |
79 | ||
80 | import os | |
81 | import sys | |
82 | import time | |
83 | import pytest | |
84 | ||
85 | # Save the Current Working Directory to find configuration files. | |
86 | CWD = os.path.dirname(os.path.realpath(__file__)) | |
87 | sys.path.append(os.path.join("../")) | |
88 | sys.path.append(os.path.join("../lib/")) | |
89 | ||
90 | # pylint: disable=C0413 | |
91 | # Import topogen and topotest helpers | |
92 | from lib.topogen import Topogen, get_topogen | |
93 | from lib.topolog import logger | |
94 | ||
95 | # Required to instantiate the topology builder class. | |
96 | ||
97 | # Import topoJson from lib, to create topology and initial configuration | |
98 | from lib.topojson import build_config_from_json | |
99 | from lib.bgp import ( | |
100 | clear_bgp, | |
101 | verify_bgp_rib, | |
102 | verify_graceful_restart, | |
103 | create_router_bgp, | |
104 | verify_r_bit, | |
105 | verify_f_bit, | |
106 | verify_bgp_convergence, | |
107 | verify_bgp_convergence_from_running_config, | |
108 | ) | |
109 | ||
110 | from lib.common_config import ( | |
111 | write_test_header, | |
112 | reset_config_on_routers, | |
113 | start_topology, | |
114 | kill_router_daemons, | |
115 | start_router_daemons, | |
116 | verify_rib, | |
117 | check_address_types, | |
118 | write_test_footer, | |
119 | check_router_status, | |
120 | shutdown_bringup_interface, | |
121 | step, | |
122 | get_frr_ipv6_linklocal, | |
123 | required_linux_kernel_version, | |
124 | ) | |
125 | ||
126 | pytestmark = [pytest.mark.bgpd] | |
127 | ||
128 | ||
129 | # Global variables | |
130 | NEXT_HOP_IP = {"ipv4": "192.168.1.10", "ipv6": "fd00:0:0:1::10"} | |
131 | NEXT_HOP_IP_1 = {"ipv4": "192.168.0.1", "ipv6": "fd00::1"} | |
132 | NEXT_HOP_IP_2 = {"ipv4": "192.168.0.2", "ipv6": "fd00::2"} | |
133 | BGP_CONVERGENCE = False | |
134 | GR_RESTART_TIMER = 20 | |
135 | PREFERRED_NEXT_HOP = "link_local" | |
136 | ||
137 | ||
138 | def setup_module(mod): | |
139 | """ | |
140 | Sets up the pytest environment | |
141 | ||
142 | * `mod`: module name | |
143 | """ | |
144 | ||
145 | global ADDR_TYPES | |
146 | ||
147 | # Required linux kernel version for this suite to run. | |
148 | result = required_linux_kernel_version("4.16") | |
149 | if result is not True: | |
d63c7094 | 150 | pytest.skip("Kernel requirements are not met, kernel version should be >=4.16") |
b0449478 DS |
151 | |
152 | testsuite_run_time = time.asctime(time.localtime(time.time())) | |
153 | logger.info("Testsuite start time: {}".format(testsuite_run_time)) | |
154 | logger.info("=" * 40) | |
155 | ||
156 | logger.info("Running setup_module to create topology") | |
157 | ||
158 | # This function initiates the topology build with Topogen... | |
159 | json_file = "{}/bgp_gr_topojson_topo1.json".format(CWD) | |
160 | tgen = Topogen(json_file, mod.__name__) | |
161 | global topo | |
162 | topo = tgen.json_topo | |
163 | # ... and here it calls Mininet initialization functions. | |
164 | ||
165 | # Starting topology, create tmp files which are loaded to routers | |
d60a3f0e | 166 | # to start daemons and then start routers |
b0449478 DS |
167 | start_topology(tgen) |
168 | ||
169 | # Creating configuration from JSON | |
170 | build_config_from_json(tgen, topo) | |
171 | ||
172 | # Don't run this test if we have any failure. | |
173 | if tgen.routers_have_failure(): | |
174 | pytest.skip(tgen.errors) | |
175 | ||
176 | # Api call verify whether BGP is converged | |
177 | ADDR_TYPES = check_address_types() | |
178 | ||
179 | BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) | |
180 | assert BGP_CONVERGENCE is True, "setup_module : Failed \n Error:" " {}".format( | |
181 | BGP_CONVERGENCE | |
182 | ) | |
183 | ||
184 | logger.info("Running setup_module() done") | |
185 | ||
186 | ||
187 | def teardown_module(mod): | |
188 | """ | |
189 | Teardown the pytest environment | |
190 | ||
191 | * `mod`: module name | |
192 | """ | |
193 | ||
194 | logger.info("Running teardown_module to delete topology") | |
195 | ||
196 | tgen = get_topogen() | |
197 | ||
198 | # Stop toplogy and Remove tmp files | |
199 | tgen.stop_topology() | |
200 | ||
201 | logger.info( | |
202 | "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) | |
203 | ) | |
204 | logger.info("=" * 40) | |
205 | ||
206 | ||
207 | def configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut, peer): | |
208 | """ | |
209 | This function groups the repetitive function calls into one function. | |
210 | """ | |
211 | ||
212 | result = create_router_bgp(tgen, topo, input_dict) | |
213 | assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) | |
214 | ||
215 | for addr_type in ADDR_TYPES: | |
216 | neighbor = topo["routers"][peer]["links"]["r1-link1"][addr_type].split("/")[0] | |
217 | clear_bgp(tgen, addr_type, dut, neighbor=neighbor) | |
218 | ||
219 | for addr_type in ADDR_TYPES: | |
220 | neighbor = topo["routers"][dut]["links"]["r2-link1"][addr_type].split("/")[0] | |
221 | clear_bgp(tgen, addr_type, peer, neighbor=neighbor) | |
222 | ||
223 | result = verify_bgp_convergence_from_running_config(tgen) | |
224 | assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) | |
225 | ||
226 | return True | |
227 | ||
228 | ||
229 | def next_hop_per_address_family( | |
230 | tgen, dut, peer, addr_type, next_hop_dict, preferred_next_hop=PREFERRED_NEXT_HOP | |
231 | ): | |
232 | """ | |
233 | This function returns link_local or global next_hop per address-family | |
234 | """ | |
235 | ||
236 | intferface = topo["routers"][peer]["links"]["{}-link1".format(dut)]["interface"] | |
237 | if addr_type == "ipv6" and "link_local" in preferred_next_hop: | |
238 | next_hop = get_frr_ipv6_linklocal(tgen, peer, intf=intferface) | |
239 | else: | |
240 | next_hop = next_hop_dict[addr_type] | |
241 | ||
242 | return next_hop | |
243 | ||
244 | ||
245 | def test_BGP_GR_TC_8_p1(request): | |
246 | """ | |
247 | Test Objective : Verify that restarting nodes set "F" bit while sending | |
248 | the BGP open messages after it restarts, only when BGP GR is enabled. | |
249 | """ | |
250 | ||
251 | tgen = get_topogen() | |
252 | tc_name = request.node.name | |
253 | write_test_header(tc_name) | |
254 | ||
255 | # Check router status | |
256 | check_router_status(tgen) | |
257 | ||
258 | # Don't run this test if we have any failure. | |
259 | if tgen.routers_have_failure(): | |
260 | pytest.skip(tgen.errors) | |
261 | ||
262 | # Creating configuration from JSON | |
263 | reset_config_on_routers(tgen) | |
264 | ||
265 | logger.info( | |
266 | "[Phase 1] : Test Setup" " [Restart Mode]R1-----R2[Restart Mode] initialized " | |
267 | ) | |
268 | ||
269 | # Configure graceful-restart | |
270 | input_dict = { | |
271 | "r1": { | |
272 | "bgp": { | |
273 | "graceful-restart": {"preserve-fw-state": True}, | |
274 | "address_family": { | |
275 | "ipv4": { | |
276 | "unicast": { | |
277 | "neighbor": { | |
278 | "r2": { | |
279 | "dest_link": { | |
280 | "r1-link1": {"graceful-restart": True} | |
281 | } | |
282 | } | |
283 | } | |
284 | } | |
285 | }, | |
286 | "ipv6": { | |
287 | "unicast": { | |
288 | "neighbor": { | |
289 | "r2": { | |
290 | "dest_link": { | |
291 | "r1-link1": {"graceful-restart": True} | |
292 | } | |
293 | } | |
294 | } | |
295 | } | |
296 | }, | |
297 | }, | |
298 | } | |
299 | }, | |
300 | "r2": { | |
301 | "bgp": { | |
302 | "address_family": { | |
303 | "ipv4": { | |
304 | "unicast": { | |
305 | "neighbor": { | |
306 | "r1": { | |
307 | "dest_link": { | |
308 | "r2-link1": {"graceful-restart": True} | |
309 | } | |
310 | } | |
311 | } | |
312 | } | |
313 | }, | |
314 | "ipv6": { | |
315 | "unicast": { | |
316 | "neighbor": { | |
317 | "r1": { | |
318 | "dest_link": { | |
319 | "r2-link1": {"graceful-restart": True} | |
320 | } | |
321 | } | |
322 | } | |
323 | } | |
324 | }, | |
325 | } | |
326 | } | |
327 | }, | |
328 | } | |
329 | ||
330 | configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2") | |
331 | ||
332 | for addr_type in ADDR_TYPES: | |
333 | result = verify_graceful_restart( | |
334 | tgen, topo, addr_type, input_dict, dut="r1", peer="r2" | |
335 | ) | |
336 | assert result is True, "Testcase {} : Failed \n Error {}".format( | |
337 | tc_name, result | |
338 | ) | |
339 | ||
340 | # Verifying BGP RIB routes | |
341 | dut = "r1" | |
342 | peer = "r2" | |
343 | next_hop = next_hop_per_address_family( | |
344 | tgen, dut, peer, addr_type, NEXT_HOP_IP_2 | |
345 | ) | |
346 | input_topo = {key: topo["routers"][key] for key in ["r2"]} | |
347 | result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop) | |
348 | assert result is True, "Testcase {} : Failed \n Error {}".format( | |
349 | tc_name, result | |
350 | ) | |
351 | ||
352 | # Verifying RIB routes | |
353 | protocol = "bgp" | |
354 | result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) | |
355 | assert result is True, "Testcase {} : Failed \n Error {}".format( | |
356 | tc_name, result | |
357 | ) | |
358 | ||
359 | logger.info("[Phase 2] : R1 goes for reload ") | |
360 | ||
361 | kill_router_daemons(tgen, "r1", ["bgpd"]) | |
362 | ||
363 | logger.info("[Phase 3] : R1 is about to come up now ") | |
364 | start_router_daemons(tgen, "r1", ["bgpd"]) | |
365 | ||
366 | logger.info("[Phase 4] : R2 is UP now, so time to collect GR stats ") | |
367 | ||
368 | for addr_type in ADDR_TYPES: | |
369 | result = verify_graceful_restart( | |
370 | tgen, topo, addr_type, input_dict, dut="r1", peer="r2" | |
371 | ) | |
372 | assert result is True, "Testcase {} : Failed \n Error {}".format( | |
373 | tc_name, result | |
374 | ) | |
375 | ||
376 | result = verify_r_bit(tgen, topo, addr_type, input_dict, dut="r2", peer="r1") | |
377 | assert result is True, "Testcase {} : Failed \n Error {}".format( | |
378 | tc_name, result | |
379 | ) | |
380 | ||
381 | result = verify_f_bit(tgen, topo, addr_type, input_dict, dut="r2", peer="r1") | |
382 | assert result is True, "Testcase {} : Failed \n Error {}".format( | |
383 | tc_name, result | |
384 | ) | |
385 | ||
386 | write_test_footer(tc_name) | |
387 | ||
388 | ||
389 | if __name__ == "__main__": | |
390 | args = ["-s"] + sys.argv[1:] | |
391 | sys.exit(pytest.main(args)) |