]>
Commit | Line | Data |
---|---|---|
df437d25 LB |
1 | #!/usr/bin/env python |
2 | ||
3 | # | |
4 | # Part of NetDEF Topology Tests | |
5 | # | |
6 | # Copyright (c) 2017 by | |
7 | # Network Device Education Foundation, Inc. ("NetDEF") | |
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 | ltemplate.py: LabN template for FRR tests. | |
26 | """ | |
27 | ||
28 | import os | |
29 | import sys | |
89b9abd9 | 30 | import platform |
49581587 | 31 | |
df437d25 LB |
32 | import pytest |
33 | ||
34 | # pylint: disable=C0413 | |
35 | # Import topogen and topotest helpers | |
36 | from lib import topotest | |
37 | from lib.topogen import Topogen, TopoRouter, get_topogen | |
38 | from lib.topolog import logger | |
39 | from lib.lutil import * | |
40 | ||
41 | # Required to instantiate the topology builder class. | |
9e219b9a LB |
42 | |
43 | customize = None | |
44 | ||
a53c08bc | 45 | |
701a0192 | 46 | class LTemplate: |
9e219b9a LB |
47 | test = None |
48 | testdir = None | |
850de337 LB |
49 | scriptdir = None |
50 | logdir = None | |
89b9abd9 LB |
51 | prestarthooksuccess = True |
52 | poststarthooksuccess = True | |
53 | iproute2Ver = None | |
9e219b9a LB |
54 | |
55 | def __init__(self, test, testdir): | |
49581587 | 56 | pathname = os.path.join(testdir, "customize.py") |
9e219b9a | 57 | global customize |
49581587 CH |
58 | if sys.version_info >= (3, 5): |
59 | import importlib.util | |
a53c08bc | 60 | |
49581587 CH |
61 | spec = importlib.util.spec_from_file_location("customize", pathname) |
62 | customize = importlib.util.module_from_spec(spec) | |
63 | spec.loader.exec_module(customize) | |
64 | else: | |
65 | import imp | |
a53c08bc | 66 | |
49581587 | 67 | customize = imp.load_source("customize", pathname) |
9e219b9a LB |
68 | self.test = test |
69 | self.testdir = testdir | |
850de337 | 70 | self.scriptdir = testdir |
49581587 | 71 | self.logdir = "" |
701a0192 | 72 | logger.info("LTemplate: " + test) |
9e219b9a LB |
73 | |
74 | def setup_module(self, mod): | |
75 | "Sets up the pytest environment" | |
76 | # This function initiates the topology build with Topogen... | |
e82b531d | 77 | tgen = Topogen(customize.build_topo, mod.__name__) |
9e219b9a LB |
78 | # ... and here it calls Mininet initialization functions. |
79 | tgen.start_topology() | |
49581587 CH |
80 | |
81 | self.logdir = tgen.logdir | |
9e219b9a | 82 | |
701a0192 | 83 | logger.info("Topology started") |
9e219b9a | 84 | try: |
89b9abd9 LB |
85 | self.prestarthooksuccess = customize.ltemplatePreRouterStartHook() |
86 | except AttributeError: | |
701a0192 | 87 | # not defined |
9e219b9a | 88 | logger.debug("ltemplatePreRouterStartHook() not defined") |
af01532c | 89 | if self.prestarthooksuccess != True: |
701a0192 | 90 | logger.info("ltemplatePreRouterStartHook() failed, skipping test") |
af01532c | 91 | return |
9e219b9a LB |
92 | |
93 | # This is a sample of configuration loading. | |
94 | router_list = tgen.routers() | |
95 | ||
b515c81a | 96 | # For all registered routers, load the zebra configuration file |
e5f0ed14 | 97 | for rname, router in router_list.items(): |
a38f0083 LB |
98 | logger.info("Setting up %s" % rname) |
99 | for rd_val in TopoRouter.RD: | |
701a0192 | 100 | config = os.path.join( |
101 | self.testdir, "{}/{}.conf".format(rname, TopoRouter.RD[rd_val]) | |
102 | ) | |
a38f0083 LB |
103 | prog = os.path.join(tgen.net[rname].daemondir, TopoRouter.RD[rd_val]) |
104 | if os.path.exists(config): | |
105 | if os.path.exists(prog): | |
106 | router.load_config(rd_val, config) | |
107 | else: | |
701a0192 | 108 | logger.warning( |
109 | "{} not found, but have {}.conf file".format( | |
110 | prog, TopoRouter.RD[rd_val] | |
111 | ) | |
112 | ) | |
9e219b9a LB |
113 | |
114 | # After loading the configurations, this function loads configured daemons. | |
701a0192 | 115 | logger.info("Starting routers") |
9e219b9a LB |
116 | tgen.start_router() |
117 | try: | |
89b9abd9 LB |
118 | self.poststarthooksuccess = customize.ltemplatePostRouterStartHook() |
119 | except AttributeError: | |
701a0192 | 120 | # not defined |
9e219b9a | 121 | logger.debug("ltemplatePostRouterStartHook() not defined") |
850de337 | 122 | luStart(baseScriptDir=self.scriptdir, baseLogDir=self.logdir, net=tgen.net) |
9e219b9a | 123 | |
701a0192 | 124 | |
125 | # initialized by ltemplate_start | |
9e219b9a | 126 | _lt = None |
df437d25 | 127 | |
701a0192 | 128 | |
df437d25 | 129 | def setup_module(mod): |
9e219b9a LB |
130 | global _lt |
131 | root = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) | |
701a0192 | 132 | test = mod.__name__[: mod.__name__.rfind(".")] |
9e219b9a LB |
133 | testdir = os.path.join(root, test) |
134 | ||
701a0192 | 135 | # don't do this for now as reload didn't work as expected |
136 | # fixup sys.path, want test dir there only once | |
137 | # try: | |
9e219b9a | 138 | # sys.path.remove(testdir) |
701a0192 | 139 | # except ValueError: |
9e219b9a | 140 | # logger.debug(testdir+" not found in original sys.path") |
701a0192 | 141 | # add testdir |
142 | # sys.path.append(testdir) | |
9e219b9a | 143 | |
701a0192 | 144 | # init class |
9e219b9a LB |
145 | _lt = LTemplate(test, testdir) |
146 | _lt.setup_module(mod) | |
147 | ||
701a0192 | 148 | # drop testdir |
149 | # sys.path.remove(testdir) | |
150 | ||
df437d25 LB |
151 | |
152 | def teardown_module(mod): | |
850de337 | 153 | global _lt |
df437d25 LB |
154 | "Teardown the pytest environment" |
155 | tgen = get_topogen() | |
156 | ||
af01532c | 157 | if _lt != None and _lt.scriptdir != None and _lt.prestarthooksuccess == True: |
dccb75bb | 158 | luShowResults(logger.info) |
850de337 LB |
159 | print(luFinish()) |
160 | ||
df437d25 LB |
161 | # This function tears down the whole topology. |
162 | tgen.stop_topology() | |
850de337 | 163 | _lt = None |
df437d25 | 164 | |
701a0192 | 165 | |
166 | def ltemplateTest( | |
167 | script, SkipIfFailed=True, CallOnFail=None, CheckFuncStr=None, KeepGoing=False | |
168 | ): | |
af01532c LB |
169 | global _lt |
170 | if _lt == None or _lt.prestarthooksuccess != True: | |
171 | return | |
172 | ||
df437d25 LB |
173 | tgen = get_topogen() |
174 | if not os.path.isfile(script): | |
175 | if not os.path.isfile(os.path.join(_lt.scriptdir, script)): | |
701a0192 | 176 | logger.error("Could not find script file: " + script) |
177 | assert "Could not find script file: " + script | |
df437d25 LB |
178 | logger.info("Starting template test: " + script) |
179 | numEntry = luNumFail() | |
180 | ||
181 | if SkipIfFailed and tgen.routers_have_failure(): | |
182 | pytest.skip(tgen.errors) | |
183 | if numEntry > 0: | |
11761ab0 MS |
184 | if not KeepGoing: |
185 | pytest.skip("Have %d errors" % numEntry) | |
df437d25 LB |
186 | |
187 | if CheckFuncStr != None: | |
188 | check = eval(CheckFuncStr) | |
189 | if check != True: | |
701a0192 | 190 | pytest.skip("Check function '" + CheckFuncStr + "' returned: " + check) |
df437d25 | 191 | |
8ab91133 LB |
192 | if CallOnFail != None: |
193 | CallOnFail = eval(CallOnFail) | |
194 | luInclude(script, CallOnFail) | |
df437d25 LB |
195 | numFail = luNumFail() - numEntry |
196 | if numFail > 0: | |
197 | luShowFail() | |
198 | fatal_error = "%d tests failed" % numFail | |
11761ab0 | 199 | if not KeepGoing: |
701a0192 | 200 | assert ( |
201 | "scripts/cleanup_all.py failed" == "See summary output above" | |
202 | ), fatal_error | |
203 | ||
df437d25 LB |
204 | |
205 | # Memory leak test template | |
206 | def test_memory_leak(): | |
207 | "Run the memory leak test and report results." | |
208 | tgen = get_topogen() | |
209 | if not tgen.is_memleak_enabled(): | |
701a0192 | 210 | pytest.skip("Memory leak test/report is disabled") |
df437d25 LB |
211 | |
212 | tgen.report_memory_leaks() | |
213 | ||
701a0192 | 214 | |
215 | class ltemplateRtrCmd: | |
89b9abd9 LB |
216 | def __init__(self): |
217 | self.resetCounts() | |
218 | ||
701a0192 | 219 | def doCmd(self, tgen, rtr, cmd, checkstr=None): |
1b9ebabb | 220 | logger.info("doCmd: {} {}".format(rtr, cmd)) |
89b9abd9 LB |
221 | output = tgen.net[rtr].cmd(cmd).strip() |
222 | if len(output): | |
223 | self.output += 1 | |
224 | if checkstr != None: | |
225 | ret = re.search(checkstr, output) | |
226 | if ret == None: | |
227 | self.nomatch += 1 | |
228 | else: | |
229 | self.match += 1 | |
230 | return ret | |
701a0192 | 231 | logger.info("output: " + output) |
1b9ebabb LB |
232 | else: |
233 | logger.info("No output") | |
234 | self.none += 1 | |
89b9abd9 LB |
235 | return None |
236 | ||
237 | def resetCounts(self): | |
238 | self.match = 0 | |
239 | self.nomatch = 0 | |
240 | self.output = 0 | |
241 | self.none = 0 | |
242 | ||
243 | def getMatch(self): | |
244 | return self.match | |
245 | ||
246 | def getNoMatch(self): | |
247 | return self.nomatch | |
248 | ||
249 | def getOutput(self): | |
250 | return self.output | |
251 | ||
252 | def getNone(self): | |
253 | return self.none | |
254 | ||
701a0192 | 255 | |
256 | def ltemplateVersionCheck( | |
257 | vstr, rname="r1", compstr="<", cli=False, kernel="4.9", iproute2=None, mpls=True | |
258 | ): | |
89b9abd9 LB |
259 | tgen = get_topogen() |
260 | router = tgen.gears[rname] | |
261 | ||
262 | if cli: | |
701a0192 | 263 | logger.info("calling mininet CLI") |
89b9abd9 | 264 | tgen.mininet_cli() |
701a0192 | 265 | logger.info("exited mininet CLI") |
89b9abd9 LB |
266 | |
267 | if _lt == None: | |
701a0192 | 268 | ret = "Template not initialized" |
89b9abd9 LB |
269 | return ret |
270 | ||
271 | if _lt.prestarthooksuccess != True: | |
701a0192 | 272 | ret = "ltemplatePreRouterStartHook failed" |
89b9abd9 LB |
273 | return ret |
274 | ||
275 | if _lt.poststarthooksuccess != True: | |
701a0192 | 276 | ret = "ltemplatePostRouterStartHook failed" |
89b9abd9 LB |
277 | return ret |
278 | ||
279 | if mpls == True and tgen.hasmpls != True: | |
701a0192 | 280 | ret = "MPLS not initialized" |
89b9abd9 LB |
281 | return ret |
282 | ||
283 | if kernel != None: | |
284 | krel = platform.release() | |
285 | if topotest.version_cmp(krel, kernel) < 0: | |
701a0192 | 286 | ret = "Skipping tests, old kernel ({} < {})".format(krel, kernel) |
89b9abd9 LB |
287 | return ret |
288 | ||
289 | if iproute2 != None: | |
290 | if _lt.iproute2Ver == None: | |
701a0192 | 291 | # collect/log info on iproute2 |
89b9abd9 | 292 | cc = ltemplateRtrCmd() |
701a0192 | 293 | found = cc.doCmd( |
48f05b23 | 294 | tgen, rname, "apt-cache policy iproute2", r"Installed: ([\d\.]*)" |
701a0192 | 295 | ) |
89b9abd9 LB |
296 | if found != None: |
297 | iproute2Ver = found.group(1) | |
298 | else: | |
701a0192 | 299 | iproute2Ver = "0-unknown" |
300 | logger.info("Have iproute2 version=" + iproute2Ver) | |
89b9abd9 LB |
301 | |
302 | if topotest.version_cmp(iproute2Ver, iproute2) < 0: | |
701a0192 | 303 | ret = "Skipping tests, old iproute2 ({} < {})".format(iproute2Ver, iproute2) |
89b9abd9 LB |
304 | return ret |
305 | ||
306 | ret = True | |
307 | try: | |
308 | if router.has_version(compstr, vstr): | |
701a0192 | 309 | ret = "Skipping tests, old FRR version {} {}".format(compstr, vstr) |
89b9abd9 LB |
310 | return ret |
311 | except: | |
312 | ret = True | |
313 | ||
314 | return ret | |
315 | ||
701a0192 | 316 | |
317 | # for testing | |
318 | if __name__ == "__main__": | |
df437d25 LB |
319 | args = ["-s"] + sys.argv[1:] |
320 | sys.exit(pytest.main(args)) |