t/strided: call parent class check_result()
[fio.git] / t / strided.py
CommitLineData
8c00c383 1#!/usr/bin/env python3
5a36d0e4
VF
2
3"""
6d5af490
VF
4# strided.py
5#
6# Test zonemode=strided. This uses the null ioengine when no file is
7# specified. If a file is specified, use it for randdom read testing.
8# Some of the zoneranges in the tests are 16MiB. So when using a file
03317fbb 9# a minimum size of 64MiB is recommended.
6d5af490
VF
10#
11# USAGE
12# python strided.py fio-executable [-f file/device]
13#
14# EXAMPLES
15# python t/strided.py ./fio
16# python t/strided.py ./fio -f /dev/sda
03317fbb 17# dd if=/dev/zero of=temp bs=1M count=64
6d5af490
VF
18# python t/strided.py ./fio -f temp
19#
6d5af490
VF
20# ===TEST MATRIX===
21#
fa9fd914 22# --zonemode=strided, zoneskip unset
6d5af490
VF
23# w/ randommap and LFSR
24# zonesize=zonerange all blocks in zonerange touched
25# zonesize>zonerange all blocks touched and roll-over back into zone
26# zonesize<zonerange all blocks inside zone
27#
28# w/o randommap all blocks inside zone
5a36d0e4 29"""
6d5af490 30
6d5af490
VF
31import os
32import sys
5a36d0e4 33import time
6d5af490 34import argparse
5a36d0e4
VF
35from pathlib import Path
36from fiotestlib import FioJobCmdTest, run_fio_tests
6d5af490
VF
37
38
5a36d0e4
VF
39class StridedTest(FioJobCmdTest):
40 """Test zonemode=strided."""
6d5af490 41
5a36d0e4
VF
42 def setup(self, parameters):
43 fio_args = [
44 "--name=strided",
45 "--zonemode=strided",
46 "--log_offset=1",
47 "--randrepeat=0",
48 "--rw=randread",
49 f"--write_iops_log={self.filenames['iopslog']}",
50 f"--output={self.filenames['output']}",
51 f"--zonerange={self.fio_opts['zonerange']}",
52 f"--zonesize={self.fio_opts['zonesize']}",
53 f"--bs={self.fio_opts['bs']}",
54 ]
6d5af490 55
5a36d0e4
VF
56 for opt in ['norandommap', 'random_generator', 'offset']:
57 if opt in self.fio_opts:
58 option = f"--{opt}={self.fio_opts[opt]}"
59 fio_args.append(option)
6d5af490 60
5a36d0e4
VF
61 if 'filename' in self.fio_opts:
62 for opt in ['filename', 'filesize']:
63 option = f"--{opt}={self.fio_opts[opt]}"
64 fio_args.append(option)
65 else:
66 fio_args.append('--ioengine=null')
67 for opt in ['size', 'io_size', 'filesize']:
68 option = f"--{opt}={self.fio_opts[opt]}"
69 fio_args.append(option)
70
71 super().setup(fio_args)
72
73 def check_result(self):
f4c55efe
VF
74 super().check_result()
75 if not self.passed:
76 return
77
5a36d0e4
VF
78 zonestart = 0 if 'offset' not in self.fio_opts else self.fio_opts['offset']
79 iospersize = self.fio_opts['zonesize'] / self.fio_opts['bs']
80 iosperrange = self.fio_opts['zonerange'] / self.fio_opts['bs']
81 iosperzone = 0
82 lines = self.iops_log_lines.split('\n')
83 zoneset = set()
84
85 for line in lines:
86 if len(line) == 0:
6d5af490
VF
87 continue
88
5a36d0e4
VF
89 if iosperzone == iospersize:
90 # time to move to a new zone
91 iosperzone = 0
92 zoneset = set()
93 zonestart += self.fio_opts['zonerange']
94 if zonestart >= self.fio_opts['filesize']:
95 zonestart = 0 if 'offset' not in self.fio_opts else self.fio_opts['offset']
96
97 iosperzone = iosperzone + 1
98 tokens = line.split(',')
99 offset = int(tokens[4])
100 if offset < zonestart or offset >= zonestart + self.fio_opts['zonerange']:
101 print(f"Offset {offset} outside of zone starting at {zonestart}")
6d5af490 102 return False
6d5af490 103
5a36d0e4
VF
104 # skip next section if norandommap is enabled with no
105 # random_generator or with a random_generator != lfsr
106 if 'norandommap' in self.fio_opts:
107 if 'random_generator' in self.fio_opts:
108 if self.fio_opts['random_generator'] != 'lfsr':
109 continue
110 else:
111 continue
6d5af490 112
5a36d0e4
VF
113 # we either have a random map enabled or we
114 # are using an LFSR
115 # so all blocks should be unique and we should have
116 # covered the entire zone when iosperzone % iosperrange == 0
117 block = (offset - zonestart) / self.fio_opts['bs']
118 if block in zoneset:
119 print(f"Offset {offset} in zone already touched")
120 return False
121
122 zoneset.add(block)
123 if iosperzone % iosperrange == 0:
124 if len(zoneset) != iosperrange:
125 print(f"Expected {iosperrange} blocks in zone but only saw {len(zoneset)}")
126 return False
127 zoneset = set()
128
129 return True
130
131
132TEST_LIST = [ # randommap enabled
133 {
134 "test_id": 1,
135 "fio_opts": {
136 "zonerange": 4096,
137 "zonesize": 4096,
138 "bs": 4096,
139 "offset": 8*4096,
140 "size": 16*4096,
141 "io_size": 16*4096,
142 },
143 "test_class": StridedTest,
144 },
145 {
146 "test_id": 2,
147 "fio_opts": {
148 "zonerange": 4096,
149 "zonesize": 4096,
150 "bs": 4096,
151 "size": 16*4096,
152 "io_size": 16*4096,
153 },
154 "test_class": StridedTest,
155 },
156 {
157 "test_id": 3,
158 "fio_opts": {
159 "zonerange": 16*1024*1024,
160 "zonesize": 16*1024*1024,
161 "bs": 4096,
162 "size": 256*1024*1024,
163 "io_size": 256*1024*204,
164 },
165 "test_class": StridedTest,
166 },
167 {
168 "test_id": 4,
169 "fio_opts": {
170 "zonerange": 4096,
171 "zonesize": 4*4096,
172 "bs": 4096,
173 "size": 16*4096,
174 "io_size": 16*4096,
175 },
176 "test_class": StridedTest,
177 },
178 {
179 "test_id": 5,
180 "fio_opts": {
181 "zonerange": 16*1024*1024,
182 "zonesize": 32*1024*1024,
183 "bs": 4096,
184 "size": 256*1024*1024,
185 "io_size": 256*1024*204,
186 },
187 "test_class": StridedTest,
188 },
189 {
190 "test_id": 6,
191 "fio_opts": {
192 "zonerange": 8192,
193 "zonesize": 4096,
194 "bs": 4096,
195 "size": 16*4096,
196 "io_size": 16*4096,
197 },
198 "test_class": StridedTest,
199 },
200 {
201 "test_id": 7,
202 "fio_opts": {
203 "zonerange": 16*1024*1024,
204 "zonesize": 8*1024*1024,
205 "bs": 4096,
206 "size": 256*1024*1024,
207 "io_size": 256*1024*204,
208 },
209 "test_class": StridedTest,
210 },
211 # lfsr
212 {
213 "test_id": 8,
214 "fio_opts": {
215 "random_generator": "lfsr",
216 "zonerange": 4096*1024,
217 "zonesize": 4096*1024,
218 "bs": 4096,
219 "offset": 8*4096*1024,
220 "size": 16*4096*1024,
221 "io_size": 16*4096*1024,
222 },
223 "test_class": StridedTest,
224 },
225 {
226 "test_id": 9,
227 "fio_opts": {
228 "random_generator": "lfsr",
229 "zonerange": 4096*1024,
230 "zonesize": 4096*1024,
231 "bs": 4096,
232 "size": 16*4096*1024,
233 "io_size": 16*4096*1024,
234 },
235 "test_class": StridedTest,
236 },
237 {
238 "test_id": 10,
239 "fio_opts": {
240 "random_generator": "lfsr",
241 "zonerange": 16*1024*1024,
242 "zonesize": 16*1024*1024,
243 "bs": 4096,
244 "size": 256*1024*1024,
245 "io_size": 256*1024*204,
246 },
247 "test_class": StridedTest,
248 },
249 {
250 "test_id": 11,
251 "fio_opts": {
252 "random_generator": "lfsr",
253 "zonerange": 4096*1024,
254 "zonesize": 4*4096*1024,
255 "bs": 4096,
256 "size": 16*4096*1024,
257 "io_size": 16*4096*1024,
258 },
259 "test_class": StridedTest,
260 },
261 {
262 "test_id": 12,
263 "fio_opts": {
264 "random_generator": "lfsr",
265 "zonerange": 16*1024*1024,
266 "zonesize": 32*1024*1024,
267 "bs": 4096,
268 "size": 256*1024*1024,
269 "io_size": 256*1024*204,
270 },
271 "test_class": StridedTest,
272 },
273 {
274 "test_id": 13,
275 "fio_opts": {
276 "random_generator": "lfsr",
277 "zonerange": 8192*1024,
278 "zonesize": 4096*1024,
279 "bs": 4096,
280 "size": 16*4096*1024,
281 "io_size": 16*4096*1024,
282 },
283 "test_class": StridedTest,
284 },
285 {
286 "test_id": 14,
287 "fio_opts": {
288 "random_generator": "lfsr",
289 "zonerange": 16*1024*1024,
290 "zonesize": 8*1024*1024,
291 "bs": 4096,
292 "size": 256*1024*1024,
293 "io_size": 256*1024*204,
294 },
295 "test_class": StridedTest,
296 },
297 # norandommap
298 {
299 "test_id": 15,
300 "fio_opts": {
301 "norandommap": 1,
302 "zonerange": 4096,
303 "zonesize": 4096,
304 "bs": 4096,
305 "offset": 8*4096,
306 "size": 16*4096,
307 "io_size": 16*4096,
308 },
309 "test_class": StridedTest,
310 },
311 {
312 "test_id": 16,
313 "fio_opts": {
314 "norandommap": 1,
315 "zonerange": 4096,
316 "zonesize": 4096,
317 "bs": 4096,
318 "size": 16*4096,
319 "io_size": 16*4096,
320 },
321 "test_class": StridedTest,
322 },
323 {
324 "test_id": 17,
325 "fio_opts": {
326 "norandommap": 1,
327 "zonerange": 16*1024*1024,
328 "zonesize": 16*1024*1024,
329 "bs": 4096,
330 "size": 256*1024*1024,
331 "io_size": 256*1024*204,
332 },
333 "test_class": StridedTest,
334 },
335 {
336 "test_id": 18,
337 "fio_opts": {
338 "norandommap": 1,
339 "zonerange": 4096,
340 "zonesize": 8192,
341 "bs": 4096,
342 "size": 16*4096,
343 "io_size": 16*4096,
344 },
345 "test_class": StridedTest,
346 },
347 {
348 "test_id": 19,
349 "fio_opts": {
350 "norandommap": 1,
351 "zonerange": 16*1024*1024,
352 "zonesize": 32*1024*1024,
353 "bs": 4096,
354 "size": 256*1024*1024,
355 "io_size": 256*1024*204,
356 },
357 "test_class": StridedTest,
358 },
359 {
360 "test_id": 20,
361 "fio_opts": {
362 "norandommap": 1,
363 "zonerange": 8192,
364 "zonesize": 4096,
365 "bs": 4096,
366 "size": 16*4096,
367 "io_size": 16*4096,
368 },
369 "test_class": StridedTest,
370 },
371 {
372 "test_id": 21,
373 "fio_opts": {
374 "norandommap": 1,
375 "zonerange": 16*1024*1024,
376 "zonesize": 8*1024*1024,
377 "bs": 4096,
378 "size": 256*1024*1024,
379 "io_size": 256*1024*1024,
380 },
381 "test_class": StridedTest,
382 },
383]
384
385
386def parse_args():
387 """Parse command-line arguments."""
388
389 parser = argparse.ArgumentParser()
390 parser.add_argument('-f', '--fio', help='path to file executable (e.g., ./fio)')
391 parser.add_argument('-a', '--artifact-root', help='artifact root directory')
392 parser.add_argument('-s', '--skip', nargs='+', type=int,
393 help='list of test(s) to skip')
394 parser.add_argument('-o', '--run-only', nargs='+', type=int,
395 help='list of test(s) to run, skipping all others')
396 parser.add_argument('--dut',
397 help='target file/device to test.')
398 args = parser.parse_args()
399
400 return args
401
402
403def main():
404 """Run zonemode=strided tests."""
6d5af490 405
6d5af490
VF
406 args = parse_args()
407
5a36d0e4
VF
408 artifact_root = args.artifact_root if args.artifact_root else \
409 f"strided-test-{time.strftime('%Y%m%d-%H%M%S')}"
410 os.mkdir(artifact_root)
411 print(f"Artifact directory is {artifact_root}")
412
413 if args.fio:
414 fio_path = str(Path(args.fio).absolute())
415 else:
416 fio_path = 'fio'
417 print(f"fio path is {fio_path}")
418
419 if args.dut:
420 statinfo = os.stat(args.dut)
6d5af490
VF
421 filesize = statinfo.st_size
422 if filesize == 0:
5a36d0e4 423 f = os.open(args.dut, os.O_RDONLY)
6d5af490
VF
424 filesize = os.lseek(f, 0, os.SEEK_END)
425 os.close(f)
426
5a36d0e4
VF
427 for test in TEST_LIST:
428 if args.dut:
429 test['fio_opts']['filename'] = os.path.abspath(args.dut)
430 test['fio_opts']['filesize'] = filesize
6d5af490 431 else:
5a36d0e4 432 test['fio_opts']['filesize'] = test['fio_opts']['size']
6d5af490 433
5a36d0e4
VF
434 test_env = {
435 'fio_path': fio_path,
436 'fio_root': str(Path(__file__).absolute().parent.parent),
437 'artifact_root': artifact_root,
438 'basename': 'strided',
439 }
6d5af490 440
5a36d0e4 441 _, failed, _ = run_fio_tests(TEST_LIST, test_env, args)
6d5af490 442 sys.exit(failed)
5a36d0e4
VF
443
444
445if __name__ == '__main__':
446 main()