]>
Commit | Line | Data |
---|---|---|
4256a209 | 1 | #!/usr/bin/python |
2 | ||
3 | # | |
4 | # Copyright (c) 2020 by VMware, Inc. ("VMware") | |
5 | # Used Copyright (c) 2018 by Network Device Education Foundation, Inc. | |
6 | # ("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 | ||
24 | """OSPF Basic Functionality Automation.""" | |
25 | import os | |
26 | import sys | |
27 | import time | |
28 | import pytest | |
29 | import json | |
4256a209 | 30 | |
31 | # Save the Current Working Directory to find configuration files. | |
32 | CWD = os.path.dirname(os.path.realpath(__file__)) | |
33 | sys.path.append(os.path.join(CWD, "../")) | |
34 | sys.path.append(os.path.join(CWD, "../lib/")) | |
35 | ||
36 | # pylint: disable=C0413 | |
37 | # Import topogen and topotest helpers | |
8db751b8 | 38 | from lib.micronet_compat import Topo |
4256a209 | 39 | from lib.topogen import Topogen, get_topogen |
40 | ||
41 | # Import topoJson from lib, to create topology and initial configuration | |
42 | from lib.common_config import ( | |
43 | start_topology, | |
44 | write_test_header, | |
45 | create_interfaces_cfg, | |
46 | write_test_footer, | |
47 | reset_config_on_routers, | |
48 | verify_rib, | |
49 | create_static_routes, | |
50 | check_address_types, | |
51 | step, | |
52 | create_route_maps, | |
53 | shutdown_bringup_interface, | |
54 | stop_router, | |
55 | start_router, | |
701a0192 | 56 | topo_daemons, |
4256a209 | 57 | ) |
58 | from lib.bgp import verify_bgp_convergence, create_router_bgp | |
59 | from lib.topolog import logger | |
60 | from lib.topojson import build_topo_from_json, build_config_from_json | |
61 | ||
62 | from lib.ospf import ( | |
63 | verify_ospf_neighbor, | |
64 | config_ospf_interface, | |
65 | clear_ospf, | |
66 | verify_ospf_rib, | |
67 | create_router_ospf, | |
68 | verify_ospf_interface, | |
88b7d3e7 | 69 | redistribute_ospf, |
4256a209 | 70 | ) |
71 | from ipaddress import IPv4Address | |
72 | ||
6ff492b1 DS |
73 | pytestmark = [pytest.mark.ospfd, pytest.mark.staticd] |
74 | ||
75 | ||
4256a209 | 76 | # Global variables |
77 | topo = None | |
78 | # Reading the data from JSON File for topology creation | |
79 | ||
80 | jsonFile = "{}/ospf_ecmp_lan.json".format(CWD) | |
81 | try: | |
82 | with open(jsonFile, "r") as topoJson: | |
83 | topo = json.load(topoJson) | |
84 | except IOError: | |
85 | assert False, "Could not read file {}".format(jsonFile) | |
86 | ||
87 | NETWORK = { | |
88 | "ipv4": [ | |
89 | "11.0.20.1/32", | |
90 | "11.0.20.2/32", | |
91 | "11.0.20.3/32", | |
92 | "11.0.20.4/32", | |
93 | "11.0.20.5/32", | |
94 | ], | |
95 | "ipv6": ["1::1/128", "1::2/128", "1::3/128", "1::4/128", "1::5/128"], | |
96 | } | |
97 | MASK = {"ipv4": "32", "ipv6": "128"} | |
98 | NEXT_HOP = { | |
99 | "ipv4": ["10.0.0.1", "10.0.1.1", "10.0.2.1", "10.0.3.1", "10.0.4.1"], | |
100 | "ipv6": ["Null0", "Null0", "Null0", "Null0", "Null0"], | |
101 | } | |
102 | """ | |
103 | TOPOOLOGY = | |
104 | Please view in a fixed-width font such as Courier. | |
105 | Topo : Broadcast Networks | |
106 | +---+ +---+ +---+ +---+ | |
107 | |R0 + +R1 + +R2 + +R3 | | |
108 | +-+-+ +-+-+ +-+-+ +-+-+ | |
109 | | | | | | |
110 | | | | | | |
111 | --+-----------+--------------+---------------+----- | |
112 | Ethernet Segment | |
113 | ||
114 | TESTCASES = | |
115 | 1. Verify OSPF ECMP with max path configured as 8 | |
116 | (Edge having 1 uplink port as broadcast network, | |
117 | connect to 8 TORs - LAN case) | |
118 | ||
119 | """ | |
120 | ||
121 | ||
122 | class CreateTopo(Topo): | |
123 | """ | |
124 | Test topology builder. | |
125 | ||
126 | * `Topo`: Topology object | |
127 | """ | |
128 | ||
129 | def build(self, *_args, **_opts): | |
130 | """Build function.""" | |
131 | tgen = get_topogen(self) | |
132 | ||
133 | # Building topology from json file | |
134 | build_topo_from_json(tgen, topo) | |
135 | ||
136 | ||
137 | def setup_module(mod): | |
138 | """ | |
139 | Sets up the pytest environment | |
140 | ||
141 | * `mod`: module name | |
142 | """ | |
143 | global topo | |
144 | testsuite_run_time = time.asctime(time.localtime(time.time())) | |
145 | logger.info("Testsuite start time: {}".format(testsuite_run_time)) | |
146 | logger.info("=" * 40) | |
147 | ||
148 | logger.info("Running setup_module to create topology") | |
149 | ||
150 | # This function initiates the topology build with Topogen... | |
151 | tgen = Topogen(CreateTopo, mod.__name__) | |
152 | # ... and here it calls Mininet initialization functions. | |
153 | ||
035267a3 | 154 | # get list of daemons needs to be started for this suite. |
155 | daemons = topo_daemons(tgen, topo) | |
156 | ||
4256a209 | 157 | # Starting topology, create tmp files which are loaded to routers |
158 | # to start deamons and then start routers | |
035267a3 | 159 | start_topology(tgen, daemons) |
160 | ||
4256a209 | 161 | # Creating configuration from JSON |
162 | build_config_from_json(tgen, topo) | |
163 | ||
164 | # Don't run this test if we have any failure. | |
165 | if tgen.routers_have_failure(): | |
166 | pytest.skip(tgen.errors) | |
167 | # Api call verify whether OSPF is converged | |
168 | ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True) | |
169 | assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( | |
170 | ospf_covergence | |
171 | ) | |
172 | ||
173 | logger.info("Running setup_module() done") | |
174 | ||
175 | ||
176 | def teardown_module(): | |
177 | """Teardown the pytest environment""" | |
178 | ||
179 | logger.info("Running teardown_module to delete topology") | |
180 | ||
181 | tgen = get_topogen() | |
182 | ||
183 | try: | |
184 | # Stop toplogy and Remove tmp files | |
185 | tgen.stop_topology() | |
186 | ||
187 | except OSError: | |
188 | # OSError exception is raised when mininet tries to stop switch | |
189 | # though switch is stopped once but mininet tries to stop same | |
190 | # switch again, where it ended up with exception | |
191 | pass | |
192 | ||
193 | ||
4256a209 | 194 | # ################################## |
195 | # Test cases start here. | |
196 | # ################################## | |
197 | ||
198 | ||
199 | def test_ospf_lan_ecmp_tc18_p0(request): | |
200 | """ | |
201 | OSPF ECMP. | |
202 | ||
203 | Verify OSPF ECMP with max path configured as 8 | |
204 | (Edge having 1 uplink port as broadcast network, | |
205 | connect to 8 TORs - LAN case) | |
206 | ||
207 | """ | |
208 | tc_name = request.node.name | |
209 | write_test_header(tc_name) | |
210 | tgen = get_topogen() | |
211 | ||
212 | # Don't run this test if we have any failure. | |
213 | if tgen.routers_have_failure(): | |
214 | pytest.skip(tgen.errors) | |
215 | ||
216 | global topo | |
217 | step("Bring up the base config as per the topology") | |
218 | step(". Configure ospf in all the routers on LAN interface.") | |
219 | reset_config_on_routers(tgen) | |
220 | step("Verify that OSPF is up with 8 neighborship sessions.") | |
221 | ||
222 | ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True) | |
223 | assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( | |
224 | ospf_covergence | |
225 | ) | |
226 | ||
227 | step( | |
228 | "Configure a static route in all the routes and " | |
229 | "redistribute static/connected in OSPF." | |
230 | ) | |
231 | ||
232 | for rtr in topo["routers"]: | |
233 | input_dict = { | |
234 | rtr: { | |
235 | "static_routes": [ | |
236 | {"network": NETWORK["ipv4"][0], "no_of_ip": 5, "next_hop": "Null0"} | |
237 | ] | |
238 | } | |
239 | } | |
240 | result = create_static_routes(tgen, input_dict) | |
241 | assert result is True, "Testcase {} : Failed \n Error: {}".format( | |
242 | tc_name, result | |
243 | ) | |
244 | ||
245 | dut = rtr | |
88b7d3e7 | 246 | redistribute_ospf(tgen, topo, dut, "static") |
4256a209 | 247 | |
248 | step( | |
249 | "Verify that route in R0 in stalled with 8 hops. " | |
250 | "Verify ospf route table and ip route table." | |
251 | ) | |
252 | ||
253 | nh = [] | |
254 | for rtr in topo["routers"]: | |
255 | nh.append(topo["routers"][rtr]["links"]["s1"]["ipv4"].split("/")[0]) | |
256 | nh.remove(topo["routers"]["r1"]["links"]["s1"]["ipv4"].split("/")[0]) | |
257 | dut = "r1" | |
258 | result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh) | |
259 | assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) | |
260 | ||
261 | protocol = "ospf" | |
262 | result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, next_hop=nh) | |
263 | assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) | |
264 | ||
265 | step(" clear ip ospf interface on DUT(r0)") | |
266 | clear_ospf(tgen, "r0") | |
267 | ||
268 | step( | |
269 | "Verify that after clearing the ospf interface all the " | |
270 | "neighbours are up and routes are installed with 8 next hop " | |
271 | "in ospf and ip route tables on R0" | |
272 | ) | |
273 | ||
274 | dut = "r0" | |
275 | ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut, lan=True) | |
276 | assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( | |
277 | ospf_covergence | |
278 | ) | |
279 | ||
280 | step(" clear ip ospf interface on R2") | |
281 | clear_ospf(tgen, "r2") | |
282 | ||
283 | dut = "r2" | |
284 | ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut, lan=True) | |
285 | assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( | |
286 | ospf_covergence | |
287 | ) | |
288 | ||
289 | step("Delete static/connected cmd in ospf in all the routes one by one.") | |
290 | for rtr in topo["routers"]: | |
291 | input_dict = { | |
292 | rtr: { | |
293 | "static_routes": [ | |
294 | { | |
295 | "network": NETWORK["ipv4"][0], | |
296 | "no_of_ip": 5, | |
297 | "next_hop": "Null0", | |
298 | "delete": True, | |
299 | } | |
300 | ] | |
301 | } | |
302 | } | |
303 | result = create_static_routes(tgen, input_dict) | |
304 | assert result is True, "Testcase {} : Failed \n Error: {}".format( | |
305 | tc_name, result | |
306 | ) | |
307 | ||
308 | step("Verify that all the routes are withdrawn from R0") | |
309 | dut = "r1" | |
a81774ec | 310 | result = verify_ospf_rib( |
ed776e38 | 311 | tgen, dut, input_dict, next_hop=nh, retry_timeout=10, expected=False |
a81774ec | 312 | ) |
0b25370e DS |
313 | assert ( |
314 | result is not True | |
315 | ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format( | |
4256a209 | 316 | tc_name, result |
0b25370e | 317 | ) |
4256a209 | 318 | |
319 | protocol = "ospf" | |
a81774ec | 320 | result = verify_rib( |
321 | tgen, | |
322 | "ipv4", | |
323 | dut, | |
324 | input_dict, | |
325 | protocol=protocol, | |
326 | next_hop=nh, | |
ed776e38 | 327 | retry_timeout=10, |
a81774ec | 328 | expected=False, |
329 | ) | |
0b25370e DS |
330 | assert ( |
331 | result is not True | |
332 | ), "Testcase {} : Failed \n " "r1: routes are still present \n Error: {}".format( | |
4256a209 | 333 | tc_name, result |
0b25370e | 334 | ) |
4256a209 | 335 | |
336 | write_test_footer(tc_name) | |
337 | ||
338 | ||
339 | if __name__ == "__main__": | |
340 | args = ["-s"] + sys.argv[1:] | |
341 | sys.exit(pytest.main(args)) |