Commit | Line | Data |
---|---|---|
fb551941 VF |
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 | |
ecd6d30f | 10 | import locale |
fb551941 VF |
11 | import logging |
12 | import platform | |
13 | import subprocess | |
14 | import multiprocessing | |
fb551941 VF |
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 | ||
ecd6d30f VF |
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 | ||
fb551941 VF |
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") | |
ecd6d30f | 71 | contents, success = get_file(config_file) |
fb551941 VF |
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 | ||
ecd6d30f | 79 | contents, success = get_file("/proc/kallsyms") |
fb551941 VF |
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" |