blob: 57ba2292935fdf83b9df51fec98cd3d6fcc2d1a4 [file] [log] [blame]
koder aka kdanilov4d4771c2015-04-23 01:32:02 +03001import os
koder aka kdanilovda45e882015-04-06 02:24:42 +03002import sys
3import time
4import json
koder aka kdanilov0c598a12015-04-21 03:01:40 +03005import copy
koder aka kdanilovda45e882015-04-06 02:24:42 +03006import select
7import pprint
koder aka kdanilov4d4771c2015-04-23 01:32:02 +03008import os.path
koder aka kdanilovda45e882015-04-06 02:24:42 +03009import argparse
10import traceback
11import subprocess
12import itertools
13from collections import OrderedDict
14
15
16SECTION = 0
17SETTING = 1
18
19
koder aka kdanilov0c598a12015-04-21 03:01:40 +030020class FioJobSection(object):
21 def __init__(self, name):
22 self.name = name
23 self.vals = OrderedDict()
24 self.format_params = {}
25
26 def copy(self):
27 return copy.deepcopy(self)
28
29
30def to_bytes(sz):
31 sz = sz.lower()
32 try:
33 return int(sz)
34 except ValueError:
35 if sz[-1] == 'm':
36 return (1024 ** 2) * int(sz[:-1])
37 if sz[-1] == 'k':
38 return 1024 * int(sz[:-1])
39 if sz[-1] == 'g':
40 return (1024 ** 3) * int(sz[:-1])
41 raise
42
43
44def fio_config_lexer(fio_cfg):
45 for lineno, line in enumerate(fio_cfg.split("\n")):
46 try:
47 line = line.strip()
48
49 if line.startswith("#") or line.startswith(";"):
50 continue
51
52 if line == "":
53 continue
54
55 if line.startswith('['):
56 assert line.endswith(']'), "name should ends with ]"
57 yield lineno, SECTION, line[1:-1], None
58 elif '=' in line:
59 opt_name, opt_val = line.split('=', 1)
60 yield lineno, SETTING, opt_name.strip(), opt_val.strip()
61 else:
62 yield lineno, SETTING, line, None
63 except Exception as exc:
koder aka kdanilovec1b9732015-04-23 20:43:29 +030064 pref = "During parsing line number {0}\n{1!s}".format(lineno, exc)
65 raise ValueError(pref)
koder aka kdanilov0c598a12015-04-21 03:01:40 +030066
67
68def fio_config_parse(lexer_iter, format_params):
69 orig_format_params_keys = set(format_params)
70 format_params = format_params.copy()
71 in_defaults = False
72 curr_section = None
73 defaults = OrderedDict()
74
75 for lineno, tp, name, val in lexer_iter:
76 if tp == SECTION:
77 if curr_section is not None:
78 yield curr_section
79
80 if name == 'defaults':
81 in_defaults = True
82 curr_section = None
83 else:
84 in_defaults = False
85 curr_section = FioJobSection(name)
86 curr_section.format_params = format_params.copy()
87 curr_section.vals = defaults.copy()
88 else:
89 assert tp == SETTING
90 if name == name.upper():
91 msg = "Param not in default section in line " + str(lineno)
92 assert in_defaults, msg
93 if name not in orig_format_params_keys:
94 # don't make parse_value for PARAMS
95 # they would be parsed later
96 # or this would breakes arrays
97 format_params[name] = val
98 elif in_defaults:
99 defaults[name] = parse_value(val)
100 else:
101 msg = "data outside section, line " + str(lineno)
102 assert curr_section is not None, msg
103 curr_section.vals[name] = parse_value(val)
104
105 if curr_section is not None:
106 yield curr_section
107
108
109def parse_value(val):
110 if val is None:
111 return None
112
113 try:
114 return int(val)
115 except ValueError:
116 pass
117
118 try:
119 return float(val)
120 except ValueError:
121 pass
122
123 if val.startswith('{%'):
124 assert val.endswith("%}")
125 content = val[2:-2]
126 vals = list(i.strip() for i in content.split(','))
127 return map(parse_value, vals)
128 return val
129
130
131def process_repeats(sec_iter):
132
133 for sec in sec_iter:
134 if '*' in sec.name:
135 msg = "Only one '*' allowed in section name"
136 assert sec.name.count('*') == 1, msg
137
138 name, count = sec.name.split("*")
139 sec.name = name.strip()
140 count = count.strip()
141
142 try:
143 count = int(count.strip().format(**sec.format_params))
144 except KeyError:
145 raise ValueError("No parameter {0} given".format(count[1:-1]))
146 except ValueError:
147 msg = "Parameter {0} nas non-int value {1!r}"
148 raise ValueError(msg.format(count[1:-1],
149 count.format(**sec.format_params)))
150
koder aka kdanilov7e0f7cf2015-05-01 17:24:35 +0300151 yield sec.copy()
koder aka kdanilov0c598a12015-04-21 03:01:40 +0300152
153 if 'ramp_time' in sec.vals:
154 sec = sec.copy()
155 sec.vals['_ramp_time'] = sec.vals.pop('ramp_time')
156
157 for _ in range(count - 1):
158 yield sec.copy()
159 else:
160 yield sec
161
162
163def process_cycles(sec_iter):
164 # insert parametrized cycles
165 sec_iter = try_format_params_into_section(sec_iter)
166
167 for sec in sec_iter:
168
169 cycles_var_names = []
170 cycles_var_values = []
171
172 for name, val in sec.vals.items():
173 if isinstance(val, list):
174 cycles_var_names.append(name)
175 cycles_var_values.append(val)
176
177 if len(cycles_var_names) == 0:
178 yield sec
179 else:
180 for combination in itertools.product(*cycles_var_values):
181 new_sec = sec.copy()
182 new_sec.vals.update(zip(cycles_var_names, combination))
183 yield new_sec
184
185
186def try_format_params_into_section(sec_iter):
187 for sec in sec_iter:
188 params = sec.format_params
189 for name, val in sec.vals.items():
190 if isinstance(val, basestring):
191 try:
192 sec.vals[name] = parse_value(val.format(**params))
193 except:
194 pass
195
196 yield sec
197
198
199def format_params_into_section_finall(sec_iter, counter=[0]):
200 group_report_err_msg = "Group reporting should be set if numjobs != 1"
201
202 for sec in sec_iter:
203
204 num_jobs = int(sec.vals.get('numjobs', '1'))
205 if num_jobs != 1:
206 assert 'group_reporting' in sec.vals, group_report_err_msg
207
208 params = sec.format_params
209
210 fsize = to_bytes(sec.vals['size'])
211 params['PER_TH_OFFSET'] = fsize // num_jobs
212
213 for name, val in sec.vals.items():
214 if isinstance(val, basestring):
215 sec.vals[name] = parse_value(val.format(**params))
216 else:
217 assert isinstance(val, (int, float)) or val is None
218
219 params['UNIQ'] = 'UN{0}'.format(counter[0])
koder aka kdanilov7e0f7cf2015-05-01 17:24:35 +0300220 params['COUNTER'] = str(counter[0])
koder aka kdanilov0c598a12015-04-21 03:01:40 +0300221 counter[0] += 1
222 params['TEST_SUMM'] = get_test_summary(sec.vals)
koder aka kdanilov7e0f7cf2015-05-01 17:24:35 +0300223
koder aka kdanilov0c598a12015-04-21 03:01:40 +0300224 sec.name = sec.name.format(**params)
225
226 yield sec
227
228
229def fio_config_to_str(sec_iter):
230 res = ""
231
232 for pos, sec in enumerate(sec_iter):
233 if pos != 0:
234 res += "\n"
235
236 res += "[{0}]\n".format(sec.name)
237
238 for name, val in sec.vals.items():
239 if name.startswith('_'):
240 continue
241
242 if val is None:
243 res += name + "\n"
244 else:
245 res += "{0}={1}\n".format(name, val)
246
247 return res
248
249
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +0300250def get_test_sync_mode(config):
koder aka kdanilov4e9f3ed2015-04-14 11:26:12 +0300251 try:
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +0300252 return config['sync_mode']
koder aka kdanilov4e9f3ed2015-04-14 11:26:12 +0300253 except KeyError:
254 pass
255
koder aka kdanilovea22c3d2015-04-21 03:42:22 +0300256 is_sync = str(config.get("sync", "0")) == "1"
257 is_direct = str(config.get("direct", "0")) == "1"
koder aka kdanilov4e9f3ed2015-04-14 11:26:12 +0300258
259 if is_sync and is_direct:
260 return 'x'
261 elif is_sync:
262 return 's'
263 elif is_direct:
264 return 'd'
265 else:
266 return 'a'
267
268
koder aka kdanilovda45e882015-04-06 02:24:42 +0300269def get_test_summary(params):
270 rw = {"randread": "rr",
271 "randwrite": "rw",
272 "read": "sr",
273 "write": "sw"}[params["rw"]]
274
koder aka kdanilov4e9f3ed2015-04-14 11:26:12 +0300275 sync_mode = get_test_sync_mode(params)
276 th_count = params.get('numjobs')
koder aka kdanilovea22c3d2015-04-21 03:42:22 +0300277
koder aka kdanilov4e9f3ed2015-04-14 11:26:12 +0300278 if th_count is None:
koder aka kdanilovea22c3d2015-04-21 03:42:22 +0300279 th_count = params.get('concurence', 1)
koder aka kdanilovda45e882015-04-06 02:24:42 +0300280
koder aka kdanilov4e9f3ed2015-04-14 11:26:12 +0300281 return "{0}{1}{2}th{3}".format(rw,
282 sync_mode,
283 params['blocksize'],
284 th_count)
koder aka kdanilovda45e882015-04-06 02:24:42 +0300285
286
koder aka kdanilov0c598a12015-04-21 03:01:40 +0300287def calculate_execution_time(sec_iter):
koder aka kdanilovda45e882015-04-06 02:24:42 +0300288 time = 0
koder aka kdanilov0c598a12015-04-21 03:01:40 +0300289 for sec in sec_iter:
290 time += sec.vals.get('ramp_time', 0)
291 time += sec.vals.get('runtime', 0)
koder aka kdanilovda45e882015-04-06 02:24:42 +0300292 return time
293
294
koder aka kdanilov57ce4db2015-04-25 21:25:51 +0300295def slice_config(sec_iter, runcycle=None, max_jobs=1000,
koder aka kdanilovd5ed4da2015-05-07 23:33:23 +0300296 soft_runcycle=None, split_on_names=False):
koder aka kdanilov0c598a12015-04-21 03:01:40 +0300297 jcount = 0
298 runtime = 0
299 curr_slice = []
koder aka kdanilov57ce4db2015-04-25 21:25:51 +0300300 prev_name = None
koder aka kdanilovda45e882015-04-06 02:24:42 +0300301
koder aka kdanilov0c598a12015-04-21 03:01:40 +0300302 for pos, sec in enumerate(sec_iter):
koder aka kdanilovd5ed4da2015-05-07 23:33:23 +0300303
304 if prev_name is not None:
305 split_here = False
306
307 if soft_runcycle is not None and prev_name != sec.name:
308 split_here = (runtime > soft_runcycle)
309
310 if split_on_names and prev_name != sec.name:
311 split_here = True
312
313 if split_here:
koder aka kdanilov57ce4db2015-04-25 21:25:51 +0300314 yield curr_slice
315 curr_slice = []
316 runtime = 0
317 jcount = 0
318
319 prev_name = sec.name
koder aka kdanilovda45e882015-04-06 02:24:42 +0300320
koder aka kdanilov0c598a12015-04-21 03:01:40 +0300321 jc = sec.vals.get('numjobs', 1)
322 msg = "numjobs should be integer, not {0!r}".format(jc)
323 assert isinstance(jc, int), msg
koder aka kdanilovda45e882015-04-06 02:24:42 +0300324
koder aka kdanilov0c598a12015-04-21 03:01:40 +0300325 curr_task_time = calculate_execution_time([sec])
koder aka kdanilovda45e882015-04-06 02:24:42 +0300326
koder aka kdanilov0c598a12015-04-21 03:01:40 +0300327 if jc > max_jobs:
328 err_templ = "Can't process job {0!r} - too large numjobs"
329 raise ValueError(err_templ.format(sec.name))
koder aka kdanilovda45e882015-04-06 02:24:42 +0300330
koder aka kdanilov0c598a12015-04-21 03:01:40 +0300331 if runcycle is not None and len(curr_slice) != 0:
332 rc_ok = curr_task_time + runtime <= runcycle
koder aka kdanilovda45e882015-04-06 02:24:42 +0300333 else:
koder aka kdanilov0c598a12015-04-21 03:01:40 +0300334 rc_ok = True
koder aka kdanilovda45e882015-04-06 02:24:42 +0300335
koder aka kdanilov0c598a12015-04-21 03:01:40 +0300336 if jc + jcount <= max_jobs and rc_ok:
337 runtime += curr_task_time
338 jcount += jc
339 curr_slice.append(sec)
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300340 continue
341
koder aka kdanilov0c598a12015-04-21 03:01:40 +0300342 assert len(curr_slice) != 0
343 yield curr_slice
koder aka kdanilovda45e882015-04-06 02:24:42 +0300344
koder aka kdanilov0c598a12015-04-21 03:01:40 +0300345 if '_ramp_time' in sec.vals:
346 sec.vals['ramp_time'] = sec.vals.pop('_ramp_time')
347 curr_task_time = calculate_execution_time([sec])
348
349 runtime = curr_task_time
350 jcount = jc
351 curr_slice = [sec]
koder aka kdanilov57ce4db2015-04-25 21:25:51 +0300352 prev_name = None
koder aka kdanilov0c598a12015-04-21 03:01:40 +0300353
354 if curr_slice != []:
355 yield curr_slice
koder aka kdanilovda45e882015-04-06 02:24:42 +0300356
357
koder aka kdanilov0c598a12015-04-21 03:01:40 +0300358def parse_all_in_1(source, test_params):
359 lexer_it = fio_config_lexer(source)
360 sec_it = fio_config_parse(lexer_it, test_params)
361 sec_it = process_cycles(sec_it)
362 sec_it = process_repeats(sec_it)
363 return format_params_into_section_finall(sec_it)
koder aka kdanilovb896f692015-04-07 14:57:55 +0300364
365
koder aka kdanilov0c598a12015-04-21 03:01:40 +0300366def parse_and_slice_all_in_1(source, test_params, **slice_params):
367 sec_it = parse_all_in_1(source, test_params)
368 return slice_config(sec_it, **slice_params)
koder aka kdanilovb896f692015-04-07 14:57:55 +0300369
370
koder aka kdanilov0c598a12015-04-21 03:01:40 +0300371def compile_all_in_1(source, test_params, **slice_params):
372 slices_it = parse_and_slice_all_in_1(source, test_params, **slice_params)
373 for slices in slices_it:
374 yield fio_config_to_str(slices)
koder aka kdanilovb896f692015-04-07 14:57:55 +0300375
376
koder aka kdanilov0c598a12015-04-21 03:01:40 +0300377def do_run_fio(config_slice):
378 benchmark_config = fio_config_to_str(config_slice)
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300379 cmd = ["fio", "--output-format=json", "--alloc-size=262144", "-"]
koder aka kdanilove87ae652015-04-20 02:14:35 +0300380 p = subprocess.Popen(cmd,
381 stdin=subprocess.PIPE,
koder aka kdanilovda45e882015-04-06 02:24:42 +0300382 stdout=subprocess.PIPE,
koder aka kdanilove87ae652015-04-20 02:14:35 +0300383 stderr=subprocess.PIPE)
koder aka kdanilovda45e882015-04-06 02:24:42 +0300384
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300385 start_time = time.time()
koder aka kdanilovda45e882015-04-06 02:24:42 +0300386 # set timeout
koder aka kdanilove87ae652015-04-20 02:14:35 +0300387 raw_out, raw_err = p.communicate(benchmark_config)
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300388 end_time = time.time()
koder aka kdanilove87ae652015-04-20 02:14:35 +0300389
koder aka kdanilov6b1341a2015-04-21 22:44:21 +0300390 # HACK
391 raw_out = "{" + raw_out.split('{', 1)[1]
392
koder aka kdanilove87ae652015-04-20 02:14:35 +0300393 if 0 != p.returncode:
394 msg = "Fio failed with code: {0}\nOutput={1}"
395 raise OSError(msg.format(p.returncode, raw_err))
koder aka kdanilovda45e882015-04-06 02:24:42 +0300396
397 try:
398 parsed_out = json.loads(raw_out)["jobs"]
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +0300399 except KeyError:
400 msg = "Can't parse fio output {0!r}: no 'jobs' found"
401 raw_out = raw_out[:100]
402 raise ValueError(msg.format(raw_out))
403
404 except Exception as exc:
koder aka kdanilovec1b9732015-04-23 20:43:29 +0300405 msg = "Can't parse fio output: {0!r}\nError: {1!s}"
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +0300406 raw_out = raw_out[:100]
koder aka kdanilovec1b9732015-04-23 20:43:29 +0300407 raise ValueError(msg.format(raw_out, exc))
koder aka kdanilovda45e882015-04-06 02:24:42 +0300408
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300409 return zip(parsed_out, config_slice), (start_time, end_time)
koder aka kdanilovda45e882015-04-06 02:24:42 +0300410
411
koder aka kdanilov0c598a12015-04-21 03:01:40 +0300412def add_job_results(section, job_output, res):
koder aka kdanilovda45e882015-04-06 02:24:42 +0300413 if job_output['write']['iops'] != 0:
414 raw_result = job_output['write']
415 else:
416 raw_result = job_output['read']
417
koder aka kdanilov0c598a12015-04-21 03:01:40 +0300418 vals = section.vals
419 if section.name not in res:
koder aka kdanilovda45e882015-04-06 02:24:42 +0300420 j_res = {}
koder aka kdanilov0c598a12015-04-21 03:01:40 +0300421 j_res["rw"] = vals["rw"]
422 j_res["sync_mode"] = get_test_sync_mode(vals)
423 j_res["concurence"] = int(vals.get("numjobs", 1))
424 j_res["blocksize"] = vals["blocksize"]
koder aka kdanilovda45e882015-04-06 02:24:42 +0300425 j_res["jobname"] = job_output["jobname"]
koder aka kdanilov0c598a12015-04-21 03:01:40 +0300426 j_res["timings"] = [int(vals.get("runtime", 0)),
427 int(vals.get("ramp_time", 0))]
koder aka kdanilovda45e882015-04-06 02:24:42 +0300428 else:
koder aka kdanilov0c598a12015-04-21 03:01:40 +0300429 j_res = res[section.name]
430 assert j_res["rw"] == vals["rw"]
431 assert j_res["rw"] == vals["rw"]
432 assert j_res["sync_mode"] == get_test_sync_mode(vals)
433 assert j_res["concurence"] == int(vals.get("numjobs", 1))
434 assert j_res["blocksize"] == vals["blocksize"]
koder aka kdanilovda45e882015-04-06 02:24:42 +0300435 assert j_res["jobname"] == job_output["jobname"]
koder aka kdanilov652cd802015-04-13 12:21:07 +0300436
437 # ramp part is skipped for all tests, except first
koder aka kdanilov0c598a12015-04-21 03:01:40 +0300438 # assert j_res["timings"] == (vals.get("runtime"),
439 # vals.get("ramp_time"))
koder aka kdanilovda45e882015-04-06 02:24:42 +0300440
441 def j_app(name, x):
442 j_res.setdefault(name, []).append(x)
443
koder aka kdanilov4e9f3ed2015-04-14 11:26:12 +0300444 j_app("bw", raw_result["bw"])
koder aka kdanilovda45e882015-04-06 02:24:42 +0300445 j_app("iops", raw_result["iops"])
446 j_app("lat", raw_result["lat"]["mean"])
447 j_app("clat", raw_result["clat"]["mean"])
448 j_app("slat", raw_result["slat"]["mean"])
449
koder aka kdanilov0c598a12015-04-21 03:01:40 +0300450 res[section.name] = j_res
koder aka kdanilovda45e882015-04-06 02:24:42 +0300451
452
koder aka kdanilov0c598a12015-04-21 03:01:40 +0300453def run_fio(sliced_it, raw_results_func=None):
454 sliced_list = list(sliced_it)
koder aka kdanilov0c598a12015-04-21 03:01:40 +0300455
koder aka kdanilov46d4f392015-04-24 11:35:00 +0300456 curr_test_num = 0
457 executed_tests = 0
458 result = {}
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300459 timings = []
koder aka kdanilovb896f692015-04-07 14:57:55 +0300460
koder aka kdanilov46d4f392015-04-24 11:35:00 +0300461 for i, test_slice in enumerate(sliced_list):
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300462 res_cfg_it, slice_timings = do_run_fio(test_slice)
koder aka kdanilov46d4f392015-04-24 11:35:00 +0300463 res_cfg_it = enumerate(res_cfg_it, curr_test_num)
koder aka kdanilovda45e882015-04-06 02:24:42 +0300464
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300465 section_names = []
koder aka kdanilov46d4f392015-04-24 11:35:00 +0300466 for curr_test_num, (job_output, section) in res_cfg_it:
467 executed_tests += 1
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300468 section_names.append(section.name)
koder aka kdanilov0c598a12015-04-21 03:01:40 +0300469
koder aka kdanilov46d4f392015-04-24 11:35:00 +0300470 if raw_results_func is not None:
471 raw_results_func(executed_tests,
472 [job_output, section])
koder aka kdanilovda45e882015-04-06 02:24:42 +0300473
koder aka kdanilov46d4f392015-04-24 11:35:00 +0300474 msg = "{0} != {1}".format(section.name, job_output["jobname"])
475 assert section.name == job_output["jobname"], msg
koder aka kdanilovda45e882015-04-06 02:24:42 +0300476
koder aka kdanilov46d4f392015-04-24 11:35:00 +0300477 if section.name.startswith('_'):
478 continue
koder aka kdanilovda45e882015-04-06 02:24:42 +0300479
koder aka kdanilov46d4f392015-04-24 11:35:00 +0300480 add_job_results(section, job_output, result)
koder aka kdanilov0c598a12015-04-21 03:01:40 +0300481
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300482 timings.append((section_names, slice_timings))
koder aka kdanilov46d4f392015-04-24 11:35:00 +0300483 curr_test_num += 1
484 msg_template = "Done {0} tests from {1}. ETA: {2}"
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300485
koder aka kdanilov46d4f392015-04-24 11:35:00 +0300486 rest = sliced_list[i:]
487 time_eta = sum(map(calculate_execution_time, rest))
488 test_left = sum(map(len, rest))
489 print msg_template.format(curr_test_num,
490 test_left,
491 sec_to_str(time_eta))
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300492
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300493 return result, executed_tests, timings
koder aka kdanilovda45e882015-04-06 02:24:42 +0300494
495
496def run_benchmark(binary_tp, *argv, **kwargs):
497 if 'fio' == binary_tp:
498 return run_fio(*argv, **kwargs)
499 raise ValueError("Unknown behcnmark {0}".format(binary_tp))
500
501
koder aka kdanilov652cd802015-04-13 12:21:07 +0300502def read_config(fd, timeout=10):
503 job_cfg = ""
504 etime = time.time() + timeout
505 while True:
506 wtime = etime - time.time()
507 if wtime <= 0:
508 raise IOError("No config provided")
509
510 r, w, x = select.select([fd], [], [], wtime)
511 if len(r) == 0:
512 raise IOError("No config provided")
513
514 char = fd.read(1)
515 if '' == char:
516 return job_cfg
517
518 job_cfg += char
519
520
koder aka kdanilov652cd802015-04-13 12:21:07 +0300521def sec_to_str(seconds):
522 h = seconds // 3600
523 m = (seconds % 3600) // 60
524 s = seconds % 60
525 return "{0}:{1:02d}:{2:02d}".format(h, m, s)
526
527
koder aka kdanilovda45e882015-04-06 02:24:42 +0300528def parse_args(argv):
529 parser = argparse.ArgumentParser(
530 description="Run fio' and return result")
531 parser.add_argument("--type", metavar="BINARY_TYPE",
532 choices=['fio'], default='fio',
533 help=argparse.SUPPRESS)
534 parser.add_argument("--start-at", metavar="START_AT_UTC", type=int,
535 help="Start execution at START_AT_UTC")
536 parser.add_argument("--json", action="store_true", default=False,
537 help="Json output format")
koder aka kdanilov4d4771c2015-04-23 01:32:02 +0300538 parser.add_argument("-o", "--output", default='-', metavar="FILE_PATH",
koder aka kdanilovda45e882015-04-06 02:24:42 +0300539 help="Store results to FILE_PATH")
540 parser.add_argument("--estimate", action="store_true", default=False,
541 help="Only estimate task execution time")
542 parser.add_argument("--compile", action="store_true", default=False,
543 help="Compile config file to fio config")
544 parser.add_argument("--num-tests", action="store_true", default=False,
545 help="Show total number of tests")
546 parser.add_argument("--runcycle", type=int, default=None,
547 metavar="MAX_CYCLE_SECONDS",
548 help="Max cycle length in seconds")
549 parser.add_argument("--show-raw-results", action='store_true',
550 default=False, help="Output raw input and results")
koder aka kdanilovda45e882015-04-06 02:24:42 +0300551 parser.add_argument("--params", nargs="*", metavar="PARAM=VAL",
552 default=[],
553 help="Provide set of pairs PARAM=VAL to" +
554 "format into job description")
koder aka kdanilov4d4771c2015-04-23 01:32:02 +0300555 parser.add_argument("-p", "--pid-file", metavar="FILE_TO_STORE_PID",
556 default=None, help="Store pid to FILE_TO_STORE_PID " +
557 "and remove this file on exit")
koder aka kdanilovda45e882015-04-06 02:24:42 +0300558 parser.add_argument("jobfile")
559 return parser.parse_args(argv)
560
561
koder aka kdanilovda45e882015-04-06 02:24:42 +0300562def main(argv):
563 argv_obj = parse_args(argv)
564
565 if argv_obj.jobfile == '-':
566 job_cfg = read_config(sys.stdin)
567 else:
568 job_cfg = open(argv_obj.jobfile).read()
569
570 if argv_obj.output == '-':
571 out_fd = sys.stdout
572 else:
573 out_fd = open(argv_obj.output, "w")
574
koder aka kdanilov4d4771c2015-04-23 01:32:02 +0300575 if argv_obj.pid_file is not None:
576 with open(argv_obj.pid_file, "w") as fd:
577 fd.write(str(os.getpid()))
koder aka kdanilovda45e882015-04-06 02:24:42 +0300578
koder aka kdanilov4d4771c2015-04-23 01:32:02 +0300579 try:
580 params = {}
581 for param_val in argv_obj.params:
582 assert '=' in param_val
583 name, val = param_val.split("=", 1)
584 params[name] = val
koder aka kdanilov0c598a12015-04-21 03:01:40 +0300585
koder aka kdanilov4d4771c2015-04-23 01:32:02 +0300586 slice_params = {
587 'runcycle': argv_obj.runcycle,
588 }
koder aka kdanilov0c598a12015-04-21 03:01:40 +0300589
koder aka kdanilov4d4771c2015-04-23 01:32:02 +0300590 sliced_it = parse_and_slice_all_in_1(job_cfg, params, **slice_params)
koder aka kdanilov652cd802015-04-13 12:21:07 +0300591
koder aka kdanilov4d4771c2015-04-23 01:32:02 +0300592 if argv_obj.estimate:
593 it = map(calculate_execution_time, sliced_it)
594 print sec_to_str(sum(it))
595 return 0
koder aka kdanilovda45e882015-04-06 02:24:42 +0300596
koder aka kdanilov4d4771c2015-04-23 01:32:02 +0300597 if argv_obj.num_tests or argv_obj.compile:
598 if argv_obj.compile:
599 for test_slice in sliced_it:
600 out_fd.write(fio_config_to_str(test_slice))
601 out_fd.write("\n#" + "-" * 70 + "\n\n")
koder aka kdanilovda45e882015-04-06 02:24:42 +0300602
koder aka kdanilov4d4771c2015-04-23 01:32:02 +0300603 if argv_obj.num_tests:
604 print len(list(sliced_it))
koder aka kdanilovda45e882015-04-06 02:24:42 +0300605
koder aka kdanilov4d4771c2015-04-23 01:32:02 +0300606 return 0
koder aka kdanilovda45e882015-04-06 02:24:42 +0300607
koder aka kdanilov4d4771c2015-04-23 01:32:02 +0300608 if argv_obj.start_at is not None:
609 ctime = time.time()
610 if argv_obj.start_at >= ctime:
611 time.sleep(ctime - argv_obj.start_at)
koder aka kdanilovda45e882015-04-06 02:24:42 +0300612
koder aka kdanilov4d4771c2015-04-23 01:32:02 +0300613 def raw_res_func(test_num, data):
614 pref = "========= RAW_RESULTS({0}) =========\n".format(test_num)
615 out_fd.write(pref)
616 out_fd.write(json.dumps(data))
617 out_fd.write("\n========= END OF RAW_RESULTS =========\n")
618 out_fd.flush()
koder aka kdanilovda45e882015-04-06 02:24:42 +0300619
koder aka kdanilov4d4771c2015-04-23 01:32:02 +0300620 rrfunc = raw_res_func if argv_obj.show_raw_results else None
koder aka kdanilovda45e882015-04-06 02:24:42 +0300621
koder aka kdanilov4d4771c2015-04-23 01:32:02 +0300622 stime = time.time()
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300623 job_res, num_tests, timings = run_benchmark(argv_obj.type,
624 sliced_it, rrfunc)
koder aka kdanilov4d4771c2015-04-23 01:32:02 +0300625 etime = time.time()
koder aka kdanilovda45e882015-04-06 02:24:42 +0300626
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300627 res = {'__meta__': {'raw_cfg': job_cfg,
628 'params': params,
629 'timings': timings},
koder aka kdanilov4d4771c2015-04-23 01:32:02 +0300630 'res': job_res}
koder aka kdanilovda45e882015-04-06 02:24:42 +0300631
koder aka kdanilov4d4771c2015-04-23 01:32:02 +0300632 oformat = 'json' if argv_obj.json else 'eval'
633 msg = "\nRun {0} tests in {1} seconds\n"
634 out_fd.write(msg.format(num_tests, int(etime - stime)))
635
636 msg = "========= RESULTS(format={0}) =========\n"
637 out_fd.write(msg.format(oformat))
638 if argv_obj.json:
639 out_fd.write(json.dumps(res))
640 else:
641 out_fd.write(pprint.pformat(res) + "\n")
642 out_fd.write("\n========= END OF RESULTS =========\n")
643
koder aka kdanilov46d4f392015-04-24 11:35:00 +0300644 return 0
645 except:
646 out_fd.write("============ ERROR =============\n")
647 out_fd.write(traceback.format_exc() + "\n")
648 out_fd.write("============ END OF ERROR =============\n")
649 return 1
koder aka kdanilov4d4771c2015-04-23 01:32:02 +0300650 finally:
koder aka kdanilov46d4f392015-04-24 11:35:00 +0300651 try:
652 if out_fd is not sys.stdout:
653 out_fd.flush()
654 os.fsync(out_fd)
655 out_fd.close()
656 except Exception:
657 traceback.print_exc()
658
koder aka kdanilov4d4771c2015-04-23 01:32:02 +0300659 if argv_obj.pid_file is not None:
660 if os.path.exists(argv_obj.pid_file):
661 os.unlink(argv_obj.pid_file)
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +0300662
663
664def fake_main(x):
665 import yaml
666 time.sleep(60)
667 out_fd = sys.stdout
668 fname = "/tmp/perf_tests/metempirical_alisha/raw_results.yaml"
669 res = yaml.load(open(fname).read())[0][1]
670 out_fd.write("========= RESULTS(format=json) =========\n")
671 out_fd.write(json.dumps(res))
672 out_fd.write("\n========= END OF RESULTS =========\n")
koder aka kdanilovda45e882015-04-06 02:24:42 +0300673 return 0
674
675
676if __name__ == '__main__':
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +0300677 # exit(fake_main(sys.argv[1:]))
koder aka kdanilovda45e882015-04-06 02:24:42 +0300678 exit(main(sys.argv[1:]))