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
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
|
##############################################################################
# Copyright (c) 2015 Huawei Technologies Co.,Ltd.
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Apache License, Version 2.0
# which accompanies this distribution, and is available at
# http://www.apache.org/licenses/LICENSE-2.0
##############################################################################
from __future__ import absolute_import
from __future__ import print_function
import logging
import pkg_resources
from oslo_serialization import jsonutils
import yardstick.ssh as ssh
from yardstick.benchmark.scenarios import base
LOG = logging.getLogger(__name__)
class Fio(base.Scenario):
"""Execute fio benchmark in a host
Parameters
filename - file name for fio workload
type: string
unit: na
default: /home/ubuntu/data.raw
job_file - fio job configuration file
type: string
unit: na
default: None
job_file_config - content of job configuration file
type: list
unit: na
default: None
directory - mount directoey for test volume
type: string
unit: na
default: None
bs - block size used for the io units
type: int
unit: bytes
default: 4k
iodepth - number of iobuffers to keep in flight
type: int
unit: na
default: 1
rw - type of io pattern [read, write, randwrite, randread, rw, randrw]
type: string
unit: na
default: write
rwmixwrite - percentage of a mixed workload that should be writes
type: int
unit: percentage
default: 50
ramp_time - run time before logging any performance
type: int
unit: seconds
default: 20
direct - whether use non-buffered I/O or not
type: boolean
unit: na
default: 1
size - total size of I/O for this job.
type: string
unit: na
default: 1g
numjobs - number of clones (processes/threads performing the same workload) of this job
type: int
unit: na
default: 1
Read link below for more fio args description:
http://www.bluestop.org/fio/HOWTO.txt
"""
__scenario_type__ = "Fio"
TARGET_SCRIPT = "fio_benchmark.bash"
def __init__(self, scenario_cfg, context_cfg):
self.scenario_cfg = scenario_cfg
self.context_cfg = context_cfg
self.options = self.scenario_cfg["options"]
self.setup_done = False
def setup(self):
"""scenario setup"""
host = self.context_cfg["host"]
self.client = ssh.SSH.from_node(host, defaults={"user": "root"})
self.client.wait(timeout=600)
self.job_file = self.options.get("job_file", None)
config_lines = self.options.get("job_file_config", None)
if self.job_file:
self.job_file_script = pkg_resources.resource_filename(
"yardstick.resources", 'files/' + self.job_file)
# copy job file to host
self.client._put_file_shell(self.job_file_script, '~/job_file.ini')
elif config_lines:
LOG.debug("Job file configuration received, Fio job file will be created.")
self.job_file = 'tmp_job_file.ini'
self.job_file_script = pkg_resources.resource_filename(
"yardstick.resources", 'files/' + self.job_file)
with open(self.job_file_script, 'w') as f:
f.write('\n'.join(str(line) for line in config_lines))
# copy job file to host
self.client._put_file_shell(self.job_file_script, '~/job_file.ini')
else:
LOG.debug("No job file configuration received, Fio will use parameters.")
self.target_script = pkg_resources.resource_filename(
"yardstick.benchmark.scenarios.storage", Fio.TARGET_SCRIPT)
# copy script to host
self.client._put_file_shell(self.target_script, '~/fio.sh')
mount_dir = self.options.get("directory", None)
if mount_dir:
LOG.debug("Formating volume...")
_, stdout, _ = self.client.execute(
"lsblk -dps | grep -m 1 disk | awk '{print $1}'")
block_device = stdout.strip()
if block_device:
self.client.execute("sudo mkfs.ext4 %s" % block_device)
cmd = "sudo mkdir %s" % mount_dir
self.client.execute(cmd)
LOG.debug("Mounting volume at: %s", mount_dir)
cmd = "sudo mount %s %s" % (block_device, mount_dir)
self.client.execute(cmd)
self.setup_done = True
def run(self, result):
"""execute the benchmark"""
default_args = "-ioengine=libaio -group_reporting -time_based -time_based " \
"--output-format=json"
timeout = 3600
if not self.setup_done:
self.setup()
if self.job_file:
cmd = "sudo fio job_file.ini --output-format=json"
else:
filename = self.options.get("filename", "/home/ubuntu/data.raw")
bs = self.options.get("bs", "4k")
iodepth = self.options.get("iodepth", "1")
rw = self.options.get("rw", "write")
ramp_time = self.options.get("ramp_time", 20)
size = self.options.get("size", "1g")
direct = self.options.get("direct", "1")
numjobs = self.options.get("numjobs", "1")
rwmixwrite = self.options.get("rwmixwrite", 50)
name = "yardstick-fio"
# if run by a duration runner
duration_time = self.scenario_cfg["runner"].get("duration", None) \
if "runner" in self.scenario_cfg else None
# if run by an arithmetic runner
arithmetic_time = self.options.get("duration", None)
if duration_time:
runtime = duration_time
elif arithmetic_time:
runtime = arithmetic_time
else:
runtime = 30
# Set timeout, so that the cmd execution does not exit incorrectly
# when the test run time is last long
timeout = int(ramp_time) + int(runtime) + 600
cmd_args = "-filename=%s -direct=%s -bs=%s -iodepth=%s -rw=%s -rwmixwrite=%s " \
"-size=%s -ramp_time=%s -numjobs=%s -runtime=%s -name=%s %s" \
% (filename, direct, bs, iodepth, rw, rwmixwrite, size, ramp_time, numjobs,
runtime, name, default_args)
cmd = "sudo bash fio.sh %s %s" % (filename, cmd_args)
LOG.debug("Executing command: %s", cmd)
status, stdout, stderr = self.client.execute(cmd, timeout=timeout)
if status:
raise RuntimeError(stderr)
raw_data = jsonutils.loads(stdout)
if self.job_file:
result["read_bw"] = raw_data["jobs"][0]["read"]["bw"]
result["read_iops"] = raw_data["jobs"][0]["read"]["iops"]
result["read_lat"] = raw_data["jobs"][0]["read"]["lat"]["mean"]
result["write_bw"] = raw_data["jobs"][0]["write"]["bw"]
result["write_iops"] = raw_data["jobs"][0]["write"]["iops"]
result["write_lat"] = raw_data["jobs"][0]["write"]["lat"]["mean"]
else:
# The bandwidth unit is KB/s, and latency unit is us
if rw in ["read", "randread", "rw", "randrw"]:
result["read_bw"] = raw_data["jobs"][0]["read"]["bw"]
result["read_iops"] = raw_data["jobs"][0]["read"]["iops"]
result["read_lat"] = raw_data["jobs"][0]["read"]["lat"]["mean"]
if rw in ["write", "randwrite", "rw", "randrw"]:
result["write_bw"] = raw_data["jobs"][0]["write"]["bw"]
result["write_iops"] = raw_data["jobs"][0]["write"]["iops"]
result["write_lat"] = raw_data["jobs"][0]["write"]["lat"]["mean"]
if "sla" in self.scenario_cfg:
sla_error = ""
for k, v in result.items():
if k not in self.scenario_cfg['sla']:
continue
if "lat" in k:
# For lattency small value is better
max_v = float(self.scenario_cfg['sla'][k])
if v > max_v:
sla_error += "%s %f > sla:%s(%f); " % (k, v, k, max_v)
else:
# For bandwidth and iops big value is better
min_v = int(self.scenario_cfg['sla'][k])
if v < min_v:
sla_error += "%s %d < " \
"sla:%s(%d); " % (k, v, k, min_v)
self.verify_SLA(sla_error == "", sla_error)
def _test():
"""internal test function"""
key_filename = pkg_resources.resource_filename("yardstick.resources",
"files/yardstick_key")
ctx = {
"host": {
"ip": "10.229.47.137",
"user": "root",
"key_filename": key_filename
}
}
logger = logging.getLogger("yardstick")
logger.setLevel(logging.DEBUG)
options = {
"filename": "/home/ubuntu/data.raw",
"bs": "4k",
"iodepth": "1",
"rw": "rw",
"ramp_time": 1,
"duration": 10
}
result = {}
args = {"options": options}
fio = Fio(args, ctx)
fio.run(result)
print(result)
if __name__ == '__main__':
_test()
|