]> git.proxmox.com Git - mirror_zfs.git/blame - tests/test-runner/bin/zts-report.py.in
zfs_rename: restructure to have cleaner fallbacks
[mirror_zfs.git] / tests / test-runner / bin / zts-report.py.in
CommitLineData
38e2e9ce 1#!/usr/bin/env @PYTHON_SHEBANG@
e4a3297a
BB
2
3#
4# This file and its contents are supplied under the terms of the
5# Common Development and Distribution License ("CDDL"), version 1.0.
6# You may only use this file in accordance with the terms of version
7# 1.0 of the CDDL.
8#
9# A full copy of the text of the CDDL should have accompanied this
10# source. A copy of the CDDL is also available via the Internet at
11# http://www.illumos.org/license/CDDL.
12#
13
14#
15# Copyright (c) 2017 by Delphix. All rights reserved.
16# Copyright (c) 2018 by Lawrence Livermore National Security, LLC.
17#
8a7c4efd 18# This script must remain compatible with Python 3.6+.
6e72a5b9 19#
e4a3297a
BB
20
21import os
22import re
23import sys
2320e6eb 24import argparse
e4a3297a
BB
25
26#
27# This script parses the stdout of zfstest, which has this format:
28#
29# Test: /path/to/testa (run as root) [00:00] [PASS]
30# Test: /path/to/testb (run as jkennedy) [00:00] [PASS]
31# Test: /path/to/testc (run as root) [00:00] [FAIL]
32# [...many more results...]
33#
34# Results Summary
35# FAIL 22
36# SKIP 32
37# PASS 1156
38#
39# Running Time: 02:50:31
40# Percent passed: 95.5%
41# Log directory: /var/tmp/test_results/20180615T205926
42#
43
44#
45# Common generic reasons for a test or test group to be skipped.
46#
47# Some test cases are known to fail in ways which are not harmful or dangerous.
48# In these cases simply mark the test as a known failure until it can be
49# updated and the issue resolved. Note that it's preferable to open a unique
50# issue on the GitHub issue tracker for each test case failure.
51#
52known_reason = 'Known issue'
53
54#
55# Some tests require that a test user be able to execute the zfs utilities.
56# This may not be possible when testing in-tree due to the default permissions
57# on the user's home directory. When testing this can be resolved by granting
58# group read access.
59#
60# chmod 0750 $HOME
61#
62exec_reason = 'Test user execute permissions required for utilities'
63
e4a3297a 64#
8a7c4efd 65# Some tests require a minimum python version of 3.6 and will be skipped when
e4a3297a 66# the default system version is too old. There may also be tests which require
8a7c4efd 67# additional python modules be installed, for example python3-cffi is required
e4a3297a
BB
68# by the pyzfs tests.
69#
8a7c4efd 70python_deps_reason = 'Python modules missing: python3-cffi'
e4a3297a
BB
71
72#
73# Some tests require the O_TMPFILE flag which was first introduced in the
74# 3.11 kernel.
75#
76tmpfile_reason = 'Kernel O_TMPFILE support required'
77
8ae86e2e
RM
78#
79# Some tests require the statx(2) system call on Linux which was first
80# introduced in the 4.11 kernel.
81#
82statx_reason = 'Kernel statx(2) system call required on Linux'
83
e4a3297a
BB
84#
85# Some tests require that the lsattr utility support the project id feature.
86#
87project_id_reason = 'lsattr with set/show project ID required'
88
89#
90# Some tests require that the kernel support user namespaces.
91#
92user_ns_reason = 'Kernel user namespace support required'
93
94#
95# Some rewind tests can fail since nothing guarantees that old MOS blocks
96# are not overwritten. Snapshots protect datasets and data files but not
97# the MOS. Reasonable efforts are made in the test case to increase the
98# odds that some txgs will have their MOS data left untouched, but it is
99# never a sure thing.
100#
101rewind_reason = 'Arbitrary pool rewind is not guaranteed'
102
a584ef26
BB
103#
104# Some tests require a minimum version of the fio benchmark utility.
105# Older distributions such as CentOS 6.x only provide fio-2.0.13.
106#
107fio_reason = 'Fio v2.3 or newer required'
108
1b939560
BB
109#
110# Some tests require that the DISKS provided support the discard operation.
111# Normally this is not an issue because loop back devices are used for DISKS
112# and they support discard (TRIM/UNMAP).
113#
114trim_reason = 'DISKS must support discard (TRIM/UNMAP)'
115
c3cb57ae
KHN
116#
117# Some tests on FreeBSD require the fspacectl(2) system call and the
118# truncate(1) utility supporting the -d option. The system call was first
119# introduced in FreeBSD version 1400032.
120#
121fspacectl_reason = 'fspacectl(2) and truncate -d support required'
122
e4a3297a 123#
4b06d052
BB
124# Some tests are not applicable to a platform or need to be updated to operate
125# in the manor required by the platform. Any tests which are skipped for this
e4a3297a
BB
126# reason will be suppressed in the final analysis output.
127#
4b06d052 128na_reason = "Not applicable"
e4a3297a 129
9eee7fce
GM
130#
131# Some test cases doesn't have all requirements to run on Github actions CI.
132#
133ci_reason = 'CI runner doesn\'t have all requirements'
134
2a068a13
YY
135#
136# Idmapped mount is only supported in kernel version >= 5.12
137#
138idmap_reason = 'Idmapped mount needs kernel 5.12+'
e4a3297a
BB
139
140#
141# These tests are known to fail, thus we use this list to prevent these
142# failures from failing the job as a whole; only unexpected failures
143# bubble up to cause this script to exit with a non-zero exit status.
144#
145# Format: { 'test-name': ['expected result', 'issue-number | reason'] }
146#
147# For each known failure it is recommended to link to a GitHub issue by
148# setting the reason to the issue number. Alternately, one of the generic
149# reasons listed above can be used.
150#
151known = {
e78b7a73
AZ
152 'casenorm/mixed_none_lookup_ci': ['FAIL', 7633],
153 'casenorm/mixed_formd_lookup_ci': ['FAIL', 7633],
14ba514a
BB
154 'cli_root/zpool_import/import_rewind_device_replaced':
155 ['FAIL', rewind_reason],
e4a3297a
BB
156 'cli_user/misc/zfs_share_001_neg': ['SKIP', na_reason],
157 'cli_user/misc/zfs_unshare_001_neg': ['SKIP', na_reason],
e4a3297a
BB
158 'privilege/setup': ['SKIP', na_reason],
159 'refreserv/refreserv_004_pos': ['FAIL', known_reason],
e4a3297a 160 'rootpool/setup': ['SKIP', na_reason],
e78b7a73 161 'rsend/rsend_008_pos': ['SKIP', 6066],
e4a3297a 162 'vdev_zaps/vdev_zaps_007_pos': ['FAIL', known_reason],
e4a3297a
BB
163}
164
4b06d052
BB
165if sys.platform.startswith('freebsd'):
166 known.update({
19229e5f
BB
167 'cli_root/zfs_receive/receive-o-x_props_override':
168 ['FAIL', known_reason],
788398c5
RM
169 'cli_root/zpool_wait/zpool_wait_trim_basic': ['SKIP', trim_reason],
170 'cli_root/zpool_wait/zpool_wait_trim_cancel': ['SKIP', trim_reason],
171 'cli_root/zpool_wait/zpool_wait_trim_flag': ['SKIP', trim_reason],
88d5580e 172 'cli_root/zfs_unshare/zfs_unshare_008_pos': ['SKIP', na_reason],
4b06d052 173 'link_count/link_count_001': ['SKIP', na_reason],
533aef2b 174 'casenorm/mixed_create_failure': ['FAIL', 13215],
411f4a01 175 'mmap/mmap_sync_001_pos': ['SKIP', na_reason],
4b06d052 176 })
d3fe62cb
RM
177elif sys.platform.startswith('linux'):
178 known.update({
e78b7a73
AZ
179 'casenorm/mixed_formd_lookup': ['FAIL', 7633],
180 'casenorm/mixed_formd_delete': ['FAIL', 7633],
181 'casenorm/sensitive_formd_lookup': ['FAIL', 7633],
182 'casenorm/sensitive_formd_delete': ['FAIL', 7633],
d3fe62cb 183 'removal/removal_with_zdb': ['SKIP', known_reason],
6b261643 184 'cli_root/zfs_unshare/zfs_unshare_002_pos': ['SKIP', na_reason],
d3fe62cb
RM
185 })
186
4b06d052 187
e4a3297a
BB
188#
189# These tests may occasionally fail or be skipped. We want there failures
190# to be reported but only unexpected failures should bubble up to cause
191# this script to exit with a non-zero exit status.
192#
193# Format: { 'test-name': ['expected result', 'issue-number | reason'] }
194#
195# For each known failure it is recommended to link to a GitHub issue by
196# setting the reason to the issue number. Alternately, one of the generic
197# reasons listed above can be used.
198#
199maybe = {
e4a3297a 200 'chattr/setup': ['SKIP', exec_reason],
8ae86e2e 201 'crtime/crtime_001_pos': ['SKIP', statx_reason],
e4a3297a 202 'cli_root/zdb/zdb_006_pos': ['FAIL', known_reason],
7d07d1be
BB
203 'cli_root/zfs_destroy/zfs_destroy_dev_removal_condense':
204 ['FAIL', known_reason],
e4a3297a 205 'cli_root/zfs_get/zfs_get_004_pos': ['FAIL', known_reason],
e78b7a73 206 'cli_root/zfs_get/zfs_get_009_pos': ['SKIP', 5479],
7d07d1be
BB
207 'cli_root/zfs_rollback/zfs_rollback_001_pos': ['FAIL', known_reason],
208 'cli_root/zfs_rollback/zfs_rollback_002_pos': ['FAIL', known_reason],
e4a3297a 209 'cli_root/zfs_snapshot/zfs_snapshot_002_neg': ['FAIL', known_reason],
3886e708 210 'cli_root/zfs_unshare/zfs_unshare_006_pos': ['SKIP', na_reason],
e4a3297a 211 'cli_root/zpool_add/zpool_add_004_pos': ['FAIL', known_reason],
e78b7a73
AZ
212 'cli_root/zpool_destroy/zpool_destroy_001_pos': ['SKIP', 6145],
213 'cli_root/zpool_import/zpool_import_missing_003_pos': ['SKIP', 6839],
b1f73412 214 'cli_root/zpool_initialize/zpool_initialize_import_export':
e78b7a73 215 ['FAIL', 11948],
7d07d1be
BB
216 'cli_root/zpool_labelclear/zpool_labelclear_removed':
217 ['FAIL', known_reason],
1b939560 218 'cli_root/zpool_trim/setup': ['SKIP', trim_reason],
e78b7a73 219 'cli_root/zpool_upgrade/zpool_upgrade_004_pos': ['FAIL', 6141],
e4a3297a 220 'delegate/setup': ['SKIP', exec_reason],
c3cb57ae 221 'fallocate/fallocate_punch-hole': ['SKIP', fspacectl_reason],
e78b7a73
AZ
222 'history/history_004_pos': ['FAIL', 7026],
223 'history/history_005_neg': ['FAIL', 6680],
224 'history/history_006_neg': ['FAIL', 5657],
e4a3297a
BB
225 'history/history_008_pos': ['FAIL', known_reason],
226 'history/history_010_pos': ['SKIP', exec_reason],
a584ef26 227 'io/mmap': ['SKIP', fio_reason],
e4a3297a 228 'largest_pool/largest_pool_001_pos': ['FAIL', known_reason],
f1b81537 229 'mmp/mmp_on_uberblocks': ['FAIL', known_reason],
e4a3297a 230 'pyzfs/pyzfs_unittest': ['SKIP', python_deps_reason],
e78b7a73 231 'pool_checkpoint/checkpoint_discard_busy': ['FAIL', 11946],
e4a3297a 232 'projectquota/setup': ['SKIP', exec_reason],
7d07d1be 233 'removal/removal_condense_export': ['FAIL', known_reason],
e78b7a73
AZ
234 'reservation/reservation_008_pos': ['FAIL', 7741],
235 'reservation/reservation_018_pos': ['FAIL', 5642],
e4a3297a 236 'snapshot/clone_001_pos': ['FAIL', known_reason],
e78b7a73
AZ
237 'snapshot/snapshot_009_pos': ['FAIL', 7961],
238 'snapshot/snapshot_010_pos': ['FAIL', 7961],
239 'snapused/snapused_004_pos': ['FAIL', 5513],
e4a3297a 240 'tmpfile/setup': ['SKIP', tmpfile_reason],
f567d67f 241 'append/threadsappend_001_pos': ['FAIL', 6136],
1b939560 242 'trim/setup': ['SKIP', trim_reason],
e4a3297a
BB
243 'upgrade/upgrade_projectquota_001_pos': ['SKIP', project_id_reason],
244 'user_namespace/setup': ['SKIP', user_ns_reason],
245 'userquota/setup': ['SKIP', exec_reason],
e78b7a73 246 'zvol/zvol_ENOSPC/zvol_ENOSPC_001_pos': ['FAIL', 5848],
221e6704 247 'pam/setup': ['SKIP', "pamtester might be not available"],
e4a3297a
BB
248}
249
d3fe62cb
RM
250if sys.platform.startswith('freebsd'):
251 maybe.update({
252 'cli_root/zfs_copies/zfs_copies_002_pos': ['FAIL', known_reason],
9f0a21e6 253 'cli_root/zfs_inherit/zfs_inherit_001_neg': ['FAIL', known_reason],
c15d36c6
GW
254 'cli_root/zfs_share/zfs_share_concurrent_shares':
255 ['FAIL', known_reason],
f1b81537 256 'cli_root/zpool_import/zpool_import_012_pos': ['FAIL', known_reason],
d3fe62cb 257 'delegate/zfs_allow_003_pos': ['FAIL', known_reason],
e78b7a73 258 'inheritance/inherit_001_pos': ['FAIL', 11829],
d3fe62cb 259 'resilver/resilver_restart_001': ['FAIL', known_reason],
e78b7a73
AZ
260 'pool_checkpoint/checkpoint_big_rewind': ['FAIL', 12622],
261 'pool_checkpoint/checkpoint_indirect': ['FAIL', 12623],
d3fe62cb 262 })
745ace3f
BB
263elif sys.platform.startswith('linux'):
264 maybe.update({
745ace3f 265 'cli_root/zfs_rename/zfs_rename_002_pos': ['FAIL', known_reason],
745ace3f 266 'cli_root/zpool_reopen/zpool_reopen_003_pos': ['FAIL', known_reason],
e78b7a73 267 'fault/auto_spare_shared': ['FAIL', 11889],
3d149db1 268 'fault/auto_spare_multiple': ['FAIL', 11889],
716408f5 269 'io/io_uring': ['SKIP', 'io_uring support required'],
e862b7ec
BB
270 'limits/filesystem_limit': ['SKIP', known_reason],
271 'limits/snapshot_limit': ['SKIP', known_reason],
7d07d1be 272 'mmp/mmp_active_import': ['FAIL', known_reason],
d60c0dbd
BB
273 'mmp/mmp_exported_import': ['FAIL', known_reason],
274 'mmp/mmp_inactive_import': ['FAIL', known_reason],
e78b7a73 275 'zvol/zvol_misc/zvol_misc_snapdev': ['FAIL', 12621],
380b0724 276 'zvol/zvol_misc/zvol_misc_volmode': ['FAIL', known_reason],
2a068a13
YY
277 'idmap_mount/idmap_mount_001': ['SKIP', idmap_reason],
278 'idmap_mount/idmap_mount_002': ['SKIP', idmap_reason],
279 'idmap_mount/idmap_mount_003': ['SKIP', idmap_reason],
280 'idmap_mount/idmap_mount_004': ['SKIP', idmap_reason],
745ace3f 281 })
d3fe62cb 282
e4a3297a 283
9eee7fce
GM
284# Not all Github actions runners have scsi_debug module, so we may skip
285# some tests which use it.
286if os.environ.get('CI') == 'true':
287 known.update({
288 'cli_root/zpool_expand/zpool_expand_001_pos': ['SKIP', ci_reason],
289 'cli_root/zpool_expand/zpool_expand_003_neg': ['SKIP', ci_reason],
290 'cli_root/zpool_expand/zpool_expand_005_pos': ['SKIP', ci_reason],
291 'cli_root/zpool_reopen/setup': ['SKIP', ci_reason],
292 'cli_root/zpool_reopen/zpool_reopen_001_pos': ['SKIP', ci_reason],
293 'cli_root/zpool_reopen/zpool_reopen_002_pos': ['SKIP', ci_reason],
294 'cli_root/zpool_reopen/zpool_reopen_003_pos': ['SKIP', ci_reason],
295 'cli_root/zpool_reopen/zpool_reopen_004_pos': ['SKIP', ci_reason],
296 'cli_root/zpool_reopen/zpool_reopen_005_pos': ['SKIP', ci_reason],
297 'cli_root/zpool_reopen/zpool_reopen_006_neg': ['SKIP', ci_reason],
298 'cli_root/zpool_reopen/zpool_reopen_007_pos': ['SKIP', ci_reason],
299 'cli_root/zpool_split/zpool_split_wholedisk': ['SKIP', ci_reason],
300 'fault/auto_offline_001_pos': ['SKIP', ci_reason],
301 'fault/auto_online_001_pos': ['SKIP', ci_reason],
cfc564f9 302 'fault/auto_online_002_pos': ['SKIP', ci_reason],
9eee7fce
GM
303 'fault/auto_replace_001_pos': ['SKIP', ci_reason],
304 'fault/auto_spare_ashift': ['SKIP', ci_reason],
305 'fault/auto_spare_shared': ['SKIP', ci_reason],
306 'procfs/pool_state': ['SKIP', ci_reason],
307 })
308
309 maybe.update({
e78b7a73 310 'events/events_002_pos': ['FAIL', 11546],
9eee7fce
GM
311 })
312
313
e4a3297a
BB
314def process_results(pathname):
315 try:
316 f = open(pathname)
3ed2fbcc 317 except IOError as e:
7b6c4cbb 318 print('Error opening file:', e)
e4a3297a
BB
319 sys.exit(1)
320
321 prefix = '/zfs-tests/tests/functional/'
b74f48fe 322 pattern = \
54aefa6a 323 r'^Test(?:\s+\(\S+\))?:' + \
7b6c4cbb
AZ
324 rf'\s*\S*{prefix}(\S+)' + \
325 r'\s*\(run as (\S+)\)\s*\[(\S+)\]\s*\[(\S+)\]'
b74f48fe 326 pattern_log = r'^\s*Log directory:\s*(\S*)'
e4a3297a
BB
327
328 d = {}
7b6c4cbb 329 logdir = 'Could not determine log directory.'
c87f9586
BB
330 for line in f.readlines():
331 m = re.match(pattern, line)
e4a3297a 332 if m and len(m.groups()) == 4:
e4a3297a
BB
333 d[m.group(1)] = m.group(4)
334 continue
335
c87f9586 336 m = re.match(pattern_log, line)
e4a3297a 337 if m:
7b6c4cbb 338 logdir = m.group(1)
e4a3297a 339
7b6c4cbb 340 return d, logdir
e4a3297a
BB
341
342
2320e6eb
PD
343class ListMaybesAction(argparse.Action):
344 def __init__(self,
345 option_strings,
346 dest="SUPPRESS",
347 default="SUPPRESS",
348 help="list flaky tests and exit"):
349 super(ListMaybesAction, self).__init__(
350 option_strings=option_strings,
351 dest=dest,
352 default=default,
353 nargs=0,
354 help=help)
355
356 def __call__(self, parser, namespace, values, option_string=None):
357 for test in maybe:
358 print(test)
359 sys.exit(0)
360
361
e4a3297a 362if __name__ == "__main__":
2320e6eb
PD
363 parser = argparse.ArgumentParser(description='Analyze ZTS logs')
364 parser.add_argument('logfile')
365 parser.add_argument('--list-maybes', action=ListMaybesAction)
366 parser.add_argument('--no-maybes', action='store_false', dest='maybes')
367 args = parser.parse_args()
368
7b6c4cbb 369 results, logdir = process_results(args.logfile)
e4a3297a 370
7b6c4cbb 371 if not results:
3ed2fbcc 372 print("\n\nNo test results were found.")
7b6c4cbb 373 print("Log directory:", logdir)
e4a3297a
BB
374 sys.exit(0)
375
376 expected = []
377 unexpected = []
2320e6eb 378 all_maybes = True
e4a3297a 379
3ed2fbcc 380 for test in list(results.keys()):
e4a3297a
BB
381 if results[test] == "PASS":
382 continue
383
384 setup = test.replace(os.path.basename(test), "setup")
385 if results[test] == "SKIP" and test != setup:
386 if setup in known and known[setup][0] == "SKIP":
387 continue
388 if setup in maybe and maybe[setup][0] == "SKIP":
389 continue
390
2320e6eb 391 if (test in known and results[test] in known[test][0]):
e4a3297a 392 expected.append(test)
2320e6eb
PD
393 elif test in maybe and results[test] in maybe[test][0]:
394 if results[test] == 'SKIP' or args.maybes:
395 expected.append(test)
396 elif not args.maybes:
397 unexpected.append(test)
398 else:
399 unexpected.append(test)
400 all_maybes = False
e4a3297a 401
3ed2fbcc 402 print("\nTests with results other than PASS that are expected:")
e4a3297a 403 for test in sorted(expected):
1289fbb5 404 issue_url = 'https://github.com/openzfs/zfs/issues/'
e4a3297a
BB
405
406 # Include the reason why the result is expected, given the following:
4b06d052 407 # 1. Suppress test results which set the "Not applicable" reason.
e4a3297a
BB
408 # 2. Numerical reasons are assumed to be GitHub issue numbers.
409 # 3. When an entire test group is skipped only report the setup reason.
410 if test in known:
411 if known[test][1] == na_reason:
412 continue
e78b7a73
AZ
413 elif isinstance(known[test][1], int):
414 expect = f"{issue_url}{known[test][1]}"
e4a3297a
BB
415 else:
416 expect = known[test][1]
417 elif test in maybe:
e78b7a73
AZ
418 if isinstance(maybe[test][1], int):
419 expect = f"{issue_url}{maybe[test][1]}"
e4a3297a
BB
420 else:
421 expect = maybe[test][1]
422 elif setup in known and known[setup][0] == "SKIP" and setup != test:
423 continue
424 elif setup in maybe and maybe[setup][0] == "SKIP" and setup != test:
425 continue
426 else:
427 expect = "UNKNOWN REASON"
7b6c4cbb 428 print(f" {results[test]} {test} ({expect})")
e4a3297a 429
3ed2fbcc 430 print("\nTests with result of PASS that are unexpected:")
e4a3297a
BB
431 for test in sorted(known.keys()):
432 # We probably should not be silently ignoring the case
433 # where "test" is not in "results".
434 if test not in results or results[test] != "PASS":
435 continue
7b6c4cbb 436 print(f" {results[test]} {test} (expected {known[test][0]})")
e4a3297a 437
3ed2fbcc 438 print("\nTests with results other than PASS that are unexpected:")
e4a3297a
BB
439 for test in sorted(unexpected):
440 expect = "PASS" if test not in known else known[test][0]
7b6c4cbb 441 print(f" {results[test]} {test} (expected {expect})")
e4a3297a
BB
442
443 if len(unexpected) == 0:
444 sys.exit(0)
2320e6eb
PD
445 elif not args.maybes and all_maybes:
446 sys.exit(2)
e4a3297a
BB
447 else:
448 sys.exit(1)