#
#
# REQUIREMENTS
-# - Python 3
+# - Python 3.5 (subprocess.run)
# - Linux (libaio ioengine, zbd tests, etc)
# - The artifact directory must be on a file system that accepts 512-byte IO
# (t0002, t0003, t0004).
#
# TODO run multiple tests simultaneously
# TODO Add sgunmap tests (requires SAS SSD)
-# TODO automatically detect dependencies and skip tests accordingly
#
import os
import shutil
import logging
import argparse
+import platform
import subprocess
+import multiprocessing
from pathlib import Path
self.passed = False
+class Requirements(object):
+ """Requirements consists of multiple run environment characteristics.
+ These are to determine if a particular test can be run"""
+
+ _linux = False
+ _libaio = False
+ _zbd = False
+ _root = False
+ _zoned_nullb = False
+ _not_macos = False
+ _unittests = False
+ _cpucount4 = False
+
+ def __init__(self, fio_root):
+ Requirements._not_macos = platform.system() != "Darwin"
+ Requirements._linux = platform.system() == "Linux"
+
+ if Requirements._linux:
+ try:
+ config_file = os.path.join(fio_root, "config-host.h")
+ with open(config_file, "r") as config:
+ contents = config.read()
+ except Exception:
+ print("Unable to open {0} to check requirements".format(config_file))
+ Requirements._zbd = True
+ else:
+ Requirements._zbd = "CONFIG_LINUX_BLKZONED" in contents
+ Requirements._libaio = "CONFIG_LIBAIO" in contents
+
+ Requirements._root = (os.geteuid() == 0)
+ if Requirements._zbd and Requirements._root:
+ subprocess.run(["modprobe", "null_blk"],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ if os.path.exists("/sys/module/null_blk/parameters/zoned"):
+ Requirements._zoned_nullb = True
+
+ unittest_path = os.path.join(fio_root, "unittests", "unittest")
+ Requirements._unittests = os.path.exists(unittest_path)
+
+ Requirements._cpucount4 = multiprocessing.cpu_count() >= 4
+
+ req_list = [Requirements.linux,
+ Requirements.libaio,
+ Requirements.zbd,
+ Requirements.root,
+ Requirements.zoned_nullb,
+ Requirements.not_macos,
+ Requirements.unittests,
+ Requirements.cpucount4]
+ for req in req_list:
+ value, desc = req()
+ logging.debug("Requirement '%s' met? %s" % (desc, value))
+
+ def linux():
+ return Requirements._linux, "Linux required"
+
+ def libaio():
+ return Requirements._libaio, "libaio required"
+
+ def zbd():
+ return Requirements._zbd, "Zoned block device support required"
+
+ def root():
+ return Requirements._root, "root required"
+
+ def zoned_nullb():
+ return Requirements._zoned_nullb, "Zoned null block device support required"
+
+ def not_macos():
+ return Requirements._not_macos, "platform other than macOS required"
+
+ def unittests():
+ return Requirements._unittests, "Unittests support required"
+
+ def cpucount4():
+ return Requirements._cpucount4, "4+ CPUs required"
+
+
SUCCESS_DEFAULT = {
'zero_return': True,
'stderr_empty': True,
'success': SUCCESS_DEFAULT,
'pre_job': None,
'pre_success': None,
+ 'requirements': [],
},
{
'test_id': 2,
'success': SUCCESS_DEFAULT,
'pre_job': 't0002-13af05ae-pre.fio',
'pre_success': None,
+ 'requirements': [Requirements.linux, Requirements.libaio],
},
{
'test_id': 3,
'success': SUCCESS_NONZERO,
'pre_job': 't0003-0ae2c6e1-pre.fio',
'pre_success': SUCCESS_DEFAULT,
+ 'requirements': [Requirements.linux, Requirements.libaio],
},
{
'test_id': 4,
'success': SUCCESS_DEFAULT,
'pre_job': None,
'pre_success': None,
+ 'requirements': [Requirements.linux, Requirements.libaio],
},
{
'test_id': 5,
'pre_job': None,
'pre_success': None,
'output_format': 'json',
+ 'requirements': [],
},
{
'test_id': 6,
'pre_job': None,
'pre_success': None,
'output_format': 'json',
+ 'requirements': [Requirements.linux, Requirements.libaio],
},
{
'test_id': 7,
'pre_job': None,
'pre_success': None,
'output_format': 'json',
+ 'requirements': [],
},
{
'test_id': 8,
'pre_job': None,
'pre_success': None,
'output_format': 'json',
+ 'requirements': [],
},
{
'test_id': 9,
'pre_job': None,
'pre_success': None,
'output_format': 'json',
+ 'requirements': [Requirements.not_macos,
+ Requirements.cpucount4],
+ # mac os does not support CPU affinity
},
{
'test_id': 10,
'success': SUCCESS_DEFAULT,
'pre_job': None,
'pre_success': None,
+ 'requirements': [],
},
{
'test_id': 11,
'pre_job': None,
'pre_success': None,
'output_format': 'json',
+ 'requirements': [],
},
{
'test_id': 1000,
'exe': 't/axmap',
'parameters': None,
'success': SUCCESS_DEFAULT,
+ 'requirements': [],
},
{
'test_id': 1001,
'exe': 't/ieee754',
'parameters': None,
'success': SUCCESS_DEFAULT,
+ 'requirements': [],
},
{
'test_id': 1002,
'exe': 't/lfsr-test',
'parameters': ['0xFFFFFF', '0', '0', 'verify'],
'success': SUCCESS_STDERR,
+ 'requirements': [],
},
{
'test_id': 1003,
'exe': 't/readonly.py',
'parameters': ['-f', '{fio_path}'],
'success': SUCCESS_DEFAULT,
+ 'requirements': [],
},
{
'test_id': 1004,
'exe': 't/steadystate_tests.py',
'parameters': ['{fio_path}'],
'success': SUCCESS_DEFAULT,
+ 'requirements': [],
},
{
'test_id': 1005,
'exe': 't/stest',
'parameters': None,
'success': SUCCESS_STDERR,
+ 'requirements': [],
},
{
'test_id': 1006,
'exe': 't/strided.py',
'parameters': ['{fio_path}'],
'success': SUCCESS_DEFAULT,
+ 'requirements': [],
},
{
'test_id': 1007,
'exe': 't/zbd/run-tests-against-regular-nullb',
'parameters': None,
'success': SUCCESS_DEFAULT,
+ 'requirements': [Requirements.linux, Requirements.zbd,
+ Requirements.root],
},
{
'test_id': 1008,
'exe': 't/zbd/run-tests-against-zoned-nullb',
'parameters': None,
'success': SUCCESS_DEFAULT,
+ 'requirements': [Requirements.linux, Requirements.zbd,
+ Requirements.root, Requirements.zoned_nullb],
},
{
'test_id': 1009,
'exe': 'unittests/unittest',
'parameters': None,
'success': SUCCESS_DEFAULT,
+ 'requirements': [Requirements.unittests],
},
]
help='list of test(s) to run, skipping all others')
parser.add_argument('-d', '--debug', action='store_true',
help='provide debug output')
+ parser.add_argument('-k', '--skip-req', action='store_true',
+ help='skip requirements checking')
args = parser.parse_args()
return args
os.mkdir(artifact_root)
print("Artifact directory is %s" % artifact_root)
+ if not args.skip_req:
+ req = Requirements(fio_root)
+
passed = 0
failed = 0
skipped = 0
if (args.skip and config['test_id'] in args.skip) or \
(args.run_only and config['test_id'] not in args.run_only):
skipped = skipped + 1
- print("Test {0} SKIPPED".format(config['test_id']))
+ print("Test {0} SKIPPED (User request)".format(config['test_id']))
continue
if issubclass(config['test_class'], FioJobTest):
failed = failed + 1
continue
+ if not args.skip_req:
+ skip = False
+ for req in config['requirements']:
+ ok, reason = req()
+ skip = not ok
+ logging.debug("Requirement '%s' met? %s" % (reason, ok))
+ if skip:
+ break
+ if skip:
+ print("Test {0} SKIPPED ({1})".format(config['test_id'], reason))
+ skipped = skipped + 1
+ continue
+
test.setup(artifact_root, config['test_id'])
test.run()
test.check_result()