ip-neighbor: do not use sas to determine NS source address
[vpp.git] / test / config.py
1 import argparse
2 import os
3 import psutil
4 import time
5 from vpp_qemu_utils import can_create_namespaces
6
7
8 def positive_int_or_default(default):
9     def positive_integer(v):
10         if v is None or v == "":
11             return default
12         if int(v) <= 0:
13             raise ValueError("value must be positive")
14         return int(v)
15
16     return positive_integer
17
18
19 def positive_float_or_default(default):
20     def positive_float(v):
21         if v is None or v == "":
22             return default
23         if float(v) <= 0:
24             raise ValueError("value must be positive")
25         return float(v)
26
27     return positive_float
28
29
30 def positive_int_or_auto(v):
31     if v is None or v in ("", "auto"):
32         return "auto"
33     if int(v) <= 0:
34         raise ValueError("value must be positive or auto")
35     return int(v)
36
37
38 def int_or_auto(v):
39     if v is None or v in ("", "auto"):
40         return "auto"
41     if int(v) < 0:
42         raise ValueError("value must be positive or auto")
43     return int(v)
44
45
46 def int_choice_or_default(options, default):
47     assert default in options
48
49     def choice(v):
50         if v is None or v == "":
51             return default
52         if int(v) in options:
53             return int(v)
54         raise ValueError("invalid choice")
55
56     return choice
57
58
59 def worker_config(v):
60     if v is None or v == "":
61         return 0
62     if v.startswith("workers "):
63         return int(v.split(" ")[1])
64     return int(v)
65
66
67 def directory(v):
68     if not os.path.isdir(v):
69         raise ValueError(f"provided path '{v}' doesn't exist or is not a directory")
70     return v
71
72
73 def directory_verify_or_create(v):
74     if not os.path.isdir(v):
75         os.mkdir(v)
76     return v
77
78
79 parser = argparse.ArgumentParser(
80     description="VPP unit tests", formatter_class=argparse.RawTextHelpFormatter
81 )
82
83 parser.add_argument(
84     "--failfast", action="store_true", help="stop running tests on first failure"
85 )
86
87 parser.add_argument(
88     "--test-src-dir",
89     action="append",
90     type=directory,
91     help="directory containing test files "
92     "(may be specified multiple times) "
93     "(VPP_WS_DIR/test is added automatically to the set)",
94 )
95
96 default_verbose = 0
97
98 parser.add_argument(
99     "--verbose",
100     action="store",
101     default=default_verbose,
102     type=int_choice_or_default((0, 1, 2), default_verbose),
103     help="verbosity setting - 0 - least verbose, 2 - most verbose (default: 0)",
104 )
105
106 default_test_run_timeout = 600
107
108 parser.add_argument(
109     "--timeout",
110     action="store",
111     type=positive_int_or_default(default_test_run_timeout),
112     default=default_test_run_timeout,
113     metavar="TEST_RUN_TIMEOUT",
114     help="test run timeout in seconds - per test "
115     f"(default: {default_test_run_timeout})",
116 )
117
118 parser.add_argument(
119     "--failed-dir",
120     action="store",
121     type=directory,
122     help="directory containing failed tests (default: --tmp-dir)",
123 )
124
125 filter_help_string = """\
126 expression consists of one or more filters separated by commas (',')
127 filter consists of 3 string selectors separated by dots ('.')
128
129     <file>.<class>.<function>
130
131 - selectors restrict which files/classes/functions are run
132 - selector can be replaced with '*' or omitted entirely if not needed
133 - <file> selector is automatically prepended with 'test_' if required
134 - '.' separators are required only if selector(s) follow(s)
135
136 examples:
137
138 1. all of the following expressions are equivalent and will select
139    all test classes and functions from test_bfd.py:
140    'test_bfd' 'bfd' 'test_bfd..' 'bfd.' 'bfd.*.*' 'test_bfd.*.*'
141 2. 'bfd.BFDAPITestCase' selects all tests from test_bfd.py,
142    which are part of BFDAPITestCase class
143 3. 'bfd.BFDAPITestCase.test_add_bfd' selects a single test named
144    test_add_bfd from test_bfd.py/BFDAPITestCase
145 4. '.*.test_add_bfd' selects all test functions named test_add_bfd
146    from all files/classes
147 5. 'bfd,ip4,..test_icmp_error' selects all test functions in test_bfd.py,
148    test_ip4.py and all test functions named 'test_icmp_error' in all files
149 """
150 parser.add_argument(
151     "--filter", action="store", metavar="FILTER_EXPRESSION", help=filter_help_string
152 )
153
154 default_retries = 0
155
156 parser.add_argument(
157     "--retries",
158     action="store",
159     default=default_retries,
160     type=positive_int_or_default(default_retries),
161     help="retry failed tests RETRIES times",
162 )
163
164 parser.add_argument(
165     "--step", action="store_true", default=False, help="enable stepping through tests"
166 )
167
168 debug_help_string = """\
169 attach     - attach to already running vpp
170 core       - detect coredump and load core in gdb on crash
171 gdb        - print VPP PID and pause allowing attaching gdb
172 gdbserver  - same as above, but run gdb in gdbserver
173 """
174
175 parser.add_argument(
176     "--debug",
177     action="store",
178     choices=["attach", "core", "gdb", "gdbserver"],
179     help=debug_help_string,
180 )
181
182 parser.add_argument(
183     "--debug-framework",
184     action="store_true",
185     help="enable internal test framework debugging",
186 )
187
188 parser.add_argument(
189     "--compress-core",
190     action="store_true",
191     help="compress core files if not debugging them",
192 )
193
194 parser.add_argument("--extended", action="store_true", help="run extended tests")
195 parser.add_argument(
196     "--skip-netns-tests",
197     action="store_true",
198     help="skip tests involving netns operations",
199 )
200
201 parser.add_argument(
202     "--sanity", action="store_true", help="perform sanity vpp run before running tests"
203 )
204
205 parser.add_argument(
206     "--force-foreground",
207     action="store_true",
208     help="force running in foreground - don't fork",
209 )
210
211 parser.add_argument(
212     "--jobs",
213     action="store",
214     type=positive_int_or_auto,
215     default="auto",
216     help="maximum concurrent test jobs",
217 )
218
219 parser.add_argument(
220     "--venv-dir", action="store", type=directory, help="path to virtual environment"
221 )
222
223 default_rnd_seed = time.time()
224 parser.add_argument(
225     "--rnd-seed",
226     action="store",
227     default=default_rnd_seed,
228     type=positive_float_or_default(default_rnd_seed),
229     help="random generator seed (default: current time)",
230 )
231
232 parser.add_argument(
233     "--vpp-worker-count",
234     action="store",
235     type=worker_config,
236     default=0,
237     help="number of vpp workers",
238 )
239
240 parser.add_argument(
241     "--gcov", action="store_true", default=False, help="running gcov tests"
242 )
243
244 parser.add_argument(
245     "--cache-vpp-output",
246     action="store_true",
247     default=False,
248     help="cache VPP stdout/stderr and log as one block after test finishes",
249 )
250
251 parser.add_argument(
252     "--vpp-ws-dir",
253     action="store",
254     required=True,
255     type=directory,
256     help="vpp workspace directory",
257 )
258
259 parser.add_argument(
260     "--vpp-tag",
261     action="store",
262     default="vpp_debug",
263     metavar="VPP_TAG",
264     required=True,
265     help="vpp tag (e.g. vpp, vpp_debug, vpp_gcov)",
266 )
267
268 parser.add_argument(
269     "--vpp",
270     action="store",
271     help="path to vpp binary (default: derive from VPP_WS_DIR and VPP_TAG)",
272 )
273
274 parser.add_argument(
275     "--vpp-install-dir",
276     type=directory,
277     action="store",
278     help="path to vpp install directory"
279     "(default: derive from VPP_WS_DIR and VPP_TAG)",
280 )
281
282 parser.add_argument(
283     "--vpp-build-dir",
284     action="store",
285     type=directory,
286     help="vpp build directory (default: derive from VPP_WS_DIR and VPP_TAG)",
287 )
288
289 parser.add_argument(
290     "--vpp-plugin-dir",
291     action="append",
292     type=directory,
293     help="directory containing vpp plugins"
294     "(default: derive from VPP_WS_DIR and VPP_TAG)",
295 )
296
297 parser.add_argument(
298     "--vpp-test-plugin-dir",
299     action="append",
300     type=directory,
301     help="directory containing vpp api test plugins"
302     "(default: derive from VPP_WS_DIR and VPP_TAG)",
303 )
304
305 parser.add_argument(
306     "--extern-plugin-dir",
307     action="append",
308     type=directory,
309     default=[],
310     help="directory containing external plugins",
311 )
312
313 parser.add_argument(
314     "--extern-apidir",
315     action="append",
316     type=directory,
317     default=[],
318     help="directory to look for API JSON files",
319 )
320
321 parser.add_argument(
322     "--coredump-size",
323     action="store",
324     default="unlimited",
325     help="specify vpp coredump size",
326 )
327
328 parser.add_argument(
329     "--max-vpp-cpus",
330     action="store",
331     type=int_or_auto,
332     default=0,
333     help="max cpus used by vpp",
334 )
335
336 variant_help_string = """\
337 specify which march node variant to unit test
338   e.g. --variant=skx - test the skx march variants
339   e.g. --variant=icl - test the icl march variants
340 """
341
342 parser.add_argument("--variant", action="store", help=variant_help_string)
343
344 parser.add_argument(
345     "--api-fuzz", action="store", default=None, help="specify api fuzzing parameters"
346 )
347
348 parser.add_argument(
349     "--wipe-tmp-dir",
350     action="store_true",
351     default=True,
352     help="remove test tmp directory before running test",
353 )
354
355 parser.add_argument(
356     "--tmp-dir",
357     action="store",
358     default="/tmp",
359     type=directory_verify_or_create,
360     help="directory where to store test temporary directories",
361 )
362
363 parser.add_argument(
364     "--log-dir",
365     action="store",
366     type=directory_verify_or_create,
367     help="directory where to store directories "
368     "containing log files (default: --tmp-dir)",
369 )
370
371 default_keep_pcaps = False
372 parser.add_argument(
373     "--keep-pcaps",
374     action="store_true",
375     default=default_keep_pcaps,
376     help=f"if set, keep all pcap files from a test run (default: {default_keep_pcaps})",
377 )
378
379 parser.add_argument(
380     "-r",
381     "--use-running-vpp",
382     dest="running_vpp",
383     required=False,
384     action="store_true",
385     default=False,
386     help="Runs tests against a running VPP.",
387 )
388
389 parser.add_argument(
390     "--excluded-plugin",
391     dest="excluded_plugins",
392     required=False,
393     action="append",
394     default=[],
395     help="Exclude the tests that indicate they require this plugin(s)",
396 )
397
398 parser.add_argument(
399     "-d",
400     "--socket-dir",
401     dest="socket_dir",
402     required=False,
403     action="store",
404     default="",
405     help="Relative or absolute path to running VPP's socket directory.\n"
406     "The directory must contain VPP's socket files:api.sock & stats.sock.\n"
407     "Default: /var/run/vpp if VPP is started as the root user, else "
408     "/var/run/user/${uid}/vpp.",
409 )
410
411 config = parser.parse_args()
412
413 ws = config.vpp_ws_dir
414 br = f"{ws}/build-root"
415 tag = config.vpp_tag
416
417 if config.vpp_install_dir is None:
418     config.vpp_install_dir = f"{br}/install-{tag}-native"
419
420 if config.vpp is None:
421     config.vpp = f"{config.vpp_install_dir}/vpp/bin/vpp"
422
423 if config.vpp_build_dir is None:
424     config.vpp_build_dir = f"{br}/build-{tag}-native"
425
426 libs = ["lib", "lib64"]
427
428 if config.vpp_plugin_dir is None:
429     config.vpp_plugin_dir = [
430         f"{config.vpp_install_dir}/vpp/{lib}/vpp_plugins" for lib in libs
431     ]
432
433 if config.vpp_test_plugin_dir is None:
434     config.vpp_test_plugin_dir = [
435         f"{config.vpp_install_dir}/vpp/{lib}/vpp_api_test_plugins" for lib in libs
436     ]
437
438 test_dirs = [f"{ws}/test"]
439
440 if config.test_src_dir is not None:
441     test_dirs.extend(config.test_src_dir)
442
443 config.test_src_dir = test_dirs
444
445
446 if config.venv_dir is None:
447     config.venv_dir = f"{ws}/build-root/test/venv"
448
449 if config.failed_dir is None:
450     config.failed_dir = f"{config.tmp_dir}"
451
452 available_cpus = psutil.Process().cpu_affinity()
453 num_cpus = len(available_cpus)
454
455 if config.max_vpp_cpus == "auto":
456     max_vpp_cpus = num_cpus
457 elif config.max_vpp_cpus > 0:
458     max_vpp_cpus = min(config.max_vpp_cpus, num_cpus)
459 else:
460     max_vpp_cpus = num_cpus
461
462 if not config.skip_netns_tests:
463     if not can_create_namespaces():
464         config.skip_netns_tests = True
465
466 if __name__ == "__main__":
467     print("Provided arguments:")
468     for i in config.__dict__:
469         print(f"  {i} is {config.__dict__[i]}")