1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
|
#!/usr/bin/env python3
from subprocess import check_call, call, check_output, Popen, PIPE, CalledProcessError
import re
import sys
import signal
import os.path
import time
import argparse
fio_template = """
[global]
thread=1
invalidate=1
rw=%(testtype)s
time_based=1
runtime=%(runtime)s
ioengine=libaio
direct=1
bs=%(blocksize)d
iodepth=%(iodepth)d
norandommap=%(norandommap)d
numjobs=%(numjobs)s
%(verify)s
verify_dump=1
"""
verify_template = """
do_verify=1
verify=crc32c-intel
"""
fio_job_template = """
[job%(jobnumber)d]
filename=%(device)s
"""
def interrupt_handler(signum, frame):
fio.terminate()
print("FIO terminated")
sys.exit(0)
def main(io_size, protocol, queue_depth, test_type, runtime, num_jobs, verify):
global fio
if protocol == "nvmf":
devices = get_nvmf_target_devices()
elif protocol == "iscsi":
devices = get_iscsi_target_devices()
configure_devices(devices)
try:
fio_executable = check_output("which fio", shell=True).split()[0]
except CalledProcessError as e:
sys.stderr.write(str(e))
sys.stderr.write("\nCan't find the fio binary, please install it.\n")
sys.exit(1)
device_paths = ['/dev/' + dev for dev in devices]
print("Device paths:")
print(device_paths)
sys.stdout.flush()
signal.signal(signal.SIGTERM, interrupt_handler)
signal.signal(signal.SIGINT, interrupt_handler)
fio = Popen([fio_executable, '-'], stdin=PIPE)
fio.communicate(create_fio_config(io_size, queue_depth, device_paths, test_type, runtime, num_jobs, verify).encode())
fio.stdin.close()
rc = fio.wait()
print("FIO completed with code %d\n" % rc)
sys.stdout.flush()
sys.exit(rc)
def get_iscsi_target_devices():
output = check_output('iscsiadm -m session -P 3', shell=True)
return re.findall("Attached scsi disk (sd[a-z]+)", output.decode("ascii"))
def get_nvmf_target_devices():
output = str(check_output('lsblk -l -o NAME', shell=True).decode())
return re.findall("(nvme[0-9]+n[0-9]+)\n", output)
def create_fio_config(size, q_depth, devices, test, run_time, num_jobs, verify):
norandommap = 0
if not verify:
verifyfio = ""
norandommap = 1
else:
verifyfio = verify_template
fiofile = fio_template % {"blocksize": size, "iodepth": q_depth,
"testtype": test, "runtime": run_time,
"norandommap": norandommap, "verify": verifyfio,
"numjobs": num_jobs}
for (i, dev) in enumerate(devices):
fiofile += fio_job_template % {"jobnumber": i, "device": dev}
return fiofile
def set_device_parameter(devices, filename_template, value):
valid_value = True
for dev in devices:
filename = filename_template % dev
f = open(filename, 'r+b')
try:
f.write(value.encode())
f.close()
except OSError:
valid_value = False
continue
return valid_value
def configure_devices(devices):
for dev in devices:
retry = 30
while retry > 0:
if os.path.exists("/sys/block/%s/queue/nomerges" % dev):
break
else:
retry = retry - 1
time.sleep(0.1)
set_device_parameter(devices, "/sys/block/%s/queue/nomerges", "2")
set_device_parameter(devices, "/sys/block/%s/queue/nr_requests", "128")
requested_qd = 128
qd = requested_qd
while qd > 0:
try:
set_device_parameter(devices, "/sys/block/%s/device/queue_depth", str(qd))
break
except IOError:
qd = qd - 1
if qd == 0:
print("Could not set block device queue depths.")
elif qd < requested_qd:
print("Requested queue_depth {} but only {} is supported.".format(str(requested_qd), str(qd)))
if not set_device_parameter(devices, "/sys/block/%s/queue/scheduler", "noop"):
set_device_parameter(devices, "/sys/block/%s/queue/scheduler", "none")
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="fio.py")
parser.add_argument("-i", "--io-size", type=int, help="The desired I/O size in bytes.", required=True)
parser.add_argument("-p", "--protocol", type=str, help="The protocol we are testing against. One of iscsi or nvmf.", required=True)
parser.add_argument("-d", "--queue-depth", type=int, help="The desired queue depth for each job.", required=True)
parser.add_argument("-t", "--test-type", type=str, help="The fio I/O pattern to run. e.g. read, randwrite, randrw.", required=True)
parser.add_argument("-r", "--runtime", type=int, help="Time in seconds to run the workload.", required=True)
parser.add_argument("-n", "--num-jobs", type=int, help="The number of fio jobs to run in your workload. default 1.", default=1)
parser.add_argument("-v", "--verify", action="store_true", help="Supply this argument to verify the I/O.", default=False)
args = parser.parse_args()
if args.protocol.lower() != "nvmf" and args.protocol.lower() != "iscsi":
parser.error("Protocol must be one of the following: nvmf, iscsi.")
main(args.io_size, args.protocol, args.queue_depth, args.test_type, args.runtime, args.num_jobs, args.verify)
|