]>
Commit | Line | Data |
---|---|---|
0a434046 | 1 | #!/usr/bin/env python |
2 | ||
3 | # | |
4 | # test_isis_snmp.py | |
5 | # Part of NetDEF Topology Tests | |
6 | # | |
7 | # Copyright (c) 2020 by Volta Networks | |
8 | # | |
9 | # Permission to use, copy, modify, and/or distribute this software | |
10 | # for any purpose with or without fee is hereby granted, provided | |
11 | # that the above copyright notice and this permission notice appear | |
12 | # in all copies. | |
13 | # | |
14 | # THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES | |
15 | # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
16 | # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR | |
17 | # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY | |
18 | # DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, | |
19 | # WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS | |
20 | # ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE | |
21 | # OF THIS SOFTWARE. | |
22 | # | |
23 | ||
24 | """ | |
25 | test_isis_snmp.py: | |
26 | ||
27 | +---------+ 45.0.0.0/24 +---------+ | |
28 | | | rt4-eth1 | | | |
29 | | RT4 +----------------+ RT5 | | |
30 | | | rt5-eth1| | | |
31 | +---------+ +---------+ | |
32 | rt4-eth0| |rt5-eth0 | |
33 | | | | |
34 | 14.0.0.0/24| |25.0.0.0/24 | |
35 | | | | |
36 | rt1-eth0| |rt2-eth0 | |
37 | +---------+ +---------+ | |
38 | | | | | | |
39 | | RT1 | | RT2 | | |
40 | | 1.1.1.1 | | 2.2.2.2 | | |
41 | | | | | | |
42 | +---------+ +---------+ | |
43 | rt1-eth1| |rt2-eth1 | |
44 | | | | |
45 | | | | |
46 | 13.0.0.0/24| +---------+ |23.0.0.0/24 | |
47 | | | | | | |
48 | | | RT3 | | | |
49 | +--------+ 3.3.3.3 +-------+ | |
50 | rt3-eth1| |rt3-eth2 | |
51 | +---------+ | |
52 | |rt3-eth0 | |
53 | | | |
54 | | | |
55 | ce3-eth0 (172.16.1.3/24)| | |
56 | +---------+ | |
57 | | | | |
58 | | CE3 | | |
59 | | | | |
60 | +---------+ | |
61 | """ | |
62 | ||
63 | import os | |
0a434046 | 64 | import sys |
65 | import pytest | |
66 | import json | |
0a434046 | 67 | from functools import partial |
68 | ||
69 | # Save the Current Working Directory to find configuration files. | |
70 | CWD = os.path.dirname(os.path.realpath(__file__)) | |
71 | sys.path.append(os.path.join(CWD, "../")) | |
72 | ||
73 | # pylint: disable=C0413 | |
74 | # Import topogen and topotest helpers | |
75 | from lib import topotest | |
76 | from lib.topogen import Topogen, TopoRouter, get_topogen | |
77 | from lib.topolog import logger | |
78 | from lib.snmptest import SnmpTester | |
79 | ||
80 | # Required to instantiate the topology builder class. | |
0a434046 | 81 | |
dbc5be82 DS |
82 | pytestmark = [pytest.mark.isisd, pytest.mark.ldpd, pytest.mark.snmp] |
83 | ||
0a434046 | 84 | |
e82b531d CH |
85 | def build_topo(tgen): |
86 | "Build function" | |
0a434046 | 87 | |
e82b531d CH |
88 | # |
89 | # Define FRR Routers | |
90 | # | |
91 | for router in ["ce3", "r1", "r2", "r3", "r4", "r5"]: | |
92 | tgen.add_router(router) | |
0a434046 | 93 | |
e82b531d CH |
94 | # |
95 | # Define connections | |
96 | # | |
97 | switch = tgen.add_switch("s1") | |
98 | switch.add_link(tgen.gears["r1"]) | |
99 | switch.add_link(tgen.gears["r4"]) | |
0a434046 | 100 | |
e82b531d CH |
101 | switch = tgen.add_switch("s2") |
102 | switch.add_link(tgen.gears["r5"]) | |
103 | switch.add_link(tgen.gears["r2"]) | |
0a434046 | 104 | |
e82b531d CH |
105 | switch = tgen.add_switch("s3") |
106 | switch.add_link(tgen.gears["ce3"]) | |
107 | switch.add_link(tgen.gears["r3"]) | |
0a434046 | 108 | |
e82b531d CH |
109 | switch = tgen.add_switch("s4") |
110 | switch.add_link(tgen.gears["r4"]) | |
111 | switch.add_link(tgen.gears["r5"]) | |
0a434046 | 112 | |
e82b531d CH |
113 | switch = tgen.add_switch("s5") |
114 | switch.add_link(tgen.gears["r1"]) | |
115 | switch.add_link(tgen.gears["r3"]) | |
0a434046 | 116 | |
e82b531d CH |
117 | switch = tgen.add_switch("s6") |
118 | switch.add_link(tgen.gears["r2"]) | |
119 | switch.add_link(tgen.gears["r3"]) | |
0a434046 | 120 | |
121 | ||
0a434046 | 122 | def setup_module(mod): |
123 | "Sets up the pytest environment" | |
124 | ||
125 | # skip tests is SNMP not installed | |
126 | if not os.path.isfile("/usr/sbin/snmpd"): | |
127 | error_msg = "SNMP not installed - skipping" | |
128 | pytest.skip(error_msg) | |
129 | ||
130 | # This function initiates the topology build with Topogen... | |
e82b531d | 131 | tgen = Topogen(build_topo, mod.__name__) |
0a434046 | 132 | # ... and here it calls Mininet initialization functions. |
133 | tgen.start_topology() | |
134 | ||
135 | router_list = tgen.routers() | |
136 | ||
137 | # For all registered routers, load the zebra configuration file | |
d7d21c3a | 138 | for rname, router in router_list.items(): |
0a434046 | 139 | router.load_config( |
140 | TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) | |
141 | ) | |
142 | # Don't start the following in the CE nodes | |
143 | if router.name[0] == "r": | |
144 | router.load_config( | |
0b25370e DS |
145 | TopoRouter.RD_ISIS, |
146 | os.path.join(CWD, "{}/isisd.conf".format(rname)), | |
0a434046 | 147 | "-M snmp", |
148 | ) | |
149 | router.load_config( | |
0b25370e DS |
150 | TopoRouter.RD_LDP, |
151 | os.path.join(CWD, "{}/ldpd.conf".format(rname)), | |
0a434046 | 152 | ) |
153 | router.load_config( | |
0b25370e DS |
154 | TopoRouter.RD_SNMP, |
155 | os.path.join(CWD, "{}/snmpd.conf".format(rname)), | |
0a434046 | 156 | "-Le -Ivacm_conf,usmConf,iquery -V -DAgentX,trap", |
157 | ) | |
158 | ||
159 | # After loading the configurations, this function loads configured daemons. | |
160 | tgen.start_router() | |
161 | ||
0b25370e | 162 | |
0a434046 | 163 | def teardown_module(mod): |
164 | "Teardown the pytest environment" | |
165 | tgen = get_topogen() | |
166 | ||
167 | # This function tears down the whole topology. | |
168 | tgen.stop_topology() | |
169 | ||
0b25370e | 170 | |
0a434046 | 171 | def router_compare_json_output(rname, command, reference): |
172 | "Compare router JSON output" | |
173 | ||
174 | logger.info('Comparing router "%s" "%s" output', rname, command) | |
175 | ||
176 | tgen = get_topogen() | |
177 | filename = "{}/{}/{}".format(CWD, rname, reference) | |
178 | expected = json.loads(open(filename).read()) | |
179 | ||
180 | # Run test function until we get an result. Wait at most 80 seconds. | |
181 | test_func = partial(topotest.router_json_cmp, tgen.gears[rname], command, expected) | |
182 | _, diff = topotest.run_and_expect(test_func, None, count=160, wait=0.5) | |
183 | assertmsg = '"{}" JSON output mismatches the expected result'.format(rname) | |
184 | assert diff is None, assertmsg | |
185 | ||
0b25370e | 186 | |
0a434046 | 187 | def generate_oid(numoids, index1, index2): |
188 | if numoids == 1: | |
189 | oid = "{}".format(index1) | |
190 | else: | |
191 | oid = "{}.{}".format(index1, index2) | |
192 | return oid | |
193 | ||
194 | ||
195 | def test_isis_convergence(): | |
196 | logger.info("Test: check ISIS adjacencies") | |
197 | tgen = get_topogen() | |
198 | ||
199 | for rname in ["r1", "r2", "r3", "r4", "r5"]: | |
200 | router_compare_json_output( | |
201 | rname, | |
202 | "show yang operational-data /frr-interface:lib isisd", | |
0b25370e DS |
203 | "show_yang_interface_isis_adjacencies.ref", |
204 | ) | |
205 | ||
0a434046 | 206 | |
207 | def test_r1_scalar_snmp(): | |
208 | "Wait for protocol convergence" | |
209 | tgen = get_topogen() | |
210 | ||
211 | # Skip if previous fatal error condition is raised | |
212 | if tgen.routers_have_failure(): | |
213 | pytest.skip(tgen.errors) | |
214 | ||
8db751b8 | 215 | r1 = tgen.gears["r1"] |
0a434046 | 216 | r1_snmp = SnmpTester(r1, "1.1.1.1", "public", "2c") |
217 | ||
0b25370e DS |
218 | assert r1_snmp.test_oid("isisSysVersion", "one(1)") |
219 | assert r1_snmp.test_oid("isisSysLevelType", "level1and2(3)") | |
220 | assert r1_snmp.test_oid("isisSysID", "00 00 00 00 00 01") | |
221 | assert r1_snmp.test_oid("isisSysMaxPathSplits", "32") | |
222 | assert r1_snmp.test_oid("isisSysMaxLSPGenInt", "900 seconds") | |
223 | assert r1_snmp.test_oid("isisSysAdminState", "on(1)") | |
224 | assert r1_snmp.test_oid("isisSysMaxAge", "1200 seconds") | |
225 | assert r1_snmp.test_oid("isisSysProtSupported", "07 5 6 7") | |
0a434046 | 226 | |
8db751b8 | 227 | r2 = tgen.gears["r2"] |
0a434046 | 228 | r2_snmp = SnmpTester(r2, "2.2.2.2", "public", "2c") |
0b25370e DS |
229 | |
230 | assert r2_snmp.test_oid("isisSysVersion", "one(1)") | |
231 | assert r2_snmp.test_oid("isisSysLevelType", "level1and2(3)") | |
232 | assert r2_snmp.test_oid("isisSysID", "00 00 00 00 00 02") | |
233 | assert r2_snmp.test_oid("isisSysMaxPathSplits", "32") | |
234 | assert r2_snmp.test_oid("isisSysMaxLSPGenInt", "900 seconds") | |
235 | assert r2_snmp.test_oid("isisSysAdminState", "on(1)") | |
236 | assert r2_snmp.test_oid("isisSysMaxAge", "1200 seconds") | |
237 | assert r2_snmp.test_oid("isisSysProtSupported", "07 5 6 7") | |
0a434046 | 238 | |
239 | ||
240 | circtable_test = { | |
bcf22081 IR |
241 | "isisCircAdminState": ["on(1)", "on(1)"], |
242 | "isisCircExistState": ["active(1)", "active(1)"], | |
243 | "isisCircType": ["broadcast(1)", "ptToPt(2)"], | |
244 | "isisCircExtDomain": ["false(2)", "false(2)"], | |
245 | "isisCircLevelType": ["level1(1)", "level1(1)"], | |
246 | "isisCircPassiveCircuit": ["false(2)", "false(2)"], | |
247 | "isisCircMeshGroupEnabled": ["inactive(1)", "inactive(1)"], | |
248 | "isisCircSmallHellos": ["false(2)", "false(2)"], | |
249 | "isisCirc3WayEnabled": ["false(2)", "false(2)"], | |
0b25370e DS |
250 | } |
251 | ||
0a434046 | 252 | |
253 | def test_r1_isisCircTable(): | |
254 | tgen = get_topogen() | |
255 | ||
8db751b8 | 256 | r1 = tgen.gears["r1"] |
0a434046 | 257 | r1_snmp = SnmpTester(r1, "1.1.1.1", "public", "2c") |
258 | ||
259 | oids = [] | |
0b25370e DS |
260 | oids.append(generate_oid(1, 1, 0)) |
261 | oids.append(generate_oid(1, 2, 0)) | |
0a434046 | 262 | |
263 | # check items | |
264 | for item in circtable_test.keys(): | |
265 | assertmsg = "{} should be {} oids {} full dict {}:".format( | |
266 | item, circtable_test[item], oids, r1_snmp.walk(item) | |
267 | ) | |
268 | assert r1_snmp.test_oid_walk(item, circtable_test[item], oids), assertmsg | |
269 | ||
0b25370e | 270 | |
0a434046 | 271 | circleveltable_test = { |
bcf22081 IR |
272 | "isisCircLevelMetric": ["10", "10"], |
273 | "isisCircLevelWideMetric": ["10", "10"], | |
274 | "isisCircLevelISPriority": ["64", "64"], | |
275 | "isisCircLevelHelloMultiplier": ["10", "10"], | |
0b25370e DS |
276 | "isisCircLevelHelloTimer": [ |
277 | "3000 milliseconds", | |
278 | "3000 milliseconds", | |
0b25370e DS |
279 | ], |
280 | "isisCircLevelMinLSPRetransInt": [ | |
281 | "1 seconds", | |
282 | "1 seconds", | |
0b25370e DS |
283 | ], |
284 | } | |
285 | ||
0a434046 | 286 | |
287 | def test_r1_isislevelCircTable(): | |
288 | tgen = get_topogen() | |
289 | ||
8db751b8 | 290 | r1 = tgen.gears["r1"] |
0a434046 | 291 | r1_snmp = SnmpTester(r1, "1.1.1.1", "public", "2c") |
292 | ||
293 | oids = [] | |
0b25370e DS |
294 | oids.append(generate_oid(2, 1, "area")) |
295 | oids.append(generate_oid(2, 2, "area")) | |
0a434046 | 296 | |
297 | # check items | |
298 | for item in circleveltable_test.keys(): | |
299 | assertmsg = "{} should be {} oids {} full dict {}:".format( | |
300 | item, circleveltable_test[item], oids, r1_snmp.walk(item) | |
301 | ) | |
302 | assert r1_snmp.test_oid_walk(item, circleveltable_test[item], oids), assertmsg | |
303 | ||
304 | ||
305 | adjtable_test = { | |
306 | "isisISAdjState": ["up(3)", "up(3)"], | |
307 | "isisISAdj3WayState": ["down(2)", "up(0)"], | |
308 | "isisISAdjNeighSysType": ["l1IntermediateSystem(1)", "l1IntermediateSystem(1)"], | |
309 | "isisISAdjNeighSysID": ["00 00 00 00 00 04", "00 00 00 00 00 03"], | |
0a434046 | 310 | "isisISAdjUsage": ["0", "level1(1)"], |
311 | "isisISAdjNeighPriority": ["64", "0"], | |
312 | } | |
313 | ||
314 | adjtable_down_test = { | |
315 | "isisISAdjState": ["up(3)"], | |
316 | "isisISAdj3WayState": ["down(2)"], | |
317 | "isisISAdjNeighSysType": ["l1IntermediateSystem(1)"], | |
318 | "isisISAdjNeighSysID": ["00 00 00 00 00 04"], | |
0a434046 | 319 | "isisISAdjUsage": ["0"], |
320 | "isisISAdjNeighPriority": ["64"], | |
321 | } | |
322 | ||
0b25370e | 323 | |
0a434046 | 324 | def test_r1_isisAdjTable(): |
325 | "check ISIS Adjacency Table" | |
326 | tgen = get_topogen() | |
8db751b8 | 327 | r1 = tgen.gears["r1"] |
0a434046 | 328 | r1_snmp = SnmpTester(r1, "1.1.1.1", "public", "2c") |
329 | ||
330 | oids = [] | |
0b25370e DS |
331 | oids.append(generate_oid(2, 1, 1)) |
332 | oids.append(generate_oid(2, 2, 1)) | |
0a434046 | 333 | |
334 | oids_down = [] | |
0b25370e | 335 | oids_down.append(generate_oid(2, 1, 1)) |
0a434046 | 336 | |
337 | # check items | |
338 | for item in adjtable_test.keys(): | |
339 | assertmsg = "{} should be {} oids {} full dict {}:".format( | |
340 | item, adjtable_test[item], oids, r1_snmp.walk(item) | |
341 | ) | |
342 | assert r1_snmp.test_oid_walk(item, adjtable_test[item], oids), assertmsg | |
343 | ||
0a434046 | 344 | # shutdown interface and one adjacency should be removed |
345 | "check ISIS adjacency is removed when interface is shutdown" | |
8db751b8 | 346 | r1.vtysh_cmd("conf t\ninterface r1-eth1\nshutdown") |
0a434046 | 347 | r1_snmp = SnmpTester(r1, "1.1.1.1", "public", "2c") |
348 | ||
349 | for item in adjtable_down_test.keys(): | |
350 | assertmsg = "{} should be {} oids {} full dict {}:".format( | |
351 | item, adjtable_down_test[item], oids_down, r1_snmp.walk(item) | |
352 | ) | |
0b25370e DS |
353 | assert r1_snmp.test_oid_walk( |
354 | item, adjtable_down_test[item], oids_down | |
355 | ), assertmsg | |
0a434046 | 356 | |
357 | # no shutdown interface and adjacency should be restored | |
8db751b8 | 358 | r1.vtysh_cmd("conf t\ninterface r1-eth1\nno shutdown") |
0a434046 | 359 | |
360 | ||
361 | # Memory leak test template | |
362 | # disabling memory leak | |
363 | def test_memory_leak(): | |
364 | "Run the memory leak test and report results." | |
365 | tgen = get_topogen() | |
366 | if not tgen.is_memleak_enabled(): | |
367 | pytest.skip("Memory leak test/report is disabled") | |
368 | ||
369 | tgen.report_memory_leaks() | |
370 | ||
371 | ||
372 | if __name__ == "__main__": | |
373 | args = ["-s"] + sys.argv[1:] | |
374 | sys.exit(pytest.main(args)) |