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