]>
Commit | Line | Data |
---|---|---|
85d47773 | 1 | #!/usr/bin/env python |
acddc0ed | 2 | # SPDX-License-Identifier: ISC |
85d47773 AP |
3 | |
4 | # | |
5 | # Copyright (c) 2019 by VMware, Inc. ("VMware") | |
6 | # Used Copyright (c) 2018 by Network Device Education Foundation, | |
7 | # Inc. ("NetDEF") in this file. | |
8 | # | |
85d47773 AP |
9 | |
10 | ||
11 | """ | |
12 | Following tests are covered to test large-community/community functionality: | |
13 | 1. Verify if large community attribute can be configured only in correct | |
14 | canonical format. | |
15 | 2. Verify that the community attribute value, which we have advertised are | |
16 | received in correct format and values, at the receiving end. | |
17 | 3. Verify BGP Large Community attribute"s transitive property attribute. | |
18 | 4. Verify that BGP Large Communities attribute are malformed, if the length of | |
19 | the BGP Large Communities Attribute value, expressed in octets, | |
20 | is not a non-zero multiple of 12. | |
21 | 5. Verify if overriding large community values works fine. | |
22 | 6. Verify that large community values" aggregation works fine. | |
23 | 7. Standard community also work fine in conjunction with large-community. | |
24 | 8. Matching prefixes based on attributes other than prefix list and make use | |
25 | of set clause (IPV6). | |
26 | 9. Matching prefixes based on attributes other than prefix list and make use | |
27 | of set clause (IPV4). | |
28 | 10. Verify community and large-community list operations in route-map with all | |
29 | clause (exact, all, any, regex) works. | |
30 | 11. Verify that any value in BGP Large communities for boundary values. | |
31 | 12. Clear BGP neighbor-ship and check if large community and community | |
32 | attributes are getting re-populated. | |
33 | ||
34 | """ | |
35 | ||
36 | import pytest | |
37 | import time | |
38 | from os import path as os_path | |
39 | import sys | |
3dfd384e | 40 | |
85d47773 AP |
41 | # Required to instantiate the topology builder class. |
42 | from lib.topogen import Topogen, get_topogen | |
85d47773 AP |
43 | |
44 | from lib.common_config import ( | |
787e7624 | 45 | start_topology, |
46 | write_test_header, | |
47 | write_test_footer, | |
48 | reset_config_on_routers, | |
49 | create_route_maps, | |
50 | create_bgp_community_lists, | |
51 | create_prefix_lists, | |
52 | verify_bgp_community, | |
53 | step, | |
54 | check_address_types, | |
701a0192 | 55 | required_linux_kernel_version, |
85d47773 AP |
56 | ) |
57 | from lib.topolog import logger | |
787e7624 | 58 | from lib.bgp import verify_bgp_convergence, create_router_bgp, clear_bgp_and_verify |
4953ca97 | 59 | from lib.topojson import build_config_from_json |
85d47773 | 60 | |
bf3a0a9a DS |
61 | pytestmark = [pytest.mark.bgpd] |
62 | ||
63 | ||
85d47773 AP |
64 | # Save the Current Working Directory to find configuration files. |
65 | CWD = os_path.dirname(os_path.realpath(__file__)) | |
66 | sys.path.append(os_path.join(CWD, "../")) | |
67 | sys.path.append(os_path.join(CWD, "../lib/")) | |
68 | ||
85d47773 AP |
69 | |
70 | # Global variables | |
71 | bgp_convergence = False | |
72 | NETWORK = { | |
73 | "ipv4": ["200.50.2.0", "200.50.2.1", "200.50.2.0"], | |
787e7624 | 74 | "ipv6": ["1::1", "1::2", "1::0"], |
85d47773 AP |
75 | } |
76 | MASK = {"ipv4": "32", "ipv6": "128"} | |
77 | NET_MASK = {"ipv4": "24", "ipv6": "120"} | |
78 | IPV4_NET = ["200.50.2.0"] | |
79 | IPV6_NET = ["1::0"] | |
80 | CONFIG_ROUTER_R1 = False | |
81 | CONFIG_ROUTER_R2 = False | |
82 | CONFIG_ROUTER_ADDITIVE = False | |
83 | ADDR_TYPES = [] | |
84 | LARGE_COMM = { | |
85 | "r1": "1:1:1 1:2:1 1:3:1 1:4:1 1:5:1", | |
86 | "r2": "2:1:1 2:2:1 2:3:1 2:4:1 2:5:1", | |
87 | "mal_1": "1:1 1:2 1:3 1:4 1:5", | |
88 | "pf_list_1": "0:0:1 0:0:10 0:0:100", | |
89 | "pf_list_2": "0:0:2 0:0:20 0:0:200", | |
90 | "agg_1": "0:0:1 0:0:2 0:0:10 0:0:20 0:0:100 0:0:200 2:1:1 " | |
787e7624 | 91 | "2:2:1 2:3:1 2:4:1 2:5:1", |
92 | "agg_2": "0:0:2 0:0:20 0:0:200 2:1:1 " "2:2:1 2:3:1 2:4:1 2:5:1", | |
85d47773 AP |
93 | } |
94 | STANDARD_COMM = { | |
95 | "r1": "1:1 1:2 1:3 1:4 1:5", | |
96 | "r2": "2:1 2:2 2:3 2:4 2:5", | |
97 | "mal_1": "1 2 3 4 5", | |
98 | "pf_list_1": "0:1 0:10 0:100", | |
99 | "pf_list_2": "0:2 0:20 0:200", | |
100 | "agg_1": "0:1 0:2 0:10 0:20 0:100 0:200 2:1 2:2 2:3 2:4 2:5", | |
787e7624 | 101 | "agg_2": "0:2 0:20 0:200 2:1 2:2 2:3 2:4 2:5", |
85d47773 AP |
102 | } |
103 | ||
104 | ||
85d47773 AP |
105 | def setup_module(mod): |
106 | """ | |
107 | Sets up the pytest environment | |
108 | ||
109 | * `mod`: module name | |
110 | """ | |
3dfd384e | 111 | # Required linux kernel version for this suite to run. |
701a0192 | 112 | result = required_linux_kernel_version("4.15") |
955212d9 | 113 | if result is not True: |
114 | pytest.skip("Kernel requirements are not met") | |
3dfd384e | 115 | |
85d47773 AP |
116 | global ADDR_TYPES |
117 | testsuite_run_time = time.asctime(time.localtime(time.time())) | |
118 | logger.info("Testsuite start time: {}".format(testsuite_run_time)) | |
119 | logger.info("=" * 40) | |
120 | ||
121 | logger.info("Running setup_module to create topology") | |
122 | ||
123 | # This function initiates the topology build with Topogen... | |
e82b531d CH |
124 | json_file = "{}/bgp_large_community_topo_1.json".format(CWD) |
125 | tgen = Topogen(json_file, mod.__name__) | |
126 | global topo | |
127 | topo = tgen.json_topo | |
85d47773 AP |
128 | # ... and here it calls Mininet initialization functions. |
129 | ||
130 | # Starting topology, create tmp files which are loaded to routers | |
d60a3f0e | 131 | # to start daemons and then start routers |
85d47773 AP |
132 | start_topology(tgen) |
133 | ||
134 | # Creating configuration from JSON | |
135 | build_config_from_json(tgen, topo) | |
136 | ||
137 | # Checking BGP convergence | |
138 | global bgp_convergence | |
139 | ||
140 | # Don"t run this test if we have any failure. | |
141 | if tgen.routers_have_failure(): | |
142 | pytest.skip(tgen.errors) | |
143 | ||
144 | ##tgen.mininet_cli() | |
145 | # Api call verify whether BGP is converged | |
146 | bgp_convergence = verify_bgp_convergence(tgen, topo) | |
787e7624 | 147 | assert bgp_convergence is True, "setup_module :Failed \n Error:" " {}".format( |
148 | bgp_convergence | |
149 | ) | |
85d47773 AP |
150 | |
151 | ADDR_TYPES = check_address_types() | |
152 | logger.info("Running setup_module() done") | |
153 | ||
154 | ||
155 | def teardown_module(): | |
156 | """ | |
157 | Teardown the pytest environment | |
158 | ||
159 | * `mod`: module name | |
160 | """ | |
161 | ||
162 | logger.info("Running teardown_module to delete topology") | |
163 | ||
164 | tgen = get_topogen() | |
165 | ||
166 | # Stop toplogy and Remove tmp files | |
167 | tgen.stop_topology() | |
168 | ||
787e7624 | 169 | logger.info( |
170 | "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) | |
171 | ) | |
85d47773 AP |
172 | logger.info("=" * 40) |
173 | ||
174 | ||
175 | def config_router_r1(tgen, topo, tc_name): | |
176 | global CONFIG_ROUTER_R1 | |
177 | ||
178 | input_dict_1 = { | |
179 | "r1": { | |
180 | "route_maps": { | |
181 | "LC1": [ | |
182 | { | |
183 | "action": "permit", | |
184 | "seq_id": "10", | |
185 | "set": { | |
787e7624 | 186 | "large_community": {"num": LARGE_COMM["r1"]}, |
187 | "community": {"num": STANDARD_COMM["r1"]}, | |
188 | }, | |
85d47773 AP |
189 | } |
190 | ] | |
191 | } | |
192 | } | |
193 | } | |
194 | ||
195 | step("Configuring LC1 on r1") | |
196 | result = create_route_maps(tgen, input_dict_1) | |
787e7624 | 197 | assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result) |
85d47773 AP |
198 | |
199 | # Configure neighbor for route map | |
200 | input_dict_2 = { | |
201 | "r1": { | |
202 | "bgp": { | |
203 | "address_family": { | |
204 | "ipv4": { | |
205 | "unicast": { | |
206 | "advertise_networks": [ | |
207 | { | |
787e7624 | 208 | "network": "%s/%s" |
209 | % (NETWORK["ipv4"][0], MASK["ipv4"]), | |
210 | "no_of_network": 4, | |
85d47773 AP |
211 | } |
212 | ], | |
213 | "neighbor": { | |
214 | "r2": { | |
215 | "dest_link": { | |
216 | "r1-link1": { | |
787e7624 | 217 | "route_maps": [ |
218 | {"name": "LC1", "direction": "out"} | |
219 | ] | |
85d47773 AP |
220 | } |
221 | } | |
222 | }, | |
223 | "r3": { | |
224 | "dest_link": { | |
225 | "r1-link1": { | |
787e7624 | 226 | "route_maps": [ |
227 | {"name": "LC1", "direction": "out"} | |
228 | ] | |
85d47773 AP |
229 | } |
230 | } | |
787e7624 | 231 | }, |
232 | }, | |
85d47773 AP |
233 | } |
234 | }, | |
235 | "ipv6": { | |
236 | "unicast": { | |
237 | "advertise_networks": [ | |
238 | { | |
787e7624 | 239 | "network": "%s/%s" |
240 | % (NETWORK["ipv6"][0], MASK["ipv6"]), | |
241 | "no_of_network": 4, | |
85d47773 AP |
242 | } |
243 | ], | |
244 | "neighbor": { | |
245 | "r2": { | |
246 | "dest_link": { | |
247 | "r1-link1": { | |
787e7624 | 248 | "route_maps": [ |
249 | {"name": "LC1", "direction": "out"} | |
250 | ] | |
85d47773 AP |
251 | } |
252 | } | |
253 | }, | |
254 | "r3": { | |
255 | "dest_link": { | |
256 | "r1-link1": { | |
787e7624 | 257 | "route_maps": [ |
258 | {"name": "LC1", "direction": "out"} | |
259 | ] | |
85d47773 AP |
260 | } |
261 | } | |
787e7624 | 262 | }, |
263 | }, | |
85d47773 | 264 | } |
787e7624 | 265 | }, |
85d47773 AP |
266 | } |
267 | } | |
268 | } | |
269 | } | |
270 | ||
271 | step("Applying LC1 on r1 neighbors and advertising networks") | |
272 | result = create_router_bgp(tgen, topo, input_dict_2) | |
787e7624 | 273 | assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result) |
85d47773 AP |
274 | |
275 | CONFIG_ROUTER_R1 = True | |
276 | ||
277 | ||
278 | def config_router_r2(tgen, topo, tc_name): | |
279 | global CONFIG_ROUTER_R2 | |
280 | ||
281 | input_dict = { | |
282 | "r2": { | |
283 | "route_maps": { | |
284 | "LC2": [ | |
285 | { | |
286 | "action": "permit", | |
287 | "seq_id": "10", | |
288 | "set": { | |
787e7624 | 289 | "large_community": {"num": LARGE_COMM["r2"]}, |
290 | "community": {"num": STANDARD_COMM["r2"]}, | |
291 | }, | |
85d47773 AP |
292 | } |
293 | ] | |
294 | } | |
295 | } | |
296 | } | |
297 | ||
298 | step("Configuring route-maps LC2 on r2") | |
299 | result = create_route_maps(tgen, input_dict) | |
787e7624 | 300 | assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result) |
85d47773 AP |
301 | |
302 | input_dict_1 = { | |
303 | "r2": { | |
304 | "bgp": { | |
305 | "address_family": { | |
306 | "ipv4": { | |
307 | "unicast": { | |
308 | "neighbor": { | |
309 | "r4": { | |
310 | "dest_link": { | |
311 | "r2-link1": { | |
787e7624 | 312 | "route_maps": [ |
313 | {"name": "LC2", "direction": "out"} | |
314 | ] | |
85d47773 AP |
315 | } |
316 | } | |
317 | } | |
318 | } | |
319 | } | |
320 | }, | |
321 | "ipv6": { | |
322 | "unicast": { | |
323 | "neighbor": { | |
324 | "r4": { | |
325 | "dest_link": { | |
326 | "r2-link1": { | |
787e7624 | 327 | "route_maps": [ |
328 | {"name": "LC2", "direction": "out"} | |
329 | ] | |
85d47773 AP |
330 | } |
331 | } | |
332 | } | |
333 | } | |
334 | } | |
787e7624 | 335 | }, |
85d47773 AP |
336 | } |
337 | } | |
338 | } | |
339 | } | |
340 | ||
341 | step("Applying LC2 on r2 neighbors in out direction") | |
342 | result = create_router_bgp(tgen, topo, input_dict_1) | |
787e7624 | 343 | assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result) |
85d47773 AP |
344 | |
345 | CONFIG_ROUTER_R2 = True | |
346 | ||
347 | ||
348 | def config_router_additive(tgen, topo, tc_name): | |
349 | global CONFIG_ROUTER_ADDITIVE | |
350 | ||
351 | input_dict = { | |
352 | "r2": { | |
353 | "route_maps": { | |
354 | "LC2": [ | |
355 | { | |
356 | "action": "permit", | |
357 | "seq_id": "10", | |
358 | "set": { | |
359 | "large_community": { | |
360 | "num": LARGE_COMM["r2"], | |
787e7624 | 361 | "action": "additive", |
85d47773 AP |
362 | }, |
363 | "community": { | |
364 | "num": STANDARD_COMM["r2"], | |
787e7624 | 365 | "action": "additive", |
366 | }, | |
367 | }, | |
85d47773 AP |
368 | } |
369 | ] | |
370 | } | |
371 | } | |
372 | } | |
373 | ||
374 | step("Configuring LC2 with community attributes as additive") | |
375 | result = create_route_maps(tgen, input_dict) | |
787e7624 | 376 | assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result) |
85d47773 AP |
377 | |
378 | # tgen.mininet_cli() | |
379 | CONFIG_ROUTER_ADDITIVE = True | |
380 | ||
381 | ||
382 | def config_for_as_path(tgen, topo, tc_name): | |
383 | config_router_r1(tgen, topo, tc_name) | |
384 | ||
385 | config_router_r2(tgen, topo, tc_name) | |
386 | ||
387 | # Create ipv6 prefix list | |
388 | input_dict_1 = { | |
389 | "r1": { | |
390 | "prefix_lists": { | |
391 | "ipv4": { | |
392 | "pf_list_1": [ | |
393 | { | |
394 | "seqid": "10", | |
787e7624 | 395 | "network": "%s/%s" % (NETWORK["ipv4"][0], MASK["ipv4"]), |
396 | "action": "permit", | |
85d47773 AP |
397 | } |
398 | ], | |
399 | "pf_list_2": [ | |
400 | { | |
401 | "seqid": "10", | |
787e7624 | 402 | "network": "%s/%s" % (NETWORK["ipv4"][1], MASK["ipv4"]), |
403 | "action": "permit", | |
85d47773 | 404 | } |
787e7624 | 405 | ], |
85d47773 AP |
406 | }, |
407 | "ipv6": { | |
408 | "pf_list_3": [ | |
409 | { | |
410 | "seqid": "10", | |
787e7624 | 411 | "network": "%s/%s" % (NETWORK["ipv6"][0], MASK["ipv6"]), |
412 | "action": "permit", | |
85d47773 AP |
413 | } |
414 | ], | |
415 | "pf_list_4": [ | |
416 | { | |
417 | "seqid": "10", | |
787e7624 | 418 | "network": "%s/%s" % (NETWORK["ipv6"][1], MASK["ipv6"]), |
419 | "action": "permit", | |
85d47773 | 420 | } |
787e7624 | 421 | ], |
422 | }, | |
85d47773 AP |
423 | } |
424 | } | |
425 | } | |
426 | ||
427 | step("Configuring prefix-lists on r1 to filter networks") | |
428 | result = create_prefix_lists(tgen, input_dict_1) | |
787e7624 | 429 | assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result) |
85d47773 AP |
430 | |
431 | input_dict_2 = { | |
432 | "r1": { | |
433 | "route_maps": { | |
434 | "LC1": [ | |
435 | { | |
436 | "action": "permit", | |
437 | "seq_id": 10, | |
787e7624 | 438 | "match": {"ipv4": {"prefix_lists": "pf_list_1"}}, |
85d47773 | 439 | "set": { |
787e7624 | 440 | "large_community": {"num": LARGE_COMM["pf_list_1"]}, |
441 | "community": {"num": STANDARD_COMM["pf_list_1"]}, | |
442 | }, | |
85d47773 AP |
443 | }, |
444 | { | |
445 | "action": "permit", | |
446 | "seq_id": 20, | |
787e7624 | 447 | "match": {"ipv6": {"prefix_lists": "pf_list_3"}}, |
85d47773 | 448 | "set": { |
787e7624 | 449 | "large_community": {"num": LARGE_COMM["pf_list_1"]}, |
450 | "community": {"num": STANDARD_COMM["pf_list_1"]}, | |
451 | }, | |
85d47773 AP |
452 | }, |
453 | { | |
454 | "action": "permit", | |
455 | "seq_id": 30, | |
787e7624 | 456 | "match": {"ipv4": {"prefix_lists": "pf_list_2"}}, |
85d47773 | 457 | "set": { |
787e7624 | 458 | "large_community": {"num": LARGE_COMM["pf_list_2"]}, |
459 | "community": {"num": STANDARD_COMM["pf_list_2"]}, | |
460 | }, | |
85d47773 AP |
461 | }, |
462 | { | |
463 | "action": "permit", | |
464 | "seq_id": 40, | |
787e7624 | 465 | "match": {"ipv6": {"prefix_lists": "pf_list_4"}}, |
85d47773 | 466 | "set": { |
787e7624 | 467 | "large_community": {"num": LARGE_COMM["pf_list_2"]}, |
468 | "community": {"num": STANDARD_COMM["pf_list_2"]}, | |
469 | }, | |
470 | }, | |
85d47773 AP |
471 | ] |
472 | } | |
473 | } | |
474 | } | |
475 | ||
787e7624 | 476 | step( |
477 | "Applying prefix-lists match in route-map LC1 on r1. Setting" | |
478 | " community attritbute for filtered networks" | |
479 | ) | |
85d47773 | 480 | result = create_route_maps(tgen, input_dict_2) |
787e7624 | 481 | assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result) |
85d47773 AP |
482 | |
483 | config_router_additive(tgen, topo, tc_name) | |
484 | ||
485 | input_dict_3 = { | |
486 | "r4": { | |
487 | "bgp_community_lists": [ | |
488 | { | |
489 | "community_type": "standard", | |
490 | "action": "permit", | |
491 | "name": "ANY", | |
492 | "value": LARGE_COMM["pf_list_1"], | |
787e7624 | 493 | "large": True, |
85d47773 AP |
494 | }, |
495 | { | |
496 | "community_type": "standard", | |
497 | "action": "permit", | |
498 | "name": "ANY", | |
499 | "value": STANDARD_COMM["pf_list_1"], | |
787e7624 | 500 | }, |
85d47773 AP |
501 | ] |
502 | } | |
503 | } | |
504 | ||
505 | step("Configuring bgp community lists on r4") | |
506 | result = create_bgp_community_lists(tgen, input_dict_3) | |
787e7624 | 507 | assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result) |
85d47773 AP |
508 | |
509 | input_dict_4 = { | |
510 | "r4": { | |
511 | "route_maps": { | |
512 | "LC4": [ | |
513 | { | |
514 | "action": "permit", | |
515 | "seq_id": "10", | |
516 | "match": { | |
517 | "large_community_list": {"id": "ANY"}, | |
787e7624 | 518 | "community_list": {"id": "ANY"}, |
85d47773 | 519 | }, |
787e7624 | 520 | "set": {"path": {"as_num": "4000000", "as_action": "prepend"}}, |
85d47773 AP |
521 | } |
522 | ] | |
523 | } | |
524 | } | |
525 | } | |
526 | ||
527 | step("Applying community list on route-map on r4") | |
528 | result = create_route_maps(tgen, input_dict_4) | |
787e7624 | 529 | assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result) |
85d47773 AP |
530 | |
531 | input_dict_5 = { | |
532 | "r4": { | |
533 | "bgp": { | |
534 | "address_family": { | |
535 | "ipv4": { | |
536 | "unicast": { | |
537 | "neighbor": { | |
538 | "r5": { | |
539 | "dest_link": { | |
540 | "r4-link1": { | |
787e7624 | 541 | "route_maps": [ |
542 | {"name": "LC4", "direction": "out"} | |
543 | ] | |
85d47773 AP |
544 | } |
545 | } | |
546 | } | |
547 | } | |
548 | } | |
549 | }, | |
550 | "ipv6": { | |
551 | "unicast": { | |
552 | "neighbor": { | |
553 | "r5": { | |
554 | "dest_link": { | |
555 | "r4-link1": { | |
787e7624 | 556 | "route_maps": [ |
557 | {"name": "LC4", "direction": "out"} | |
558 | ] | |
85d47773 AP |
559 | } |
560 | } | |
561 | } | |
562 | } | |
563 | } | |
787e7624 | 564 | }, |
85d47773 AP |
565 | } |
566 | } | |
567 | } | |
568 | } | |
569 | ||
570 | step("Applying route-map LC4 out from r4 to r5 ") | |
571 | result = create_router_bgp(tgen, topo, input_dict_5) | |
787e7624 | 572 | assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result) |
85d47773 AP |
573 | |
574 | ||
575 | ##################################################### | |
576 | # | |
577 | # Test cases | |
578 | # | |
579 | ##################################################### | |
580 | def test_large_community_set(request): | |
581 | """ | |
582 | Verify if large community attribute can be configured only in correct | |
583 | canonical format. | |
584 | """ | |
585 | tc_name = request.node.name | |
586 | write_test_header(tc_name) | |
587 | tgen = get_topogen() | |
588 | ||
589 | # Don"t run this test if we have any failure. | |
590 | if tgen.routers_have_failure(): | |
591 | pytest.skip(tgen.errors) | |
592 | ||
593 | # API call to modify router id | |
594 | # input_dict dictionary to be provided to configure route_map | |
595 | input_dict = { | |
596 | "r1": { | |
597 | "route_maps": { | |
598 | "LC1": [ | |
599 | { | |
600 | "action": "permit", | |
601 | "seq_id": "10", | |
602 | "set": { | |
603 | "large_community": {"num": LARGE_COMM["r1"]}, | |
787e7624 | 604 | "community": {"num": STANDARD_COMM["r1"]}, |
605 | }, | |
85d47773 AP |
606 | } |
607 | ] | |
608 | } | |
609 | } | |
610 | } | |
611 | ||
612 | step("Trying to set bgp communities") | |
613 | result = create_route_maps(tgen, input_dict) | |
787e7624 | 614 | assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result) |
85d47773 AP |
615 | |
616 | write_test_footer(tc_name) | |
617 | ||
618 | ||
619 | def test_large_community_advertise(request): | |
620 | """ | |
621 | Verify that the community attribute value, which we have advertised are | |
622 | received in correct format and values, at the receiving end. | |
623 | """ | |
624 | tc_name = request.node.name | |
625 | write_test_header(tc_name) | |
626 | tgen = get_topogen() | |
627 | ||
628 | # Don"t run this test if we have any failure. | |
629 | if tgen.routers_have_failure(): | |
630 | pytest.skip(tgen.errors) | |
631 | ||
632 | reset_config_on_routers(tgen) | |
633 | config_router_r1(tgen, topo, tc_name) | |
634 | ||
635 | input_dict = { | |
636 | "largeCommunity": LARGE_COMM["r1"], | |
637 | "community": STANDARD_COMM["r1"], | |
638 | } | |
639 | ||
640 | for adt in ADDR_TYPES: | |
787e7624 | 641 | result = verify_bgp_community(tgen, adt, "r2", [NETWORK[adt][0]], input_dict) |
85d47773 | 642 | assert result is True, "Test case {} : Failed \n Error: {}".format( |
787e7624 | 643 | tc_name, result |
644 | ) | |
85d47773 | 645 | |
787e7624 | 646 | result = verify_bgp_community(tgen, adt, "r3", [NETWORK[adt][0]], input_dict) |
85d47773 | 647 | assert result is True, "Test case {} : Failed \n Error: {}".format( |
787e7624 | 648 | tc_name, result |
649 | ) | |
85d47773 AP |
650 | |
651 | write_test_footer(tc_name) | |
652 | ||
653 | ||
654 | def test_large_community_transitive(request): | |
655 | """ | |
656 | Verify BGP Large Community attribute"s transitive property attribute. | |
657 | """ | |
658 | tc_name = request.node.name | |
659 | write_test_header(tc_name) | |
660 | tgen = get_topogen() | |
661 | ||
662 | # Don"t run this test if we have any failure. | |
663 | if tgen.routers_have_failure(): | |
664 | pytest.skip(tgen.errors) | |
665 | ||
666 | reset_config_on_routers(tgen) | |
667 | ||
668 | config_router_r1(tgen, topo, tc_name) | |
669 | ||
670 | input_dict_1 = { | |
671 | "largeCommunity": LARGE_COMM["r1"], | |
787e7624 | 672 | "community": STANDARD_COMM["r1"], |
85d47773 AP |
673 | } |
674 | ||
675 | for adt in ADDR_TYPES: | |
787e7624 | 676 | result = verify_bgp_community(tgen, adt, "r4", [NETWORK[adt][0]], input_dict_1) |
85d47773 | 677 | assert result is True, "Test case {} : Failed \n Error: {}".format( |
787e7624 | 678 | tc_name, result |
679 | ) | |
85d47773 AP |
680 | |
681 | write_test_footer(tc_name) | |
682 | ||
683 | ||
684 | def test_large_community_override(request): | |
685 | """ | |
686 | Verify if overriding large community values works fine. | |
687 | """ | |
688 | tc_name = request.node.name | |
689 | write_test_header(tc_name) | |
690 | tgen = get_topogen() | |
691 | ||
692 | # Don"t run this test if we have any failure. | |
693 | if tgen.routers_have_failure(): | |
694 | pytest.skip(tgen.errors) | |
695 | ||
696 | reset_config_on_routers(tgen) | |
697 | config_router_r1(tgen, topo, tc_name) | |
698 | ||
699 | config_router_r2(tgen, topo, tc_name) | |
700 | ||
701 | input_dict_3 = { | |
702 | "largeCommunity": LARGE_COMM["r2"], | |
787e7624 | 703 | "community": STANDARD_COMM["r2"], |
85d47773 AP |
704 | } |
705 | ||
706 | for adt in ADDR_TYPES: | |
787e7624 | 707 | result = verify_bgp_community(tgen, adt, "r4", [NETWORK[adt][1]], input_dict_3) |
708 | assert result is True, "Test case {} : Failed \n Error: {}".format( | |
709 | tc_name, result | |
710 | ) | |
85d47773 AP |
711 | |
712 | write_test_footer(tc_name) | |
713 | ||
714 | ||
715 | def test_large_community_additive(request): | |
716 | """ | |
717 | Verify that large community values" aggregation works fine. | |
718 | """ | |
719 | tc_name = request.node.name | |
720 | write_test_header(tc_name) | |
721 | tgen = get_topogen() | |
722 | ||
723 | # Don"t run this test if we have any failure. | |
724 | if tgen.routers_have_failure(): | |
725 | pytest.skip(tgen.errors) | |
726 | ||
727 | reset_config_on_routers(tgen) | |
728 | config_router_r1(tgen, topo, tc_name) | |
729 | ||
730 | config_router_r2(tgen, topo, tc_name) | |
731 | ||
732 | config_router_additive(tgen, topo, tc_name) | |
733 | ||
734 | input_dict_1 = { | |
735 | "largeCommunity": "%s %s" % (LARGE_COMM["r1"], LARGE_COMM["r2"]), | |
787e7624 | 736 | "community": "%s %s" % (STANDARD_COMM["r1"], STANDARD_COMM["r2"]), |
85d47773 AP |
737 | } |
738 | ||
739 | for adt in ADDR_TYPES: | |
787e7624 | 740 | result = verify_bgp_community(tgen, adt, "r4", [NETWORK[adt][0]], input_dict_1) |
741 | assert result is True, "Test case {} : Failed \n Error: {}".format( | |
742 | tc_name, result | |
743 | ) | |
85d47773 AP |
744 | |
745 | write_test_footer(tc_name) | |
746 | ||
747 | ||
748 | def test_large_community_match_as_path(request): | |
749 | """ | |
750 | Matching prefixes based on attributes other than prefix list and make use | |
751 | of set clause. | |
752 | """ | |
753 | ||
754 | tc_name = request.node.name | |
755 | write_test_header(tc_name) | |
756 | tgen = get_topogen() | |
757 | ||
758 | # Don"t run this test if we have any failure. | |
759 | if tgen.routers_have_failure(): | |
760 | pytest.skip(tgen.errors) | |
761 | ||
762 | reset_config_on_routers(tgen) | |
763 | config_for_as_path(tgen, topo, tc_name) | |
764 | ||
765 | input_dict = { | |
787e7624 | 766 | "largeCommunity": "%s %s" % (LARGE_COMM["pf_list_1"], LARGE_COMM["r2"]), |
767 | "community": "%s %s" % (STANDARD_COMM["pf_list_1"], STANDARD_COMM["r2"]), | |
85d47773 AP |
768 | } |
769 | ||
770 | input_dict_1 = { | |
787e7624 | 771 | "largeCommunity": "%s %s" % (LARGE_COMM["pf_list_2"], LARGE_COMM["r2"]), |
772 | "community": "%s %s" % (STANDARD_COMM["pf_list_2"], STANDARD_COMM["r2"]), | |
85d47773 AP |
773 | } |
774 | ||
775 | for adt in ADDR_TYPES: | |
787e7624 | 776 | result = verify_bgp_community(tgen, adt, "r5", [NETWORK[adt][0]], input_dict) |
777 | assert result is True, "Test case {} : Failed \n Error: {}".format( | |
778 | tc_name, result | |
779 | ) | |
85d47773 | 780 | |
787e7624 | 781 | result = verify_bgp_community( |
782 | tgen, adt, "r5", [NETWORK[adt][1]], input_dict_1, expected=False | |
783 | ) | |
85d47773 | 784 | |
787e7624 | 785 | assert result is not True, "Test case {} : Should fail \n Error: {}".format( |
786 | tc_name, result | |
787 | ) | |
85d47773 AP |
788 | |
789 | write_test_footer(tc_name) | |
790 | ||
791 | ||
792 | def test_large_community_match_all(request): | |
793 | """ | |
794 | Verify community and large-community list operations in route-map with all | |
795 | clause (exact, all, any, regex) works. | |
796 | """ | |
797 | tc_name = request.node.name | |
798 | write_test_header(tc_name) | |
799 | tgen = get_topogen() | |
800 | ||
801 | # Don"t run this test if we have any failure. | |
802 | if tgen.routers_have_failure(): | |
803 | pytest.skip(tgen.errors) | |
804 | ||
805 | reset_config_on_routers(tgen) | |
806 | config_router_r1(tgen, topo, tc_name) | |
807 | ||
808 | config_router_r2(tgen, topo, tc_name) | |
809 | ||
810 | config_router_additive(tgen, topo, tc_name) | |
811 | ||
812 | input_dict_1 = { | |
813 | "r4": { | |
814 | "bgp_community_lists": [ | |
815 | { | |
816 | "community_type": "standard", | |
817 | "action": "permit", | |
818 | "name": "ANY", | |
819 | "value": "1:1:1", | |
787e7624 | 820 | "large": True, |
85d47773 AP |
821 | }, |
822 | { | |
823 | "community_type": "standard", | |
824 | "action": "permit", | |
825 | "name": "ALL", | |
826 | "value": "1:1:1 1:2:1 1:3:1 1:4:1 1:5:1 2:1:1 2:2:1", | |
787e7624 | 827 | "large": True, |
85d47773 AP |
828 | }, |
829 | { | |
830 | "community_type": "expanded", | |
831 | "action": "permit", | |
832 | "name": "EXP_ALL", | |
833 | "value": "1:1:1 1:2:1 1:3:1 1:4:1 1:5:1 2:[1-5]:1", | |
787e7624 | 834 | "large": True, |
835 | }, | |
85d47773 AP |
836 | ] |
837 | } | |
838 | } | |
839 | ||
840 | step("Create bgp community lists for ANY, EXACT and EXP_ALL match") | |
841 | ||
842 | result = create_bgp_community_lists(tgen, input_dict_1) | |
787e7624 | 843 | assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result) |
85d47773 AP |
844 | |
845 | input_dict_2 = { | |
846 | "r4": { | |
847 | "route_maps": { | |
848 | "LC4": [ | |
849 | { | |
850 | "action": "permit", | |
851 | "seq_id": "10", | |
787e7624 | 852 | "match": {"large-community-list": {"id": "ANY"}}, |
85d47773 AP |
853 | }, |
854 | { | |
855 | "action": "permit", | |
856 | "seq_id": "20", | |
787e7624 | 857 | "match": {"large-community-list": {"id": "EXACT"}}, |
85d47773 AP |
858 | }, |
859 | { | |
860 | "action": "permit", | |
861 | "seq_id": "30", | |
787e7624 | 862 | "match": {"large-community-list": {"id": "EXP_ALL"}}, |
863 | }, | |
85d47773 AP |
864 | ] |
865 | } | |
866 | } | |
867 | } | |
868 | ||
869 | step("Applying bgp community lits on LC4 route-map") | |
870 | result = create_route_maps(tgen, input_dict_2) | |
787e7624 | 871 | assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result) |
85d47773 AP |
872 | |
873 | input_dict_3 = { | |
874 | "r4": { | |
875 | "bgp": { | |
876 | "address_family": { | |
877 | "ipv4": { | |
878 | "unicast": { | |
879 | "neighbor": { | |
880 | "r5": { | |
881 | "dest_link": { | |
882 | "r4-link1": { | |
787e7624 | 883 | "route_maps": [ |
884 | {"name": "LC4", "direction": "in"} | |
885 | ] | |
85d47773 AP |
886 | } |
887 | } | |
888 | } | |
889 | } | |
890 | } | |
891 | }, | |
892 | "ipv6": { | |
893 | "unicast": { | |
894 | "neighbor": { | |
895 | "r5": { | |
896 | "dest_link": { | |
897 | "r4-link1": { | |
787e7624 | 898 | "route_maps": [ |
899 | {"name": "LC4", "direction": "in"} | |
900 | ] | |
85d47773 AP |
901 | } |
902 | } | |
903 | } | |
904 | } | |
905 | } | |
787e7624 | 906 | }, |
85d47773 AP |
907 | } |
908 | } | |
909 | } | |
910 | } | |
911 | ||
912 | step("Apply route-mpa LC4 on r4 for r2 neighbor, direction 'in'") | |
913 | ||
914 | result = create_router_bgp(tgen, topo, input_dict_3) | |
787e7624 | 915 | assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result) |
85d47773 AP |
916 | |
917 | input_dict_4 = { | |
918 | "largeCommunity": "1:1:1 1:2:1 1:3:1 1:4:1 1:5:1 2:1:1 2:2:1 2:3:1 " | |
787e7624 | 919 | "2:4:1 2:5:1" |
85d47773 AP |
920 | } |
921 | ||
922 | for adt in ADDR_TYPES: | |
787e7624 | 923 | result = verify_bgp_community(tgen, adt, "r4", [NETWORK[adt][0]], input_dict_4) |
924 | assert result is True, "Test case {} : Should fail \n Error: {}".format( | |
925 | tc_name, result | |
926 | ) | |
85d47773 AP |
927 | |
928 | write_test_footer(tc_name) | |
929 | ||
930 | ||
787e7624 | 931 | # @pytest.mark.skip(reason="as-set not working for ipv6") |
85d47773 AP |
932 | def test_large_community_aggregate_network(request): |
933 | """ | |
934 | Restart router and check if large community and community | |
935 | attributes are getting re-populated. | |
936 | """ | |
937 | ||
938 | tc_name = request.node.name | |
939 | write_test_header(tc_name) | |
940 | ||
941 | tgen = get_topogen() | |
942 | ||
943 | # Don"t run this test if we have any failure. | |
944 | if tgen.routers_have_failure(): | |
945 | pytest.skip(tgen.errors) | |
946 | ||
947 | reset_config_on_routers(tgen) | |
948 | ||
949 | config_for_as_path(tgen, topo, tc_name) | |
950 | ||
951 | input_dict = { | |
952 | "community": STANDARD_COMM["agg_1"], | |
787e7624 | 953 | "largeCommunity": LARGE_COMM["agg_1"], |
85d47773 AP |
954 | } |
955 | ||
956 | input_dict_1 = { | |
957 | "r2": { | |
958 | "bgp": { | |
959 | "address_family": { | |
960 | "ipv4": { | |
961 | "unicast": { | |
962 | "aggregate_address": [ | |
963 | { | |
787e7624 | 964 | "network": "%s/%s" |
965 | % (NETWORK["ipv4"][2], NET_MASK["ipv4"]), | |
966 | "as_set": True, | |
85d47773 AP |
967 | } |
968 | ] | |
969 | } | |
970 | }, | |
971 | "ipv6": { | |
972 | "unicast": { | |
973 | "aggregate_address": [ | |
974 | { | |
787e7624 | 975 | "network": "%s/%s" |
976 | % (NETWORK["ipv6"][2], NET_MASK["ipv6"]), | |
977 | "as_set": True, | |
85d47773 AP |
978 | } |
979 | ] | |
980 | } | |
787e7624 | 981 | }, |
85d47773 AP |
982 | } |
983 | } | |
984 | } | |
985 | } | |
986 | ||
987 | step("Configuring aggregate address as-set on r2") | |
988 | result = create_router_bgp(tgen, topo, input_dict_1) | |
787e7624 | 989 | assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result) |
85d47773 AP |
990 | |
991 | for adt in ADDR_TYPES: | |
787e7624 | 992 | result = verify_bgp_community( |
993 | tgen, adt, "r4", ["%s/%s" % (NETWORK[adt][2], NET_MASK[adt])], input_dict | |
994 | ) | |
995 | assert result is True, "Test case {} : Failed \n Error: {}".format( | |
996 | tc_name, result | |
997 | ) | |
85d47773 AP |
998 | |
999 | input_dict_2 = { | |
1000 | "r1": { | |
1001 | "bgp": { | |
1002 | "address_family": { | |
1003 | "ipv4": { | |
1004 | "unicast": { | |
1005 | "advertise_networks": [ | |
1006 | { | |
787e7624 | 1007 | "network": "%s/%s" |
1008 | % (NETWORK["ipv4"][0], MASK["ipv4"]), | |
85d47773 | 1009 | "no_of_network": 1, |
787e7624 | 1010 | "delete": True, |
85d47773 AP |
1011 | } |
1012 | ] | |
1013 | } | |
1014 | }, | |
1015 | "ipv6": { | |
1016 | "unicast": { | |
1017 | "advertise_networks": [ | |
1018 | { | |
787e7624 | 1019 | "network": "%s/%s" |
1020 | % (NETWORK["ipv6"][0], MASK["ipv6"]), | |
85d47773 | 1021 | "no_of_network": 1, |
787e7624 | 1022 | "delete": True, |
85d47773 AP |
1023 | } |
1024 | ] | |
1025 | } | |
787e7624 | 1026 | }, |
85d47773 AP |
1027 | } |
1028 | } | |
1029 | } | |
1030 | } | |
1031 | ||
1032 | step("Stop advertising one of the networks") | |
1033 | result = create_router_bgp(tgen, topo, input_dict_2) | |
787e7624 | 1034 | assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result) |
85d47773 AP |
1035 | |
1036 | input_dict_3 = { | |
1037 | "community": STANDARD_COMM["agg_2"], | |
787e7624 | 1038 | "largeCommunity": LARGE_COMM["agg_2"], |
85d47773 AP |
1039 | } |
1040 | ||
1041 | for adt in ADDR_TYPES: | |
1042 | step("Verifying bgp community values on r5 is also modified") | |
787e7624 | 1043 | result = verify_bgp_community( |
1044 | tgen, adt, "r4", ["%s/%s" % (NETWORK[adt][2], NET_MASK[adt])], input_dict_3 | |
1045 | ) | |
1046 | assert result is True, "Test case {} : Failed \n Error: {}".format( | |
1047 | tc_name, result | |
1048 | ) | |
85d47773 AP |
1049 | |
1050 | write_test_footer(tc_name) | |
1051 | ||
1052 | ||
1053 | def test_large_community_boundary_values(request): | |
1054 | """ | |
1055 | Verify that any value in BGP Large communities for boundary values. | |
1056 | """ | |
1057 | tc_name = request.node.name | |
1058 | write_test_header(tc_name) | |
1059 | tgen = get_topogen() | |
1060 | ||
1061 | # Don"t run this test if we have any failure. | |
1062 | if tgen.routers_have_failure(): | |
1063 | pytest.skip(tgen.errors) | |
1064 | ||
1065 | input_dict = { | |
1066 | "r4": { | |
1067 | "bgp_community_lists": [ | |
1068 | { | |
1069 | "community_type": "standard", | |
1070 | "action": "permit", | |
1071 | "name": "ANY", | |
787e7624 | 1072 | "value": "0:-1", |
85d47773 AP |
1073 | } |
1074 | ] | |
1075 | } | |
1076 | } | |
1077 | ||
1078 | step("Checking boundary value for community 0:-1") | |
1079 | result = create_bgp_community_lists(tgen, input_dict) | |
787e7624 | 1080 | assert result is not True, "Test case {} : Failed \n Error: {}".format( |
1081 | tc_name, result | |
1082 | ) | |
85d47773 AP |
1083 | |
1084 | step("Checking community attribute 0:65536") | |
1085 | input_dict_2 = { | |
1086 | "r4": { | |
1087 | "bgp_community_lists": [ | |
1088 | { | |
1089 | "community_type": "standard", | |
1090 | "action": "permit", | |
1091 | "name": "ANY", | |
787e7624 | 1092 | "value": "0:65536", |
85d47773 AP |
1093 | } |
1094 | ] | |
1095 | } | |
1096 | } | |
1097 | ||
1098 | step("Checking boundary value for community 0:65536") | |
1099 | result = create_bgp_community_lists(tgen, input_dict_2) | |
787e7624 | 1100 | assert result is not True, "Test case {} : Failed \n Error: {}".format( |
1101 | tc_name, result | |
1102 | ) | |
85d47773 AP |
1103 | |
1104 | step("Checking boundary value for community 0:4294967296") | |
1105 | input_dict_3 = { | |
1106 | "r4": { | |
1107 | "bgp_community_lists": [ | |
1108 | { | |
1109 | "community_type": "standard", | |
1110 | "action": "permit", | |
1111 | "name": "ANY", | |
1112 | "value": "0:4294967296", | |
787e7624 | 1113 | "large": True, |
85d47773 AP |
1114 | } |
1115 | ] | |
1116 | } | |
1117 | } | |
1118 | ||
1119 | result = create_bgp_community_lists(tgen, input_dict_3) | |
787e7624 | 1120 | assert result is not True, "Test case {} : Failed \n Error: {}".format( |
1121 | tc_name, result | |
1122 | ) | |
85d47773 AP |
1123 | step("Checking boundary value for community 0:-1:1") |
1124 | ||
1125 | input_dict_4 = { | |
1126 | "r4": { | |
1127 | "bgp_community_lists": [ | |
1128 | { | |
1129 | "community_type": "standard", | |
1130 | "action": "permit", | |
1131 | "name": "ANY", | |
1132 | "value": "0:-1:1", | |
787e7624 | 1133 | "large": True, |
85d47773 AP |
1134 | } |
1135 | ] | |
1136 | } | |
1137 | } | |
1138 | ||
1139 | result = create_bgp_community_lists(tgen, input_dict_4) | |
787e7624 | 1140 | assert result is not True, "Test case {} : Failed \n Error: {}".format( |
1141 | tc_name, result | |
1142 | ) | |
85d47773 AP |
1143 | |
1144 | ||
c850908b WC |
1145 | def test_large_community_invalid_chars(request): |
1146 | """ | |
1147 | BGP canonical lcommunities must only be digits | |
1148 | """ | |
1149 | tc_name = request.node.name | |
1150 | write_test_header(tc_name) | |
1151 | tgen = get_topogen() | |
1152 | ||
1153 | # Don"t run this test if we have any failure. | |
1154 | if tgen.routers_have_failure(): | |
1155 | pytest.skip(tgen.errors) | |
1156 | ||
1157 | input_dict = { | |
1158 | "r4": { | |
1159 | "bgp_community_lists": [ | |
1160 | { | |
1161 | "community_type": "standard", | |
1162 | "action": "permit", | |
1163 | "name": "ANY", | |
1164 | "value": "1:a:2", | |
1165 | "large": True, | |
1166 | } | |
1167 | ] | |
1168 | } | |
1169 | } | |
1170 | ||
1171 | step("Checking boundary value for community 1:a:2") | |
1172 | result = create_bgp_community_lists(tgen, input_dict) | |
1173 | assert result is not True, "Test case {} : Failed \n Error: {}".format( | |
1174 | tc_name, result | |
1175 | ) | |
1176 | ||
1177 | ||
85d47773 AP |
1178 | def test_large_community_after_clear_bgp(request): |
1179 | """ | |
1180 | Clear BGP neighbor-ship and check if large community and community | |
1181 | attributes are getting re-populated. | |
1182 | """ | |
1183 | tc_name = request.node.name | |
1184 | write_test_header(tc_name) | |
1185 | tgen = get_topogen() | |
1186 | ||
1187 | # Don"t run this test if we have any failure. | |
1188 | if tgen.routers_have_failure(): | |
1189 | pytest.skip(tgen.errors) | |
1190 | ||
1191 | reset_config_on_routers(tgen) | |
1192 | config_router_r1(tgen, topo, tc_name) | |
1193 | ||
787e7624 | 1194 | input_dict = {"largeCommunity": LARGE_COMM["r1"], "community": STANDARD_COMM["r1"]} |
85d47773 AP |
1195 | |
1196 | for adt in ADDR_TYPES: | |
787e7624 | 1197 | result = verify_bgp_community(tgen, adt, "r2", [NETWORK[adt][0]], input_dict) |
1198 | assert result is True, "Test case {} : Failed \n Error: {}".format( | |
1199 | tc_name, result | |
1200 | ) | |
85d47773 AP |
1201 | |
1202 | step("Clearing BGP on r1") | |
1203 | clear_bgp_and_verify(tgen, topo, "r1") | |
1204 | ||
1205 | for adt in ADDR_TYPES: | |
787e7624 | 1206 | result = verify_bgp_community(tgen, adt, "r2", [NETWORK[adt][0]], input_dict) |
1207 | assert result is True, "Test case {} : Failed \n Error: {}".format( | |
1208 | tc_name, result | |
1209 | ) | |
85d47773 AP |
1210 | |
1211 | write_test_footer(tc_name) | |
1212 | ||
1213 | ||
1214 | if __name__ == "__main__": | |
1215 | args = ["-s"] + sys.argv[1:] | |
1216 | sys.exit(pytest.main(args)) |