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