t/strided: call parent class check_result()
[fio.git] / t / strided.py
1 #!/usr/bin/env python3
2
3 """
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
9 # a minimum size of 64MiB is recommended.
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
17 # dd if=/dev/zero of=temp bs=1M count=64
18 # python t/strided.py ./fio -f temp
19 #
20 # ===TEST MATRIX===
21 #
22 # --zonemode=strided, zoneskip unset
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
29 """
30
31 import os
32 import sys
33 import time
34 import argparse
35 from pathlib import Path
36 from fiotestlib import FioJobCmdTest, run_fio_tests
37
38
39 class StridedTest(FioJobCmdTest):
40     """Test zonemode=strided."""
41
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                    ]
55
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)
60
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):
74         super().check_result()
75         if not self.passed:
76             return
77
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:
87                 continue
88
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}")
102                 return False
103
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
112
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
132 TEST_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
386 def 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
403 def main():
404     """Run zonemode=strided tests."""
405
406     args = parse_args()
407
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)
421         filesize = statinfo.st_size
422         if filesize == 0:
423             f = os.open(args.dut, os.O_RDONLY)
424             filesize = os.lseek(f, 0, os.SEEK_END)
425             os.close(f)
426
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
431         else:
432             test['fio_opts']['filesize'] = test['fio_opts']['size']
433
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               }
440
441     _, failed, _ = run_fio_tests(TEST_LIST, test_env, args)
442     sys.exit(failed)
443
444
445 if __name__ == '__main__':
446     main()