t/readonly: adapt to use fiotestlib
[fio.git] / t / fiotestcommon.py
1 #!/usr/bin/env python3
2 """
3 fiotestcommon.py
4
5 This contains constant definitions, helpers, and a Requirements class that can
6 be used to help with running fio tests.
7 """
8
9 import os
10 import locale
11 import logging
12 import platform
13 import subprocess
14 import multiprocessing
15
16
17 SUCCESS_DEFAULT = {
18     'zero_return': True,
19     'stderr_empty': True,
20     'timeout': 600,
21     }
22 SUCCESS_NONZERO = {
23     'zero_return': False,
24     'stderr_empty': False,
25     'timeout': 600,
26     }
27 SUCCESS_STDERR = {
28     'zero_return': True,
29     'stderr_empty': False,
30     'timeout': 600,
31     }
32
33
34 def get_file(filename):
35     """Safely read a file."""
36     file_data = ''
37     success = True
38
39     try:
40         with open(filename, "r", encoding=locale.getpreferredencoding()) as output_file:
41             file_data = output_file.read()
42     except OSError:
43         success = False
44
45     return file_data, success
46
47
48 class Requirements():
49     """Requirements consists of multiple run environment characteristics.
50     These are to determine if a particular test can be run"""
51
52     _linux = False
53     _libaio = False
54     _io_uring = False
55     _zbd = False
56     _root = False
57     _zoned_nullb = False
58     _not_macos = False
59     _not_windows = False
60     _unittests = False
61     _cpucount4 = False
62     _nvmecdev = False
63
64     def __init__(self, fio_root, args):
65         Requirements._not_macos = platform.system() != "Darwin"
66         Requirements._not_windows = platform.system() != "Windows"
67         Requirements._linux = platform.system() == "Linux"
68
69         if Requirements._linux:
70             config_file = os.path.join(fio_root, "config-host.h")
71             contents, success = get_file(config_file)
72             if not success:
73                 print(f"Unable to open {config_file} to check requirements")
74                 Requirements._zbd = True
75             else:
76                 Requirements._zbd = "CONFIG_HAS_BLKZONED" in contents
77                 Requirements._libaio = "CONFIG_LIBAIO" in contents
78
79             contents, success = get_file("/proc/kallsyms")
80             if not success:
81                 print("Unable to open '/proc/kallsyms' to probe for io_uring support")
82             else:
83                 Requirements._io_uring = "io_uring_setup" in contents
84
85             Requirements._root = os.geteuid() == 0
86             if Requirements._zbd and Requirements._root:
87                 try:
88                     subprocess.run(["modprobe", "null_blk"],
89                                    stdout=subprocess.PIPE,
90                                    stderr=subprocess.PIPE)
91                     if os.path.exists("/sys/module/null_blk/parameters/zoned"):
92                         Requirements._zoned_nullb = True
93                 except Exception:
94                     pass
95
96         if platform.system() == "Windows":
97             utest_exe = "unittest.exe"
98         else:
99             utest_exe = "unittest"
100         unittest_path = os.path.join(fio_root, "unittests", utest_exe)
101         Requirements._unittests = os.path.exists(unittest_path)
102
103         Requirements._cpucount4 = multiprocessing.cpu_count() >= 4
104         Requirements._nvmecdev = args.nvmecdev
105
106         req_list = [
107                 Requirements.linux,
108                 Requirements.libaio,
109                 Requirements.io_uring,
110                 Requirements.zbd,
111                 Requirements.root,
112                 Requirements.zoned_nullb,
113                 Requirements.not_macos,
114                 Requirements.not_windows,
115                 Requirements.unittests,
116                 Requirements.cpucount4,
117                 Requirements.nvmecdev,
118                     ]
119         for req in req_list:
120             value, desc = req()
121             logging.debug("Requirements: Requirement '%s' met? %s", desc, value)
122
123     @classmethod
124     def linux(cls):
125         """Are we running on Linux?"""
126         return Requirements._linux, "Linux required"
127
128     @classmethod
129     def libaio(cls):
130         """Is libaio available?"""
131         return Requirements._libaio, "libaio required"
132
133     @classmethod
134     def io_uring(cls):
135         """Is io_uring available?"""
136         return Requirements._io_uring, "io_uring required"
137
138     @classmethod
139     def zbd(cls):
140         """Is ZBD support available?"""
141         return Requirements._zbd, "Zoned block device support required"
142
143     @classmethod
144     def root(cls):
145         """Are we running as root?"""
146         return Requirements._root, "root required"
147
148     @classmethod
149     def zoned_nullb(cls):
150         """Are zoned null block devices available?"""
151         return Requirements._zoned_nullb, "Zoned null block device support required"
152
153     @classmethod
154     def not_macos(cls):
155         """Are we running on a platform other than macOS?"""
156         return Requirements._not_macos, "platform other than macOS required"
157
158     @classmethod
159     def not_windows(cls):
160         """Are we running on a platform other than Windws?"""
161         return Requirements._not_windows, "platform other than Windows required"
162
163     @classmethod
164     def unittests(cls):
165         """Were unittests built?"""
166         return Requirements._unittests, "Unittests support required"
167
168     @classmethod
169     def cpucount4(cls):
170         """Do we have at least 4 CPUs?"""
171         return Requirements._cpucount4, "4+ CPUs required"
172
173     @classmethod
174     def nvmecdev(cls):
175         """Do we have an NVMe character device to test?"""
176         return Requirements._nvmecdev, "NVMe character device test target required"