]>
Commit | Line | Data |
---|---|---|
da78f4fe | 1 | #!/usr/bin/env python |
acddc0ed | 2 | # SPDX-License-Identifier: ISC |
da78f4fe G |
3 | |
4 | # | |
5 | # test_bfd_ospf_topo1.py | |
6 | # Part of NetDEF Topology Tests | |
7 | # | |
8 | # Copyright (c) 2020 by | |
9 | # Network Device Education Foundation, Inc. ("NetDEF") | |
10 | # | |
da78f4fe G |
11 | |
12 | """ | |
13 | test_bfd_ospf_topo1.py: | |
14 | ||
15 | +---------+ | |
16 | | | | |
17 | eth-rt2 (.1) | RT1 | eth-rt3 (.1) | |
18 | +----------+ 1.1.1.1 +----------+ | |
19 | | | | | | |
20 | | +---------+ | | |
21 | | | | |
22 | | 10.0.2.0/24 | | |
23 | | | | |
24 | | eth-rt1 | (.2) | |
25 | | 10.0.1.0/24 +----+----+ | |
26 | | | | | |
27 | | | RT3 | | |
28 | | | 3.3.3.3 | | |
29 | | | | | |
30 | (.2) | eth-rt1 +----+----+ | |
31 | +----+----+ eth-rt4 | (.1) | |
32 | | | | | |
33 | | RT2 | | | |
34 | | 2.2.2.2 | 10.0.4.0/24 | | |
35 | | | | | |
36 | +----+----+ | | |
37 | (.1) | eth-rt5 eth-rt3 | (.2) | |
38 | | +----+----+ | |
39 | | | | | |
40 | | | RT4 | | |
41 | | | 4.4.4.4 | | |
42 | | | | | |
43 | | +----+----+ | |
44 | | 10.0.3.0/24 eth-rt5 | (.1) | |
45 | | | | |
46 | | | | |
47 | | 10.0.5.0/24 | | |
48 | | | | |
49 | | +---------+ | | |
50 | | | | | | |
51 | +----------+ RT5 +----------+ | |
52 | eth-rt2 (.2) | 5.5.5.5 | eth-rt4 (.2) | |
53 | | | | |
54 | +---------+ | |
55 | ||
56 | """ | |
57 | ||
58 | import os | |
59 | import sys | |
60 | import pytest | |
61 | import json | |
da78f4fe | 62 | from time import sleep |
da78f4fe G |
63 | from functools import partial |
64 | ||
65 | # Save the Current Working Directory to find configuration files. | |
66 | CWD = os.path.dirname(os.path.realpath(__file__)) | |
67 | sys.path.append(os.path.join(CWD, "../")) | |
68 | ||
69 | # pylint: disable=C0413 | |
70 | # Import topogen and topotest helpers | |
71 | from lib import topotest | |
72 | from lib.topogen import Topogen, TopoRouter, get_topogen | |
73 | from lib.topolog import logger | |
74 | ||
3dedee4f | 75 | pytestmark = [pytest.mark.bfdd, pytest.mark.ospfd] |
da78f4fe | 76 | |
5980ad0a | 77 | |
da78f4fe G |
78 | def setup_module(mod): |
79 | "Sets up the pytest environment" | |
8db751b8 CH |
80 | topodef = { |
81 | "s1": ("rt1:eth-rt2", "rt2:eth-rt1"), | |
82 | "s2": ("rt1:eth-rt3", "rt3:eth-rt1"), | |
83 | "s3": ("rt2:eth-rt5", "rt5:eth-rt2"), | |
84 | "s4": ("rt3:eth-rt4", "rt4:eth-rt3"), | |
85 | "s5": ("rt4:eth-rt5", "rt5:eth-rt4"), | |
86 | } | |
87 | tgen = Topogen(topodef, mod.__name__) | |
da78f4fe G |
88 | tgen.start_topology() |
89 | ||
90 | router_list = tgen.routers() | |
91 | ||
92 | # For all registered routers, load the zebra configuration file | |
8db751b8 | 93 | for rname, router in router_list.items(): |
da78f4fe G |
94 | router.load_config( |
95 | TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) | |
96 | ) | |
97 | router.load_config( | |
98 | TopoRouter.RD_BFD, os.path.join(CWD, "{}/bfdd.conf".format(rname)) | |
99 | ) | |
100 | router.load_config( | |
101 | TopoRouter.RD_OSPF, os.path.join(CWD, "{}/ospfd.conf".format(rname)) | |
102 | ) | |
103 | router.load_config( | |
104 | TopoRouter.RD_OSPF6, os.path.join(CWD, "{}/ospf6d.conf".format(rname)) | |
105 | ) | |
106 | ||
107 | tgen.start_router() | |
108 | ||
109 | ||
110 | def teardown_module(mod): | |
111 | "Teardown the pytest environment" | |
112 | tgen = get_topogen() | |
113 | ||
114 | # This function tears down the whole topology. | |
115 | tgen.stop_topology() | |
116 | ||
117 | ||
118 | def print_cmd_result(rname, command): | |
119 | print(get_topogen().gears[rname].vtysh_cmd(command, isjson=False)) | |
120 | ||
121 | ||
8ff24fd2 | 122 | def router_compare_json_output(rname, command, reference, count=40, wait=2): |
da78f4fe G |
123 | "Compare router JSON output" |
124 | ||
125 | logger.info('Comparing router "%s" "%s" output', rname, command) | |
126 | ||
127 | tgen = get_topogen() | |
128 | filename = "{}/{}/{}".format(CWD, rname, reference) | |
129 | expected = json.loads(open(filename).read()) | |
130 | ||
8ff24fd2 | 131 | # Run test function until we get an result. Wait at most 80 seconds. |
da78f4fe G |
132 | test_func = partial(topotest.router_json_cmp, tgen.gears[rname], command, expected) |
133 | _, diff = topotest.run_and_expect(test_func, None, count=count, wait=wait) | |
134 | assertmsg = '"{}" JSON output mismatches the expected result'.format(rname) | |
135 | assert diff is None, assertmsg | |
136 | ||
137 | ||
138 | ## TEST STEPS | |
139 | ||
140 | ||
141 | def test_rib_ospf_step1(): | |
142 | logger.info("Test (step 1): verify RIB for OSPF") | |
143 | tgen = get_topogen() | |
144 | ||
145 | # Skip if previous fatal error condition is raised | |
146 | if tgen.routers_have_failure(): | |
147 | pytest.skip(tgen.errors) | |
148 | ||
149 | router_compare_json_output( | |
150 | "rt1", "show ip route ospf json", "step1/show_ip_route.ref" | |
151 | ) | |
152 | router_compare_json_output( | |
153 | "rt1", "show ipv6 route ospf json", "step1/show_ipv6_route.ref" | |
154 | ) | |
155 | ||
156 | ||
157 | def test_bfd_ospf_sessions_step2(): | |
158 | logger.info("Test (step 2): verify BFD peers for OSPF") | |
159 | tgen = get_topogen() | |
160 | ||
161 | # Skip if previous fatal error condition is raised | |
162 | if tgen.routers_have_failure(): | |
163 | pytest.skip(tgen.errors) | |
164 | ||
165 | # BFD is just used on three routers | |
166 | for rt in ["rt1", "rt2", "rt3"]: | |
167 | router_compare_json_output( | |
168 | rt, "show bfd peers json", "step2/show_bfd_peers.ref" | |
169 | ) | |
170 | ||
171 | ||
172 | def test_bfd_ospf_interface_failure_rt2_step3(): | |
173 | logger.info("Test (step 3): Check failover handling with RT2 down") | |
174 | tgen = get_topogen() | |
175 | ||
176 | # Skip if previous fatal error condition is raised | |
177 | if tgen.routers_have_failure(): | |
178 | pytest.skip(tgen.errors) | |
179 | ||
180 | # Let's kill the interface on rt2 and see what happens with the RIB and BFD on rt1 | |
181 | tgen.gears["rt2"].link_enable("eth-rt1", enabled=False) | |
182 | ||
183 | # By default BFD provides a recovery time of 900ms plus jitter, so let's wait | |
184 | # initial 2 seconds to let the CI not suffer. | |
7b66f10e | 185 | topotest.sleep(2, "Wait for BFD down notification") |
2d28cbe6 | 186 | |
da78f4fe | 187 | router_compare_json_output( |
f786c3ee | 188 | "rt1", "show ip route ospf json", "step3/show_ip_route_rt2_down.ref", 10, 2 |
da78f4fe G |
189 | ) |
190 | router_compare_json_output( | |
7b66f10e | 191 | "rt1", "show ipv6 route ospf json", "step3/show_ipv6_route_rt2_down.ref", 10, 2 |
da78f4fe G |
192 | ) |
193 | router_compare_json_output( | |
7b66f10e | 194 | "rt1", "show bfd peers json", "step3/show_bfd_peers_rt2_down.ref", 10, 2 |
da78f4fe G |
195 | ) |
196 | ||
197 | # Check recovery, this can take some time | |
198 | tgen.gears["rt2"].link_enable("eth-rt1", enabled=True) | |
199 | ||
200 | router_compare_json_output( | |
201 | "rt1", "show ip route ospf json", "step3/show_ip_route_healthy.ref" | |
202 | ) | |
203 | router_compare_json_output( | |
204 | "rt1", "show ipv6 route ospf json", "step3/show_ipv6_route_healthy.ref" | |
205 | ) | |
206 | router_compare_json_output( | |
207 | "rt1", "show bfd peers json", "step3/show_bfd_peers_healthy.ref" | |
208 | ) | |
209 | ||
210 | ||
211 | def test_bfd_ospf_interface_failure_rt3_step3(): | |
212 | logger.info("Test (step 3): Check failover handling with RT3 down") | |
213 | tgen = get_topogen() | |
214 | ||
215 | # Skip if previous fatal error condition is raised | |
216 | if tgen.routers_have_failure(): | |
217 | pytest.skip(tgen.errors) | |
218 | ||
219 | # Let's kill the interface on rt3 and see what happens with the RIB and BFD on rt1 | |
220 | tgen.gears["rt3"].link_enable("eth-rt1", enabled=False) | |
221 | ||
222 | # By default BFD provides a recovery time of 900ms plus jitter, so let's wait | |
223 | # initial 2 seconds to let the CI not suffer. | |
7b66f10e | 224 | topotest.sleep(2, "Wait for BFD down notification") |
da78f4fe | 225 | router_compare_json_output( |
f786c3ee | 226 | "rt1", "show ip route ospf json", "step3/show_ip_route_rt3_down.ref", 10, 2 |
da78f4fe G |
227 | ) |
228 | router_compare_json_output( | |
7b66f10e | 229 | "rt1", "show ipv6 route ospf json", "step3/show_ipv6_route_rt3_down.ref", 10, 2 |
da78f4fe G |
230 | ) |
231 | router_compare_json_output( | |
7b66f10e | 232 | "rt1", "show bfd peers json", "step3/show_bfd_peers_rt3_down.ref", 10, 2 |
da78f4fe G |
233 | ) |
234 | ||
235 | # Check recovery, this can take some time | |
236 | tgen.gears["rt3"].link_enable("eth-rt1", enabled=True) | |
237 | ||
238 | router_compare_json_output( | |
239 | "rt1", "show ip route ospf json", "step3/show_ip_route_healthy.ref" | |
240 | ) | |
241 | router_compare_json_output( | |
242 | "rt1", "show ipv6 route ospf json", "step3/show_ipv6_route_healthy.ref" | |
243 | ) | |
244 | router_compare_json_output( | |
245 | "rt1", "show bfd peers json", "step3/show_bfd_peers_healthy.ref" | |
246 | ) | |
247 | ||
248 | ||
249 | def test_memory_leak(): | |
250 | "Run the memory leak test and report results." | |
251 | tgen = get_topogen() | |
252 | if not tgen.is_memleak_enabled(): | |
253 | pytest.skip("Memory leak test/report is disabled") | |
254 | ||
255 | tgen.report_memory_leaks() | |
256 | ||
257 | ||
258 | if __name__ == "__main__": | |
259 | args = ["-s"] + sys.argv[1:] | |
260 | sys.exit(pytest.main(args)) |