Skip to content

Commit 4bc702c

Browse files
committed
t/verify-trim.py: superficial test script for verify/trim
Fio can verify trim operations. This script adds some simple test cases for this feature. Signed-off-by: Vincent Fu <[email protected]>
1 parent 46f2285 commit 4bc702c

File tree

1 file changed

+309
-0
lines changed

1 file changed

+309
-0
lines changed

t/verify-trim.py

Lines changed: 309 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,309 @@
1+
#!/usr/bin/env python3
2+
"""
3+
# verify-trim.c.py
4+
#
5+
# Test fio's verify trim feature.
6+
#
7+
# USAGE
8+
# see python3 verify-trim.c.py --help
9+
#
10+
# EXAMPLES
11+
# python3 t/verify-trim.c.py
12+
# python3 t/verify-trim.c.py --fio ./fio
13+
#
14+
# REQUIREMENTS
15+
# Python 3.6
16+
# Linux
17+
#
18+
"""
19+
import os
20+
import sys
21+
import time
22+
import logging
23+
import argparse
24+
import subprocess
25+
from pathlib import Path
26+
from fiotestlib import FioJobCmdTest, run_fio_tests
27+
from fiotestcommon import SUCCESS_NONZERO, Requirements
28+
29+
30+
VERIFY_OPT_LIST = [
31+
'direct',
32+
'iodepth',
33+
'filesize',
34+
'bs',
35+
'time_based',
36+
'runtime',
37+
'io_size',
38+
'offset',
39+
'number_ios',
40+
'output-format',
41+
'directory',
42+
'norandommap',
43+
'numjobs',
44+
'nrfiles',
45+
'openfiles',
46+
'ioengine',
47+
'trim_backlog_batch',
48+
'trim_verify_zero',
49+
'number_ios',
50+
]
51+
52+
class VerifyTrimTest(FioJobCmdTest):
53+
"""
54+
VerifyTrim test class.
55+
"""
56+
57+
def setup(self, parameters):
58+
"""Setup a test."""
59+
60+
fio_args = [
61+
"--name=verifytrim",
62+
"--verify=md5",
63+
f"--filename={self.fio_opts['filename']}",
64+
f"--rw={self.fio_opts['rw']}",
65+
f"--trim_percentage={self.fio_opts['trim_percentage']}",
66+
f"--trim_backlog={self.fio_opts['trim_backlog']}",
67+
f"--output={self.filenames['output']}",
68+
]
69+
for opt in VERIFY_OPT_LIST:
70+
if opt in self.fio_opts:
71+
option = f"--{opt}={self.fio_opts[opt]}"
72+
fio_args.append(option)
73+
74+
super().setup(fio_args)
75+
76+
def check_result(self):
77+
super().check_result()
78+
79+
if self.fio_opts.get('output-format') == 'json':
80+
actual = self.json_data['jobs'][0]['trim']['total_ios']
81+
expected = self.json_data['jobs'][0]['write']['total_ios'] * self.fio_opts['trim_percentage'] / 100
82+
if abs(expected - actual) > 0.1*expected:
83+
self.passed = False
84+
self.failure_reason += f" large discrepancy between expected {expected} and {actual} actual trims,"
85+
else:
86+
logging.debug("expected %d trims ~match actual %d trims", expected, actual)
87+
88+
if not self.passed:
89+
with open(self.filenames['stderr'], "r") as se:
90+
contents = se.read()
91+
logging.info("stderr: %s", contents)
92+
93+
with open(self.filenames['stdout'], "r") as so:
94+
contents = so.read()
95+
logging.info("stdout: %s", contents)
96+
97+
with open(self.filenames['output'], "r") as out:
98+
contents = out.read()
99+
logging.info("output: %s", contents)
100+
101+
102+
TEST_LIST = [
103+
# These tests are superficial.
104+
#
105+
# TODO: add a test case for trim_verify_zero by inducing a failure; the way
106+
# to do this would be to write non-zero data to a block after it was
107+
# trimmed but before it was read back (how to do this?)
108+
{
109+
# make sure readonly option triggers error message when
110+
# trim_{percentage,backlog} options make trim operations a possibility
111+
"test_id": 1,
112+
"fio_opts": {
113+
"rw": "read",
114+
"trim_percentage": 100,
115+
"trim_backlog": 1,
116+
"readonly": 1,
117+
},
118+
"test_class": VerifyTrimTest,
119+
"success": SUCCESS_NONZERO,
120+
},
121+
{
122+
# basic test seq write
123+
# trim_backlog=1
124+
# trim_percentage=100
125+
"test_id": 100,
126+
"fio_opts": {
127+
"rw": "write",
128+
"trim_percentage": 100,
129+
"trim_backlog": 1,
130+
"trim_verify_zero": 1,
131+
"number_ios": 64,
132+
"output-format": "json",
133+
},
134+
"test_class": VerifyTrimTest,
135+
},
136+
{
137+
# basic test rand write
138+
# trim_backlog=1
139+
# trim_percentage=100
140+
"test_id": 101,
141+
"fio_opts": {
142+
"rw": "randwrite",
143+
"trim_percentage": 100,
144+
"trim_backlog": 1,
145+
"trim_verify_zero": 1,
146+
"number_ios": 64,
147+
"output-format": "json",
148+
},
149+
"test_class": VerifyTrimTest,
150+
},
151+
{
152+
# basic test seq write
153+
# trim_backlog=1
154+
# trim_percentage=50
155+
"test_id": 102,
156+
"fio_opts": {
157+
"rw": "write",
158+
"trim_percentage": 50,
159+
"trim_backlog": 1,
160+
"trim_verify_zero": 1,
161+
"number_ios": 64,
162+
"output-format": "json",
163+
},
164+
"test_class": VerifyTrimTest,
165+
},
166+
{
167+
# basic test rand write
168+
# trim_backlog=1
169+
# trim_percentage=50
170+
"test_id": 103,
171+
"fio_opts": {
172+
"rw": "randwrite",
173+
"trim_percentage": 50,
174+
"trim_backlog": 1,
175+
"trim_verify_zero": 1,
176+
"number_ios": 64,
177+
"output-format": "json",
178+
},
179+
"test_class": VerifyTrimTest,
180+
},
181+
{
182+
# basic test seq write
183+
# trim_backlog=16
184+
# trim_percentage=50
185+
"test_id": 104,
186+
"fio_opts": {
187+
"rw": "write",
188+
"trim_percentage": 50,
189+
"trim_backlog": 16,
190+
"trim_verify_zero": 1,
191+
"number_ios": 64,
192+
"output-format": "json",
193+
},
194+
"test_class": VerifyTrimTest,
195+
},
196+
{
197+
# basic test rand write
198+
# trim_backlog=16
199+
# trim_percentage=50
200+
"test_id": 105,
201+
"fio_opts": {
202+
"rw": "randwrite",
203+
"trim_percentage": 50,
204+
"trim_backlog": 16,
205+
"trim_verify_zero": 1,
206+
"number_ios": 64,
207+
"output-format": "json",
208+
},
209+
"test_class": VerifyTrimTest,
210+
},
211+
212+
]
213+
214+
215+
def parse_args():
216+
"""Parse command-line arguments."""
217+
218+
parser = argparse.ArgumentParser()
219+
parser.add_argument('-r', '--fio-root', help='fio root path')
220+
parser.add_argument('-d', '--debug', help='Enable debug messages', action='store_true')
221+
parser.add_argument('-f', '--fio', help='path to file executable (e.g., ./fio)')
222+
parser.add_argument('-a', '--artifact-root', help='artifact root directory')
223+
parser.add_argument('-s', '--skip', nargs='+', type=int,
224+
help='list of test(s) to skip')
225+
parser.add_argument('-o', '--run-only', nargs='+', type=int,
226+
help='list of test(s) to run, skipping all others')
227+
parser.add_argument('-k', '--skip-req', action='store_true',
228+
help='skip requirements checking')
229+
parser.add_argument('--dut',
230+
help='Block device to test against (use null_blk if not provided')
231+
args = parser.parse_args()
232+
233+
return args
234+
235+
236+
def main():
237+
"""
238+
Run tests for fio's verify trim feature.
239+
"""
240+
241+
args = parse_args()
242+
243+
if args.debug:
244+
logging.basicConfig(level=logging.DEBUG)
245+
else:
246+
logging.basicConfig(level=logging.INFO)
247+
248+
artifact_root = args.artifact_root if args.artifact_root else \
249+
f"verify-trim-test-{time.strftime('%Y%m%d-%H%M%S')}"
250+
os.mkdir(artifact_root)
251+
print(f"Artifact directory is {artifact_root}")
252+
253+
if args.fio:
254+
fio_path = str(Path(args.fio).absolute())
255+
else:
256+
fio_path = os.path.join(os.path.dirname(__file__), '../fio')
257+
print(f"fio path is {fio_path}")
258+
259+
if args.fio_root:
260+
fio_root = args.fio_root
261+
else:
262+
fio_root = str(Path(__file__).absolute().parent.parent)
263+
print(f"fio root is {fio_root}")
264+
265+
if not args.skip_req:
266+
Requirements(fio_root, args)
267+
268+
cleanup_nullb = False
269+
if not args.dut:
270+
subprocess.run(["sudo", "modprobe", "-r", "null_blk"],
271+
stdout=subprocess.PIPE,
272+
stderr=subprocess.PIPE)
273+
subprocess.run(["sudo", "modprobe", "null_blk", "memory_backed=1", "discard=1"],
274+
stdout=subprocess.PIPE,
275+
stderr=subprocess.PIPE)
276+
if os.path.exists('/dev/nullb0'):
277+
args.dut = '/dev/nullb0'
278+
cleanup_nullb = True
279+
else:
280+
print("No block device provided and could not create null_blk device for testing")
281+
sys.exit(1)
282+
283+
for test in TEST_LIST:
284+
test['fio_opts']['filename'] = args.dut
285+
286+
test_env = {
287+
'fio_path': fio_path,
288+
'fio_root': str(Path(__file__).absolute().parent.parent),
289+
'artifact_root': artifact_root,
290+
'basename': 'verifytrim',
291+
}
292+
293+
total = { 'passed': 0, 'failed': 0, 'skipped': 0 }
294+
295+
try:
296+
total['passed'], total['failed'], total['skipped'] = run_fio_tests(TEST_LIST, test_env, args)
297+
except KeyboardInterrupt:
298+
pass
299+
300+
if cleanup_nullb:
301+
subprocess.run(["sudo", "modprobe", "-r", "null_blk"],
302+
stdout=subprocess.PIPE,
303+
stderr=subprocess.PIPE)
304+
305+
sys.exit(total['failed'])
306+
307+
308+
if __name__ == '__main__':
309+
main()

0 commit comments

Comments
 (0)