]> git.proxmox.com Git - mirror_frr.git/blob - tests/topotests/bgp_distance_change/test_bgp_admin_dist_vrf.py
doc: Add `show ipv6 rpf X:X::X:X` command to docs
[mirror_frr.git] / tests / topotests / bgp_distance_change / test_bgp_admin_dist_vrf.py
1 #!/usr/bin/env python
2
3 #
4 # Copyright (c) 2022 by VMware, Inc. ("VMware")
5 # Used Copyright (c) 2018 by Network Device Education Foundation,
6 # Inc. ("NetDEF") in this file.
7 #
8 # Permission to use, copy, modify, and/or distribute this software
9 # for any purpose with or without fee is hereby granted, provided
10 # that the above copyright notice and this permission notice appear
11 # in all copies.
12 #
13 # THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES
14 # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15 # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR
16 # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
17 # DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
18 # WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
19 # ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
20 # OF THIS SOFTWARE.
21 #
22
23 import sys
24 import time
25 import pytest
26 import inspect
27 import os
28
29 """Following tests are covered to test bgp admin distance functionality.
30 TC_5:
31 Verify bgp admin distance functionality when static route is configured
32 same as bgp learnt route in user vrf.
33
34 TC_6: Verify bgp admin distance functionality with ECMP in user vrf.
35
36 TC_7:
37 Verify bgp admin distance functionality when routes are
38 imported between VRFs.
39 """
40
41 #################################
42 # TOPOLOGY
43 #################################
44 """
45
46 +-------+
47 +--------- | R2 |
48 | +-------+
49 |iBGP |
50 +-------+ |
51 | R1 | |iBGP
52 +-------+ |
53 | |
54 | iBGP +-------+ eBGP +-------+
55 +---------- | R3 |----------| R4 |
56 +-------+ +-------+
57 |
58 |eBGP
59 |
60 +-------+
61 | R5 |
62 +-------+
63
64
65 """
66
67 # Save the Current Working Directory to find configuration files.
68 CWD = os.path.dirname(os.path.realpath(__file__))
69 sys.path.append(os.path.join(CWD, "../"))
70 sys.path.append(os.path.join(CWD, "../lib/"))
71
72 # pylint: disable=C0413
73 # Import topogen and topotest helpers
74 from lib.topogen import Topogen, get_topogen
75
76 # Required to instantiate the topology builder class.
77 from lib.common_config import (
78 start_topology,
79 write_test_header,
80 step,
81 write_test_footer,
82 create_static_routes,
83 verify_rib,
84 check_address_types,
85 reset_config_on_routers,
86 check_router_status,
87 )
88 from lib.topolog import logger
89 from lib.bgp import (
90 verify_bgp_convergence,
91 create_router_bgp,
92 verify_best_path_as_per_admin_distance,
93 )
94
95 # pylint: disable=C0413
96 # Import topogen and topotest helpers
97 from lib.topogen import Topogen, get_topogen
98 from lib.topojson import build_config_from_json
99 from lib.topolog import logger
100
101 # Global variables
102 topo = None
103 bgp_convergence = False
104 pytestmark = [pytest.mark.bgpd, pytest.mark.staticd]
105
106 NETWORK = {
107 "ipv4": [
108 "192.168.20.1/32",
109 "192.168.20.2/32",
110 "192.168.21.1/32",
111 "192.168.21.2/32",
112 "192.168.22.1/32",
113 "192.168.22.2/32",
114 ],
115 "ipv6": [
116 "fc07:50::1/128",
117 "fc07:50::2/128",
118 "fc07:150::1/128",
119 "fc07:150::2/128",
120 "fc07:1::1/128",
121 "fc07:1::2/128",
122 ],
123 }
124 ADDR_TYPES = check_address_types()
125
126
127 def setup_module(mod):
128 """
129 Sets up the pytest environment
130
131 * `mod`: module name
132 """
133
134 global topo
135
136 testsuite_run_time = time.asctime(time.localtime(time.time()))
137 logger.info("Testsuite start time: {}".format(testsuite_run_time))
138 logger.info("=" * 40)
139
140 logger.info("Running setup_module to create topology")
141
142 # This function initiates the topology build with Topogen...
143 json_file = "{}/bgp_admin_dist_vrf.json".format(CWD)
144 tgen = Topogen(json_file, mod.__name__)
145 global topo
146 topo = tgen.json_topo
147
148 # Starting topology, create tmp files which are loaded to routers
149 # to start deamons and then start routers
150 start_topology(tgen)
151
152 # Creating configuration from JSON
153 build_config_from_json(tgen, topo)
154
155 # Checking BGP convergence
156 global bgp_convergence
157 global ADDR_TYPES
158
159 # Don't run this test if we have any failure.
160 if tgen.routers_have_failure():
161 pytest.skip(tgen.errors)
162
163 # Api call verify whether BGP is converged
164 bgp_convergence = verify_bgp_convergence(tgen, topo)
165 assert bgp_convergence is True, "setup_module :Failed \n Error:" " {}".format(
166 bgp_convergence
167 )
168 logger.info("Running setup_module() done")
169
170
171 def teardown_module(mod):
172 """teardown_module.
173
174 Teardown the pytest environment.
175 * `mod`: module name
176 """
177 logger.info("Running teardown_module to delete topology")
178 tgen = get_topogen()
179
180 # Stop toplogy and Remove tmp files
181 tgen.stop_topology()
182
183 logger.info(
184 "Testsuite end time: {}".format(time.asctime(time.localtime(time.time())))
185 )
186 logger.info("=" * 40)
187
188
189 #####################################################
190 # Tests starting
191 #####################################################
192
193
194 def test_bgp_admin_distance_ebgp_vrf_p0():
195 """
196 TC: 5
197 Verify bgp admin distance functionality when static route is
198 configured same as ebgp learnt route
199 """
200 tgen = get_topogen()
201 global bgp_convergence
202
203 if bgp_convergence is not True:
204 pytest.skip("skipping test case because of BGP Convergence failure at setup")
205
206 # test case name
207 tc_name = inspect.stack()[0][3]
208 write_test_header(tc_name)
209 if tgen.routers_have_failure():
210 check_router_status(tgen)
211
212 step("Configure base config as per the topology")
213 reset_config_on_routers(tgen)
214
215 step("Configure bgp admin distance 200 with CLI in dut.")
216
217 input_dict_1 = {
218 "r3": {
219 "bgp": [
220 {
221 "vrf": "RED",
222 "local_as": 100,
223 "address_family": {
224 "ipv4": {
225 "unicast": {
226 "distance": {"ebgp": 200, "ibgp": 200, "local": 200}
227 }
228 },
229 "ipv6": {
230 "unicast": {
231 "distance": {"ebgp": 200, "ibgp": 200, "local": 200}
232 }
233 },
234 },
235 }
236 ]
237 }
238 }
239
240 result = create_router_bgp(tgen, topo, input_dict_1)
241 assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
242
243 step("Verify bgp routes have admin distance of 200 in dut.")
244 # Verifying best path
245 dut = "r3"
246 attribute = "admin_distance"
247
248 input_dict = {
249 "ipv4": {
250 "r3": {
251 "static_routes": [
252 {
253 "network": NETWORK["ipv4"][0],
254 "admin_distance": 200,
255 "vrf": "RED",
256 },
257 {
258 "network": NETWORK["ipv4"][1],
259 "admin_distance": 200,
260 "vrf": "RED",
261 },
262 ]
263 }
264 },
265 "ipv6": {
266 "r3": {
267 "static_routes": [
268 {
269 "network": NETWORK["ipv6"][0],
270 "admin_distance": 200,
271 "vrf": "RED",
272 },
273 {
274 "network": NETWORK["ipv6"][1],
275 "admin_distance": 200,
276 "vrf": "RED",
277 },
278 ]
279 }
280 },
281 }
282
283 for addr_type in ADDR_TYPES:
284 result = verify_best_path_as_per_admin_distance(
285 tgen, addr_type, dut, input_dict[addr_type], attribute, vrf="RED"
286 )
287 assert result is True, "Testcase {} : Failed \n Error: {}".format(
288 tc_name, result
289 )
290
291 step("Modify the admin distance value to 150.")
292
293 input_dict_1 = {
294 "r3": {
295 "bgp": [
296 {
297 "local_as": 100,
298 "address_family": {
299 "ipv4": {
300 "unicast": {
301 "distance": {"ebgp": 150, "ibgp": 150, "local": 150}
302 }
303 },
304 "ipv6": {
305 "unicast": {
306 "distance": {"ebgp": 150, "ibgp": 150, "local": 150}
307 }
308 },
309 },
310 }
311 ]
312 }
313 }
314
315 result = create_router_bgp(tgen, topo, input_dict_1)
316 assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
317
318 step("Verify bgp routes have admin distance of 150 in dut.")
319 # Verifying best path
320 dut = "r3"
321 attribute = "admin_distance"
322
323 input_dict = {
324 "ipv4": {
325 "r3": {
326 "static_routes": [
327 {
328 "network": NETWORK["ipv4"][0],
329 "admin_distance": 150,
330 "vrf": "RED",
331 },
332 {
333 "network": NETWORK["ipv4"][1],
334 "admin_distance": 150,
335 "vrf": "RED",
336 },
337 ]
338 }
339 },
340 "ipv6": {
341 "r3": {
342 "static_routes": [
343 {
344 "network": NETWORK["ipv6"][0],
345 "admin_distance": 150,
346 "vrf": "RED",
347 },
348 {
349 "network": NETWORK["ipv6"][1],
350 "admin_distance": 150,
351 "vrf": "RED",
352 },
353 ]
354 }
355 },
356 }
357
358 for addr_type in ADDR_TYPES:
359 result = verify_best_path_as_per_admin_distance(
360 tgen, addr_type, dut, input_dict[addr_type], attribute, vrf="RED"
361 )
362 assert result is True, "Testcase {} : Failed \n Error: {}".format(
363 tc_name, result
364 )
365
366 step("Un configure the admin distance value on DUT")
367
368 input_dict_1 = {
369 "r3": {
370 "bgp": [
371 {
372 "local_as": 100,
373 "address_family": {
374 "ipv4": {
375 "unicast": {
376 "distance": {
377 "ebgp": 150,
378 "ibgp": 150,
379 "local": 150,
380 "delete": True,
381 }
382 }
383 },
384 "ipv6": {
385 "unicast": {
386 "distance": {
387 "ebgp": 150,
388 "ibgp": 150,
389 "local": 150,
390 "delete": True,
391 }
392 }
393 },
394 },
395 }
396 ]
397 }
398 }
399
400 result = create_router_bgp(tgen, topo, input_dict_1)
401 assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
402
403 step("Verify bgp routes have default admin distance in dut.")
404 # Verifying best path
405 dut = "r3"
406 attribute = "admin_distance"
407
408 input_dict = {
409 "ipv4": {
410 "r3": {
411 "static_routes": [
412 {"network": NETWORK["ipv4"][0], "admin_distance": 20, "vrf": "RED"},
413 {"network": NETWORK["ipv4"][1], "admin_distance": 20, "vrf": "RED"},
414 ]
415 }
416 },
417 "ipv6": {
418 "r3": {
419 "static_routes": [
420 {"network": NETWORK["ipv6"][0], "admin_distance": 20, "vrf": "RED"},
421 {"network": NETWORK["ipv6"][1], "admin_distance": 20, "vrf": "RED"},
422 ]
423 }
424 },
425 }
426
427 for addr_type in ADDR_TYPES:
428 result = verify_best_path_as_per_admin_distance(
429 tgen, addr_type, dut, input_dict[addr_type], attribute, vrf="RED"
430 )
431 assert result is True, "Testcase {} : Failed \n Error: {}".format(
432 tc_name, result
433 )
434
435 step("Configure static route Without any admin distance")
436
437 for addr_type in ADDR_TYPES:
438 # Create Static routes
439 input_dict = {
440 "r3": {
441 "static_routes": [
442 {"network": NETWORK[addr_type], "next_hop": "Null0", "vrf": "RED"}
443 ]
444 }
445 }
446
447 result = create_static_routes(tgen, input_dict)
448 assert result is True, "Testcase {} : Failed \n Error: {}".format(
449 tc_name, result
450 )
451
452 step("Verify that zebra selects static route.")
453 protocol = "static"
454 # dual stack changes
455 for addr_type in ADDR_TYPES:
456 input_dict = {
457 "r3": {
458 "static_routes": [
459 {"network": NETWORK[addr_type], "next_hop": "Null0", "vrf": "RED"}
460 ]
461 }
462 }
463 result4 = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol)
464 assert result4 is True, "Testcase {} : Failed \n Error: {}".format(
465 tc_name, result4
466 )
467
468 step("Configure static route with admin distance of 253")
469 for addr_type in ADDR_TYPES:
470 # Create Static routes
471 input_dict = {
472 "r3": {
473 "static_routes": [
474 {
475 "network": NETWORK[addr_type],
476 "next_hop": "Null0",
477 "admin_distance": 253,
478 "vrf": "RED",
479 }
480 ]
481 }
482 }
483
484 result = create_static_routes(tgen, input_dict)
485 assert result is True, "Testcase {} : Failed \n Error: {}".format(
486 tc_name, result
487 )
488
489 step("Verify that zebra selects bgp route.")
490 protocol = "bgp"
491
492 for addr_type in ADDR_TYPES:
493 input_dict = {
494 "r3": {
495 "static_routes": [
496 {
497 "network": NETWORK[addr_type],
498 "next_hop": "Null0",
499 "admin_distance": 253,
500 "vrf": "RED",
501 }
502 ]
503 }
504 }
505 result4 = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol)
506 assert result4 is True, "Testcase {} : Failed \n Error: {}".format(
507 tc_name, result4
508 )
509
510 step("Configure admin distance of 254 in bgp for route .")
511
512 input_dict_1 = {
513 "r3": {
514 "bgp": [
515 {
516 "local_as": 100,
517 "address_family": {
518 "ipv4": {
519 "unicast": {
520 "distance": {"ebgp": 254, "ibgp": 254, "local": 254}
521 }
522 },
523 "ipv6": {
524 "unicast": {
525 "distance": {"ebgp": 254, "ibgp": 254, "local": 254}
526 }
527 },
528 },
529 }
530 ]
531 }
532 }
533
534 result = create_router_bgp(tgen, topo, input_dict_1)
535 assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
536
537 step("Verify that zebra selects static route.")
538 protocol = "static"
539 # dual stack changes
540 for addr_type in ADDR_TYPES:
541 input_dict = {
542 "r3": {
543 "static_routes": [
544 {
545 "network": NETWORK[addr_type],
546 "next_hop": "Null0",
547 "admin_distance": 253,
548 "vrf": "RED",
549 }
550 ]
551 }
552 }
553
554 result4 = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol)
555 assert result4 is True, "Testcase {} : Failed \n Error: {}".format(
556 tc_name, result4
557 )
558
559 step("Configure admin distance of 255 in bgp for route in vrf red")
560
561 input_dict_1 = {
562 "r3": {
563 "bgp": [
564 {
565 "local_as": 100,
566 "address_family": {
567 "ipv4": {
568 "unicast": {
569 "distance": {"ebgp": 255, "ibgp": 255, "local": 255}
570 }
571 },
572 "ipv6": {
573 "unicast": {
574 "distance": {"ebgp": 255, "ibgp": 255, "local": 255}
575 }
576 },
577 },
578 }
579 ]
580 }
581 }
582
583 result = create_router_bgp(tgen, topo, input_dict_1)
584 assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
585
586 step("Verify that zebra selects static route.")
587 protocol = "static"
588 # dual stack changes
589 for addr_type in ADDR_TYPES:
590 input_dict = {
591 "r3": {
592 "static_routes": [
593 {
594 "network": NETWORK[addr_type],
595 "next_hop": "Null0",
596 "admin_distance": 253,
597 "vrf": "RED",
598 }
599 ]
600 }
601 }
602
603 result4 = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol)
604 assert result4 is True, "Testcase {} : Failed \n Error: {}".format(
605 tc_name, result4
606 )
607
608 step("Delete the static route.")
609 for addr_type in ADDR_TYPES:
610 # Create Static routes
611 input_dict = {
612 "r3": {
613 "static_routes": [
614 {
615 "network": NETWORK[addr_type],
616 "next_hop": "Null0",
617 "admin_distance": 253,
618 "delete": True,
619 "vrf": "RED",
620 }
621 ]
622 }
623 }
624
625 result = create_static_routes(tgen, input_dict)
626 assert result is True, "Testcase {} : Failed \n Error: {}".format(
627 tc_name, result
628 )
629
630 step("Verify that zebra selects bgp route.")
631 protocol = "bgp"
632 # dual stack changes
633 for addr_type in ADDR_TYPES:
634 result4 = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol)
635 assert result4 is True, "Testcase {} : Failed \n Error: {}".format(
636 tc_name, result4
637 )
638
639 write_test_footer(tc_name)
640
641
642 def test_bgp_admin_distance_ebgp_with_imported_rtes_vrf_p0():
643 """
644 TC: 5
645 Verify bgp admin distance functionality when static route is configured
646 same as bgp learnt route in user vrf.
647 """
648 tgen = get_topogen()
649 global bgp_convergence
650
651 if bgp_convergence is not True:
652 pytest.skip("skipping test case because of BGP Convergence failure at setup")
653
654 # test case name
655 tc_name = inspect.stack()[0][3]
656 write_test_header(tc_name)
657 if tgen.routers_have_failure():
658 check_router_status(tgen)
659
660 step("Configure base config as per the topology")
661 reset_config_on_routers(tgen)
662 step("Configure bgp admin distance 200 with CLI in dut.")
663 step(" Import route from vrf to default vrf")
664 input_dict_1 = {
665 "r3": {
666 "bgp": [
667 {
668 "vrf": "RED",
669 "local_as": 100,
670 "address_family": {
671 "ipv4": {
672 "unicast": {
673 "distance": {"ebgp": 200, "ibgp": 200, "local": 200}
674 }
675 },
676 "ipv6": {
677 "unicast": {
678 "distance": {"ebgp": 200, "ibgp": 200, "local": 200}
679 }
680 },
681 },
682 },
683 {
684 "local_as": 100,
685 "address_family": {
686 "ipv4": {
687 "unicast": {
688 "distance": {"ebgp": 200, "ibgp": 200, "local": 200},
689 "import": {"vrf": "RED"},
690 }
691 },
692 "ipv6": {
693 "unicast": {
694 "distance": {"ebgp": 200, "ibgp": 200, "local": 200},
695 "import": {
696 "vrf": "RED",
697 },
698 }
699 },
700 },
701 },
702 ]
703 }
704 }
705
706 result = create_router_bgp(tgen, topo, input_dict_1)
707 assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
708
709 step("Verify bgp routes have admin distance of 200 in dut.")
710 # Verifying best path
711 dut = "r3"
712 attribute = "admin_distance"
713
714 input_dict = {
715 "ipv4": {
716 "r3": {
717 "static_routes": [
718 {
719 "network": NETWORK["ipv4"][0],
720 "admin_distance": 200,
721 "vrf": "RED",
722 },
723 {
724 "network": NETWORK["ipv4"][1],
725 "admin_distance": 200,
726 "vrf": "RED",
727 },
728 ]
729 }
730 },
731 "ipv6": {
732 "r3": {
733 "static_routes": [
734 {
735 "network": NETWORK["ipv6"][0],
736 "admin_distance": 200,
737 "vrf": "RED",
738 },
739 {
740 "network": NETWORK["ipv6"][1],
741 "admin_distance": 200,
742 "vrf": "RED",
743 },
744 ]
745 }
746 },
747 }
748
749 for addr_type in ADDR_TYPES:
750 result = verify_best_path_as_per_admin_distance(
751 tgen, addr_type, dut, input_dict[addr_type], attribute, vrf="RED"
752 )
753 assert result is True, "Testcase {} : Failed \n Error: {}".format(
754 tc_name, result
755 )
756
757 step(
758 "Verify that routes are getting imported without any issues and "
759 "routes are calculated and installed in rib."
760 )
761
762 input_dict = {
763 "ipv4": {
764 "r3": {
765 "static_routes": [
766 {
767 "network": NETWORK["ipv4"][0],
768 "admin_distance": 200,
769 },
770 {
771 "network": NETWORK["ipv4"][1],
772 "admin_distance": 200,
773 },
774 ]
775 }
776 },
777 "ipv6": {
778 "r3": {
779 "static_routes": [
780 {
781 "network": NETWORK["ipv6"][0],
782 "admin_distance": 200,
783 },
784 {
785 "network": NETWORK["ipv6"][1],
786 "admin_distance": 200,
787 },
788 ]
789 }
790 },
791 }
792
793 step("Verify that zebra selects bgp route.")
794 protocol = "bgp"
795 # dual stack changes
796 for addr_type in ADDR_TYPES:
797 result4 = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol)
798 assert result4 is True, "Testcase {} : Failed \n Error: {}".format(
799 tc_name, result4
800 )
801
802 step(" Un configure import route vrf red inside default vrf.")
803 input_dict_1 = {
804 "r3": {
805 "bgp": [
806 {
807 "vrf": "RED",
808 "local_as": 100,
809 "address_family": {
810 "ipv4": {
811 "unicast": {
812 "distance": {"ebgp": 200, "ibgp": 200, "local": 200}
813 }
814 },
815 "ipv6": {
816 "unicast": {
817 "distance": {"ebgp": 200, "ibgp": 200, "local": 200}
818 }
819 },
820 },
821 },
822 {
823 "local_as": 100,
824 "address_family": {
825 "ipv4": {
826 "unicast": {
827 "distance": {"ebgp": 200, "ibgp": 200, "local": 200},
828 "import": {"vrf": "RED", "delete": True},
829 }
830 },
831 "ipv6": {
832 "unicast": {
833 "distance": {"ebgp": 200, "ibgp": 200, "local": 200},
834 "import": {"vrf": "RED", "delete": True},
835 }
836 },
837 },
838 },
839 ]
840 }
841 }
842
843 result = create_router_bgp(tgen, topo, input_dict_1)
844 assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
845
846 input_dict = {
847 "ipv4": {
848 "r3": {
849 "static_routes": [
850 {
851 "network": NETWORK["ipv4"][0],
852 "admin_distance": 200,
853 },
854 {
855 "network": NETWORK["ipv4"][1],
856 "admin_distance": 200,
857 },
858 ]
859 }
860 },
861 "ipv6": {
862 "r3": {
863 "static_routes": [
864 {
865 "network": NETWORK["ipv6"][0],
866 "admin_distance": 200,
867 },
868 {
869 "network": NETWORK["ipv6"][1],
870 "admin_distance": 200,
871 },
872 ]
873 }
874 },
875 }
876
877 step("Verify that route withdrawal happens properly.")
878 protocol = "bgp"
879 # dual stack changes
880 for addr_type in ADDR_TYPES:
881 result4 = verify_rib(
882 tgen,
883 addr_type,
884 dut,
885 input_dict[addr_type],
886 protocol=protocol,
887 expected=False,
888 )
889 assert (
890 result4 is not True
891 ), "Testcase {} : Failed \n Route is not withdrawn. Error: {}".format(
892 tc_name, result4
893 )
894
895 write_test_footer(tc_name)
896
897
898 if __name__ == "__main__":
899 args = ["-s"] + sys.argv[1:]
900 sys.exit(pytest.main(args))