]>
Commit | Line | Data |
---|---|---|
a4e471cf DS |
1 | #!/usr/bin/env python |
2 | ||
3 | # | |
4 | # test_eigrp_topo1.py | |
5 | # | |
6 | # Copyright (c) 2017 by | |
7 | # Cumulus Networks, Inc. | |
8 | # Donald Sharp | |
9 | # | |
10 | # Permission to use, copy, modify, and/or distribute this software | |
11 | # for any purpose with or without fee is hereby granted, provided | |
12 | # that the above copyright notice and this permission notice appear | |
13 | # in all copies. | |
14 | # | |
15 | # THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES | |
16 | # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
17 | # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR | |
18 | # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY | |
19 | # DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, | |
20 | # WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS | |
21 | # ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE | |
22 | # OF THIS SOFTWARE. | |
23 | # | |
24 | ||
25 | """ | |
26 | test_eigrp_topo1.py: Testing EIGRP | |
27 | ||
28 | """ | |
29 | ||
30 | import os | |
31 | import re | |
32 | import sys | |
a4e471cf | 33 | import pytest |
ee4523a2 | 34 | import json |
a4e471cf | 35 | |
e5355a01 RZ |
36 | # Save the Current Working Directory to find configuration files. |
37 | CWD = os.path.dirname(os.path.realpath(__file__)) | |
787e7624 | 38 | sys.path.append(os.path.join(CWD, "../")) |
a4e471cf | 39 | |
e5355a01 RZ |
40 | # pylint: disable=C0413 |
41 | # Import topogen and topotest helpers | |
a4e471cf | 42 | from lib import topotest |
e5355a01 RZ |
43 | from lib.topogen import Topogen, TopoRouter, get_topogen |
44 | from lib.topolog import logger | |
a4e471cf | 45 | |
e5355a01 RZ |
46 | # Required to instantiate the topology builder class. |
47 | from mininet.topo import Topo | |
a4e471cf DS |
48 | |
49 | ##################################################### | |
50 | ## | |
51 | ## Network Topology Definition | |
52 | ## | |
53 | ##################################################### | |
54 | ||
787e7624 | 55 | |
a4e471cf DS |
56 | class NetworkTopo(Topo): |
57 | "EIGRP Topology 1" | |
58 | ||
59 | def build(self, **_opts): | |
e5355a01 RZ |
60 | "Build function" |
61 | ||
62 | tgen = get_topogen(self) | |
63 | ||
64 | for routern in range(1, 4): | |
787e7624 | 65 | tgen.add_router("r{}".format(routern)) |
a4e471cf | 66 | |
a4e471cf DS |
67 | # On main router |
68 | # First switch is for a dummy interface (for local network) | |
787e7624 | 69 | switch = tgen.add_switch("sw1") |
70 | switch.add_link(tgen.gears["r1"]) | |
e5355a01 | 71 | |
a4e471cf DS |
72 | # Switches for EIGRP |
73 | # switch 2 switch is for connection to EIGRP router | |
787e7624 | 74 | switch = tgen.add_switch("sw2") |
75 | switch.add_link(tgen.gears["r1"]) | |
76 | switch.add_link(tgen.gears["r2"]) | |
e5355a01 | 77 | |
a4e471cf | 78 | # switch 4 is stub on remote EIGRP router |
787e7624 | 79 | switch = tgen.add_switch("sw4") |
80 | switch.add_link(tgen.gears["r3"]) | |
a4e471cf | 81 | |
e5355a01 | 82 | # switch 3 is between EIGRP routers |
787e7624 | 83 | switch = tgen.add_switch("sw3") |
84 | switch.add_link(tgen.gears["r2"]) | |
85 | switch.add_link(tgen.gears["r3"]) | |
a4e471cf DS |
86 | |
87 | ||
88 | ##################################################### | |
89 | ## | |
90 | ## Tests starting | |
91 | ## | |
92 | ##################################################### | |
93 | ||
787e7624 | 94 | |
a4e471cf | 95 | def setup_module(module): |
e5355a01 RZ |
96 | "Setup topology" |
97 | tgen = Topogen(NetworkTopo, module.__name__) | |
98 | tgen.start_topology() | |
a4e471cf | 99 | |
e5355a01 RZ |
100 | # This is a sample of configuration loading. |
101 | router_list = tgen.routers() | |
102 | for rname, router in router_list.iteritems(): | |
103 | router.load_config( | |
787e7624 | 104 | TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) |
e5355a01 RZ |
105 | ) |
106 | router.load_config( | |
787e7624 | 107 | TopoRouter.RD_EIGRP, os.path.join(CWD, "{}/eigrpd.conf".format(rname)) |
e5355a01 | 108 | ) |
a4e471cf | 109 | |
e5355a01 | 110 | tgen.start_router() |
a4e471cf | 111 | |
a4e471cf | 112 | |
e5355a01 RZ |
113 | def teardown_module(_mod): |
114 | "Teardown the pytest environment" | |
115 | tgen = get_topogen() | |
a4e471cf | 116 | |
e5355a01 RZ |
117 | # This function tears down the whole topology. |
118 | tgen.stop_topology() | |
a4e471cf DS |
119 | |
120 | ||
e5355a01 RZ |
121 | def test_converge_protocols(): |
122 | "Wait for protocol convergence" | |
a4e471cf | 123 | |
e5355a01 RZ |
124 | tgen = get_topogen() |
125 | # Don't run this test if we have any failure. | |
126 | if tgen.routers_have_failure(): | |
127 | pytest.skip(tgen.errors) | |
a4e471cf | 128 | |
787e7624 | 129 | topotest.sleep(5, "Waiting for EIGRP convergence") |
a4e471cf | 130 | |
a4e471cf | 131 | |
e5355a01 RZ |
132 | def test_eigrp_routes(): |
133 | "Test EIGRP 'show ip eigrp'" | |
a4e471cf | 134 | |
e5355a01 RZ |
135 | tgen = get_topogen() |
136 | # Don't run this test if we have any failure. | |
137 | if tgen.routers_have_failure(): | |
138 | pytest.skip(tgen.errors) | |
a4e471cf | 139 | |
e5355a01 RZ |
140 | # Verify EIGRP Status |
141 | logger.info("Verifying EIGRP routes") | |
a4e471cf | 142 | |
e5355a01 RZ |
143 | router_list = tgen.routers().values() |
144 | for router in router_list: | |
787e7624 | 145 | refTableFile = "{}/{}/show_ip_eigrp.json".format(CWD, router.name) |
a4e471cf | 146 | |
e5355a01 | 147 | # Read expected result from file |
7c1f9631 | 148 | expected = json.loads(open(refTableFile).read()) |
a4e471cf | 149 | |
e5355a01 | 150 | # Actual output from router |
7c1f9631 | 151 | actual = ip_eigrp_topo(router) |
a4e471cf | 152 | |
7c1f9631 RZ |
153 | assertmsg = '"show ip eigrp topo" mismatches on {}'.format(router.name) |
154 | assert topotest.json_cmp(actual, expected) is None, assertmsg | |
a4e471cf | 155 | |
787e7624 | 156 | |
e5355a01 RZ |
157 | def test_zebra_ipv4_routingTable(): |
158 | "Test 'show ip route'" | |
a4e471cf | 159 | |
e5355a01 RZ |
160 | tgen = get_topogen() |
161 | # Don't run this test if we have any failure. | |
162 | if tgen.routers_have_failure(): | |
163 | pytest.skip(tgen.errors) | |
a4e471cf | 164 | |
a4e471cf | 165 | failures = 0 |
e5355a01 RZ |
166 | router_list = tgen.routers().values() |
167 | for router in router_list: | |
787e7624 | 168 | output = router.vtysh_cmd("show ip route json", isjson=True) |
169 | refTableFile = "{}/{}/show_ip_route.json_ref".format(CWD, router.name) | |
ee4523a2 | 170 | expected = json.loads(open(refTableFile).read()) |
a4e471cf | 171 | |
787e7624 | 172 | assertmsg = "Zebra IPv4 Routing Table verification failed for router {}".format( |
173 | router.name | |
174 | ) | |
ee4523a2 | 175 | assert topotest.json_cmp(output, expected) is None, assertmsg |
a4e471cf | 176 | |
787e7624 | 177 | |
84b5e534 DS |
178 | def test_shut_interface_and_recover(): |
179 | "Test shutdown of an interface and recovery of the interface" | |
180 | ||
181 | tgen = get_topogen() | |
787e7624 | 182 | router = tgen.gears["r1"] |
183 | router.run("ip link set r1-eth1 down") | |
184 | topotest.sleep(5, "Waiting for EIGRP convergence") | |
185 | router.run("ip link set r1-eth1 up") | |
84b5e534 | 186 | |
a4e471cf DS |
187 | |
188 | def test_shutdown_check_stderr(): | |
787e7624 | 189 | if os.environ.get("TOPOTESTS_CHECK_STDERR") is None: |
190 | pytest.skip("Skipping test for Stderr output and memory leaks") | |
a4e471cf | 191 | |
e5355a01 RZ |
192 | tgen = get_topogen() |
193 | # Don't run this test if we have any failure. | |
194 | if tgen.routers_have_failure(): | |
195 | pytest.skip(tgen.errors) | |
a4e471cf | 196 | |
e5355a01 | 197 | logger.info("Verifying unexpected STDERR output from daemons") |
a4e471cf | 198 | |
e5355a01 RZ |
199 | router_list = tgen.routers().values() |
200 | for router in router_list: | |
201 | router.stop() | |
a4e471cf | 202 | |
787e7624 | 203 | log = tgen.net[router.name].getStdErr("eigrpd") |
8e957dbb | 204 | if log: |
787e7624 | 205 | logger.error("EIGRPd StdErr Log:" + log) |
206 | log = tgen.net[router.name].getStdErr("zebra") | |
8e957dbb | 207 | if log: |
787e7624 | 208 | logger.error("Zebra StdErr Log:" + log) |
a4e471cf DS |
209 | |
210 | ||
787e7624 | 211 | if __name__ == "__main__": |
e5355a01 RZ |
212 | args = ["-s"] + sys.argv[1:] |
213 | sys.exit(pytest.main(args)) | |
7c1f9631 RZ |
214 | |
215 | # | |
216 | # Auxiliary Functions | |
217 | # | |
218 | def ip_eigrp_topo(node): | |
219 | """ | |
220 | Parse 'show ip eigrp topo' from `node` and returns a dict with the | |
221 | result. | |
222 | ||
223 | Example: | |
224 | { | |
225 | 'P': { | |
226 | '192.168.1.0/24': { | |
227 | 'sucessors': 1, | |
228 | 'fd': 112233, | |
229 | 'serno': 0, | |
230 | 'via': 'Connected', | |
231 | 'interface': 'eth0', | |
232 | }, | |
233 | '192.168.2.0/24': { | |
234 | 'sucessors': 1, | |
235 | 'fd': 112234, | |
236 | 'serno': 0, | |
237 | 'via': 'Connected', | |
238 | 'interface': 'eth1', | |
239 | } | |
240 | } | |
241 | } | |
242 | """ | |
787e7624 | 243 | output = topotest.normalize_text(node.vtysh_cmd("show ip eigrp topo")).splitlines() |
7c1f9631 RZ |
244 | result = {} |
245 | for idx, line in enumerate(output): | |
787e7624 | 246 | columns = line.split(" ", 1) |
7c1f9631 RZ |
247 | |
248 | # Parse the following format into python dicts | |
249 | # code A.B.C.D/E, X successors, FD is Y, serno: Z | |
250 | # via FOO, interface-name | |
251 | code = columns[0] | |
787e7624 | 252 | if code not in ["P", "A", "U", "Q", "R", "r", "s"]: |
7c1f9631 RZ |
253 | continue |
254 | ||
255 | if not result.has_key(code): | |
256 | result[code] = {} | |
257 | ||
258 | # Split network from the rest | |
787e7624 | 259 | columns = columns[1].split(",") |
7c1f9631 RZ |
260 | |
261 | # Parse first line data | |
262 | network = columns[0] | |
263 | result[code][network] = {} | |
264 | for column in columns: | |
265 | # Skip the network column | |
266 | if column == columns[0]: | |
267 | continue | |
268 | ||
787e7624 | 269 | match = re.search(r"(\d+) successors", column) |
7c1f9631 | 270 | if match is not None: |
787e7624 | 271 | result[code][network]["successors"] = match.group(1) |
7c1f9631 RZ |
272 | continue |
273 | ||
787e7624 | 274 | match = re.search(r"FD is (\d+)", column) |
7c1f9631 | 275 | if match is not None: |
787e7624 | 276 | result[code][network]["fd"] = match.group(1) |
7c1f9631 RZ |
277 | continue |
278 | ||
787e7624 | 279 | match = re.search(r"serno: (\d+)", column) |
7c1f9631 | 280 | if match is not None: |
787e7624 | 281 | result[code][network]["serno"] = match.group(1) |
7c1f9631 RZ |
282 | continue |
283 | ||
284 | # Parse second line data | |
285 | nextline = output[idx + 1] | |
787e7624 | 286 | columns = topotest.normalize_text(nextline).split(",") |
7c1f9631 | 287 | for column in columns: |
787e7624 | 288 | match = re.search(r"via (.+)", column) |
7c1f9631 | 289 | if match is not None: |
787e7624 | 290 | result[code][network]["via"] = match.group(1) |
7c1f9631 RZ |
291 | continue |
292 | ||
787e7624 | 293 | match = re.search(r"(.+)", column) |
7c1f9631 | 294 | if match is not None: |
787e7624 | 295 | result[code][network]["interface"] = match.group(1) |
7c1f9631 RZ |
296 | continue |
297 | ||
298 | return result |